Skip to content

Commit 27bcbe8

Browse files
committed
Added workaround for gfortran bug with character cells.
Improved efficiency of split routine. Fixed a bug where skip_rows didn’t work right.
1 parent 9abfedb commit 27bcbe8

File tree

4 files changed

+111
-46
lines changed

4 files changed

+111
-46
lines changed

build.sh

+5-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# Requires: FoBiS and Ford
77
#
88

9-
MODCODE='csv_module.f90' # module file name
9+
MODCODE='csv_module.F90' # module file name
1010
LIBOUT='libcsv.a' # name of library
1111
DOCDIR='./doc/' # build directory for documentation
1212
SRCDIR='./src/' # library source directory
@@ -17,10 +17,10 @@ FORDMD='fortran-csv-module.md' # FORD config file name
1717

1818
#compiler flags:
1919

20-
#FCOMPILER='gnu' #Set compiler to gfortran
21-
#FCOMPILERFLAGS='-c -O2 -std=f2008'
22-
FCOMPILER='intel' #Set compiler to intel
23-
FCOMPILERFLAGS='-c -O2 -warn -stand f08 -traceback'
20+
FCOMPILER='gnu' #Set compiler to gfortran
21+
FCOMPILERFLAGS='-c -O2 -std=f2008'
22+
#FCOMPILER='intel' #Set compiler to intel
23+
#FCOMPILERFLAGS='-c -O0 -warn -stand f08 -traceback -g'
2424

2525
#build using FoBiS:
2626

files/test_2_columns.csv

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
id,first_name
2+
1,Julie
3+
2,Jose
4+
3,Lois
5+
4,Walter

src/csv_module.f90 src/csv_module.F90

+55-18
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ subroutine read_csv_file(me,filename,header_row,skip_rows,status_ok)
229229
!! class have been allocated
230230
integer :: iheader !! row number of header row
231231
!! (0 if no header specified)
232+
character(len=1) :: tmp !! for skipping a row
232233

233234
call me%destroy()
234235
arrays_allocated = .false.
@@ -267,7 +268,17 @@ subroutine read_csv_file(me,filename,header_row,skip_rows,status_ok)
267268

268269
! skip row if necessary
269270
if (allocated(rows_to_skip)) then
270-
if (any(i==rows_to_skip)) cycle
271+
if (any(i==rows_to_skip)) then
272+
read(iunit,fmt='(A1)',iostat=istat) tmp
273+
if (istat/=0) then
274+
if (me%verbose) write(error_unit,'(A)') &
275+
'Error skipping row in file: '//trim(filename)
276+
close(unit=iunit,iostat=istat)
277+
status_ok = .false.
278+
return
279+
end if
280+
cycle
281+
end if
271282
end if
272283

273284
call me%read_line_from_file(iunit,line,status_ok)
@@ -884,10 +895,11 @@ subroutine get_column(me,icol,r,status_ok)
884895
integer,intent(in) :: icol !! column number
885896
class(*),dimension(:),intent(out) :: r !! assumed to have been allocated to
886897
!! the correct size by the caller.
887-
!! (n_rows)
898+
!! (`n_rows`)
888899
logical,intent(out) :: status_ok !! status flag
889900

890901
integer :: i !! counter
902+
character(len=:),allocatable :: tmp !! for gfortran workaround
891903

892904
! we know the data is allocated, since that
893905
! was checked by the calling routines.
@@ -896,8 +908,19 @@ subroutine get_column(me,icol,r,status_ok)
896908

897909
do i=1,me%n_rows ! row loop
898910

911+
#if defined __GFORTRAN__
912+
! the following is a workaround for gfortran bugs:
913+
select type (r)
914+
type is (character(len=*))
915+
tmp = repeat(' ',len(r)) ! size the string
916+
call me%csv_get_value(i,icol,tmp,status_ok)
917+
r(i) = tmp
918+
class default
919+
call me%csv_get_value(i,icol,r(i),status_ok)
920+
end select
921+
#else
899922
call me%csv_get_value(i,icol,r(i),status_ok)
900-
923+
#endif
901924
if (.not. status_ok) then
902925
select type (r)
903926
! note: character conversion can never fail, so not
@@ -1174,11 +1197,10 @@ end subroutine read_line_from_file
11741197
!
11751198
!### Example
11761199
!````Fortran
1177-
! type(csv_file) :: f
11781200
! character(len=:),allocatable :: s
11791201
! type(csv_string),dimension(:),allocatable :: vals
11801202
! s = '1,2,3,4,5'
1181-
! vals = f%split(s,',')
1203+
! call split(s,',',vals)
11821204
!````
11831205
!
11841206
!@warning Doesn't seem to work for `len(token)>1`
@@ -1201,23 +1223,38 @@ pure subroutine split(str,token,chunk_size,vals)
12011223
integer :: i1 !! index
12021224
integer :: i2 !! index
12031225
integer :: j !! counters
1204-
character(len=:),allocatable :: temp
1205-
integer,dimension(:),allocatable :: itokens
1226+
integer,dimension(:),allocatable :: itokens !! start indices of the
1227+
!! token locations in `str`
12061228

1207-
temp = str ! make a copy of the string
12081229
len_token = len(token) ! length of the token
1209-
n_tokens = 0 ! initialize the number of token counter
1210-
j = 0 ! length of string removed
1230+
n_tokens = 0 ! initialize the token counter
1231+
j = 0 ! index to start looking for the next token
1232+
1233+
! first, count the number of times the token
1234+
! appears in the string, and get the token indices.
1235+
!
1236+
! Examples:
1237+
! ', ' --> 1
1238+
! '1234,67,90' --> 5,8
1239+
! '123, ' --> 4
1240+
1241+
! length of the string
1242+
if (token == ' ') then
1243+
! in this case, we can't ignore trailing space
1244+
len_str = len(str)
1245+
else
1246+
! safe to ignore trailing space when looking for tokens
1247+
len_str = len_trim(str)
1248+
end if
12111249

1212-
!first, count the number of times the token appears in the string
1250+
j = 1
1251+
n_tokens = 0
12131252
do
1214-
len_str = len(temp) ! length of the string
1215-
i = index(temp,token) ! location of the next token
1216-
if (i<=0) exit ! no more tokens found
1217-
call expand_vector(itokens,n_tokens,chunk_size,i+j) ! save the token location
1218-
if (i+len_token>len_str) exit ! if the last bit of the string is a token
1219-
j = j + i
1220-
temp = trim(temp(i+len_token:len_str)) !remove previously scanned part of string
1253+
if (j>len_str) exit ! end of string, finished
1254+
i = index(str(j:),token) ! index of next token in remaining string
1255+
if (i<=0) exit ! no more tokens found
1256+
call expand_vector(itokens,n_tokens,chunk_size,i+j-1) ! save the token location
1257+
j = j + i + (len_token - 1)
12211258
end do
12221259
call expand_vector(itokens,n_tokens,chunk_size,finished=.true.) ! resize the vector
12231260

src/tests/csv_test.f90

+46-23
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,58 @@ program csv_test
1919
real(wp),dimension(:),allocatable :: x !! for getting a real vector from a csv file
2020
logical :: status_ok !! error flag
2121
integer,dimension(:),allocatable :: itypes !! array of variable types in the file
22+
integer :: ifile !! file counter
23+
character(len=30),dimension(:),allocatable :: names
2224

23-
! read the file:
24-
call f%read('../files/test.csv',header_row=1,status_ok=status_ok)
25+
character(len=*),dimension(2),parameter :: files_to_test = ['../files/test.csv ',&
26+
'../files/test_2_columns.csv']
2527

26-
! print the header and type info:
27-
call f%get_header(header,status_ok)
28-
call f%variable_types(itypes,status_ok)
29-
write(*,*) ''
30-
write(*,'(*(A30,1X,A4))') 'Header', 'Type'
31-
do i=1,size(header)
32-
write(*,'(*(A30,1X,I4))') header(i), itypes(i)
33-
end do
28+
do ifile = 1, size(files_to_test)
3429

35-
write(*,*) ''
36-
write(*,*) 'print all the rows:'
30+
! read the file:
31+
if (ifile==1) then
32+
call f%read(trim(files_to_test(ifile)),header_row=1,status_ok=status_ok)
33+
else
34+
! also skip a row
35+
call f%read(trim(files_to_test(ifile)),header_row=1,skip_rows=[2],status_ok=status_ok)
36+
end if
3737

38-
call f%get(csv_data,status_ok)
39-
do i=1,size(csv_data,1)
40-
write(*,'(*(A30,1X))') csv_data(i,:)
41-
end do
38+
write(*,*) ''
39+
write(*,*) 'File: '//trim(files_to_test(ifile))
40+
! print the header and type info:
41+
call f%get_header(header,status_ok)
42+
call f%variable_types(itypes,status_ok)
43+
write(*,*) ''
44+
write(*,'(*(A30,1X,A4))') 'Header', 'Type'
45+
do i=1,size(header)
46+
write(*,'(*(A30,1X,I4))') header(i), itypes(i)
47+
end do
48+
49+
write(*,*) ''
50+
write(*,*) 'print all the rows:'
4251

43-
write(*,*) ''
44-
write(*,*) 'get some vectors:'
52+
call f%get(csv_data,status_ok)
53+
do i=1,size(csv_data,1)
54+
write(*,'(*(A30,1X))') csv_data(i,:)
55+
end do
4556

46-
write(*,*) ''
47-
write(*,*) 'age:'
48-
call f%get(3,x,status_ok)
49-
write(*,'(F6.3,1x)',advance='NO') x
50-
write(*,*) ''
57+
write(*,*) ''
58+
write(*,*) 'get some vectors:'
59+
if (ifile==1) then
60+
write(*,*) ''
61+
write(*,*) 'age:'
62+
call f%get(3,x,status_ok)
63+
write(*,'(F6.3,1x)',advance='NO') x
64+
write(*,*) ''
65+
else
66+
write(*,*) ''
67+
write(*,*) 'name:'
68+
call f%get(2,names,status_ok)
69+
write(*,'(A10,1x)',advance='NO') names
70+
write(*,*) ''
71+
end if
72+
73+
end do
5174

5275
! now test creating a CSV:
5376
call f2%initialize(enclose_strings_in_quotes=.false.,verbose=.true.)

0 commit comments

Comments
 (0)