@@ -47,6 +47,14 @@ module csv_module
47
47
type (csv_string),dimension (:),allocatable :: header ! ! the header
48
48
type (csv_string),dimension (:,:),allocatable :: csv_data ! ! the data in the file
49
49
50
+ ! for writing a csv file:
51
+ integer :: icol = 0 ! ! last column written in current row
52
+ integer :: iunit = 0 ! ! file unit for writing
53
+ logical :: enclose_strings_in_quotes = .true. ! ! if true, all string cells
54
+ ! ! will be enclosed in quotes.
55
+ logical :: enclose_all_in_quotes = .false. ! ! if true, *all* cells will
56
+ ! ! be enclosed in quotes.
57
+
50
58
contains
51
59
52
60
private
@@ -80,6 +88,12 @@ module csv_module
80
88
procedure :: get_character_column
81
89
procedure :: get_csv_string_column
82
90
91
+ procedure ,public :: open = > open_csv_file
92
+ generic,public :: add = > add_cell
93
+ procedure :: add_cell
94
+ procedure ,public :: next_row
95
+ procedure ,public :: close = > close_csv_file
96
+
83
97
procedure :: tokenize = > tokenize_csv_line
84
98
procedure :: read_line_from_file
85
99
procedure :: get_column
@@ -231,6 +245,213 @@ subroutine read_csv_file(me,filename,header_row,skip_rows,status_ok)
231
245
end subroutine read_csv_file
232
246
! *****************************************************************************************
233
247
248
+ ! *****************************************************************************************
249
+ ! >
250
+ ! Open a CSV file for writing
251
+
252
+ subroutine open_csv_file (me ,filename ,&
253
+ n_cols ,&
254
+ enclose_strings_in_quotes ,&
255
+ enclose_all_in_quotes ,&
256
+ int_fmt ,&
257
+ real_fmt ,&
258
+ status_ok )
259
+
260
+ implicit none
261
+
262
+ class(csv_file),intent (inout ) :: me
263
+ character (len=* ),intent (in ) :: filename ! ! the CSV file to open
264
+ integer ,intent (in ) :: n_cols ! ! number of columns in the file
265
+ logical ,intent (in ),optional :: enclose_strings_in_quotes ! ! default is true
266
+ logical ,intent (in ),optional :: enclose_all_in_quotes ! ! default is false
267
+ character (len=* ),intent (in ),optional :: int_fmt ! ! format string for writing integers
268
+ ! ! (default is I10)
269
+ character (len=* ),intent (in ),optional :: real_fmt ! ! format string for writing reals
270
+ ! ! (default is E27.17)
271
+ logical ,intent (out ) :: status_ok ! ! status flag
272
+
273
+ integer :: istat ! ! open `iostat` flag
274
+
275
+ call me% destroy()
276
+
277
+ me% n_cols = n_cols
278
+ me% n_rows = 0
279
+ me% icol = 0
280
+
281
+ if (present (enclose_strings_in_quotes)) then
282
+ me% enclose_strings_in_quotes = enclose_strings_in_quotes
283
+ else
284
+ me% enclose_strings_in_quotes = .true.
285
+ end if
286
+ if (present (enclose_all_in_quotes)) then
287
+ me% enclose_all_in_quotes = enclose_all_in_quotes
288
+ else
289
+ me% enclose_all_in_quotes = .false.
290
+ end if
291
+ if (me% enclose_all_in_quotes) me% enclose_strings_in_quotes = .true. ! override
292
+
293
+ open (newunit= me% iunit,file= filename,status= ' REPLACE' ,iostat= istat)
294
+ if (istat== 0 ) then
295
+ status_ok = .true.
296
+ else
297
+ write (error_unit,' (A)' ) ' Error opening file: ' // trim (filename)
298
+ status_ok = .false.
299
+ end if
300
+
301
+ end subroutine open_csv_file
302
+ ! *****************************************************************************************
303
+
304
+ ! *****************************************************************************************
305
+ ! >
306
+ ! Close a CSV file after writing
307
+
308
+ subroutine close_csv_file (me ,status_ok )
309
+
310
+ implicit none
311
+
312
+ class(csv_file),intent (inout ) :: me
313
+ logical ,intent (out ) :: status_ok ! ! status flag
314
+
315
+ integer :: istat ! ! close `iostat` flag
316
+
317
+ close (me% iunit,iostat= istat)
318
+ status_ok = istat== 0
319
+
320
+ end subroutine close_csv_file
321
+ ! *****************************************************************************************
322
+
323
+ ! *****************************************************************************************
324
+ ! >
325
+ ! Add a cell to a CSV file.
326
+
327
+ subroutine add_cell (me ,val ,int_fmt ,real_fmt ,trim_str )
328
+
329
+ implicit none
330
+
331
+ class(csv_file),intent (inout ) :: me
332
+ class(* ),intent (in ) :: val ! ! the value to add
333
+ character (len=* ),intent (in ),optional :: int_fmt ! ! format string for integers
334
+ character (len=* ),intent (in ),optional :: real_fmt ! ! format string for reals
335
+ logical ,intent (in ),optional :: trim_str ! ! to trim the string
336
+
337
+ integer :: istat ! ! write `iostat` flag
338
+ character (len= :),allocatable :: ifmt ! ! actual format string to use for integers
339
+ character (len= :),allocatable :: rfmt ! ! actual format string to use for REALS
340
+ logical :: trimstr ! ! if the strings are to be trimmed
341
+
342
+ ! make sure the row isn't already finished
343
+ if (me% icol< me% n_cols) then
344
+
345
+ me% icol = me% icol + 1
346
+
347
+ if (me% enclose_all_in_quotes) then
348
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) me% quote
349
+ end if
350
+
351
+ select type (val)
352
+ type is (integer (ip))
353
+ if (present (int_fmt)) then
354
+ ifmt = trim (adjustl (int_fmt))
355
+ else
356
+ ifmt = ' (I10)'
357
+ end if
358
+ write (me% iunit,fmt= ifmt,advance= ' NO' ,iostat= istat) val ! TODO trim(adjustl()) this string
359
+ type is (real (wp))
360
+ if (present (real_fmt)) then
361
+ rfmt = trim (adjustl (real_fmt))
362
+ else
363
+ rfmt = ' (E27.17)'
364
+ end if
365
+ write (me% iunit,fmt= rfmt,advance= ' NO' ,iostat= istat) val ! TODO trim(adjustl()) this string
366
+ type is (logical )
367
+ if (val) then
368
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) ' T' ! TODO make these user-defined strings
369
+ else
370
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) ' F'
371
+ end if
372
+ type is (character (len=* ))
373
+ if (me% enclose_strings_in_quotes .and. .not. me% enclose_all_in_quotes) &
374
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) me% quote
375
+ if (present (trim_str)) then
376
+ trimstr = trim_str
377
+ else
378
+ trimstr = .false.
379
+ end if
380
+ if (trimstr) then
381
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) trim (val)
382
+ else
383
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) val
384
+ end if
385
+ if (me% enclose_strings_in_quotes .and. .not. me% enclose_all_in_quotes) &
386
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) me% quote
387
+ type is (csv_string)
388
+ if (me% enclose_strings_in_quotes .and. .not. me% enclose_all_in_quotes) &
389
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) me% quote
390
+ if (present (trim_str)) then
391
+ trimstr = trim_str
392
+ else
393
+ trimstr = .false.
394
+ end if
395
+ if (trimstr) then
396
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) trim (val% str)
397
+ else
398
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) val% str
399
+ end if
400
+ if (me% enclose_strings_in_quotes .and. .not. me% enclose_all_in_quotes) &
401
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) me% quote
402
+ class default
403
+ write (error_unit,' (A)' ) ' Error: cannot write unknown variable type to CSV file.'
404
+ end select
405
+
406
+ if (me% enclose_all_in_quotes) then
407
+ write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) me% quote
408
+ end if
409
+ if (me% icol< me% n_cols) write (me% iunit,fmt= ' (A)' ,advance= ' NO' ,iostat= istat) me% delimiter
410
+
411
+ else
412
+ write (error_unit,' (A)' ) ' Error: cannot write more cells to the current row.'
413
+ end if
414
+
415
+ end subroutine add_cell
416
+ ! *****************************************************************************************
417
+
418
+ ! *****************************************************************************************
419
+ ! >
420
+ ! Advance to the next row in the CSV file
421
+ ! (write any blank cells that are necessary to finish the row)
422
+
423
+ subroutine next_row (me )
424
+
425
+ implicit none
426
+
427
+ class(csv_file),intent (inout ) :: me
428
+
429
+ integer :: i ! ! counter
430
+ integer :: n ! ! number of blank cells to write
431
+
432
+ if (me% icol> 0 ) then
433
+ n = me% n_cols - me% icol
434
+ do i= 1 ,n
435
+ if (i== n) then ! no trailing delimiter
436
+ if (me% enclose_strings_in_quotes) then
437
+ write (me% iunit,' (A)' ,advance= ' NO' ) me% quote// me% quote
438
+ end if
439
+ else
440
+ if (me% enclose_strings_in_quotes) then
441
+ write (me% iunit,' (A)' ,advance= ' NO' ) me% quote// me% quote// me% delimiter
442
+ else
443
+ write (me% iunit,' (A)' ,advance= ' NO' ) me% delimiter
444
+ end if
445
+ end if
446
+ end do
447
+ write (me% iunit,' (A)' ) ' ' ! new line
448
+ end if
449
+
450
+ me% icol = 0 ! this row is finished
451
+
452
+ end subroutine next_row
453
+ ! *****************************************************************************************
454
+
234
455
! *****************************************************************************************
235
456
! >
236
457
! Returns the header as a `type(csv_string)` array.
@@ -869,7 +1090,7 @@ pure subroutine split(str,token,chunk_size,vals)
869
1090
type (csv_string),dimension (:),allocatable ,intent (out ) :: vals
870
1091
871
1092
integer :: i
872
- integer :: len_str
1093
+ integer :: len_str
873
1094
integer :: len_token ! ! length of the token
874
1095
integer :: n_tokens ! ! number of tokens
875
1096
integer :: i1 ! ! index
0 commit comments