Skip to content

Commit c892a74

Browse files
committed
io: enhance delimiter handling in loadtxt and savetxt functions
1 parent 8fb821d commit c892a74

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

src/stdlib_io.fypp

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,15 @@ module stdlib_io
5959
!> Format string for quadruple precision real numbers
6060
FMT_REAL_QP = '(es44.35e4)', &
6161
!> Format string for single precision complex numbers
62-
FMT_COMPLEX_SP = '(es15.8e2,1x,es15.8e2)', &
62+
FMT_COMPLEX_SP = '(es15.08e2,1x,es15.08e2)', &
6363
!> Format string for double precision complex numbers
6464
FMT_COMPLEX_DP = '(es24.16e3,1x,es24.16e3)', &
6565
!> Format string for extended double precision complex numbers
6666
FMT_COMPLEX_XDP = '(es26.18e3,1x,es26.18e3)', &
6767
!> Format string for quadruple precision complex numbers
6868
FMT_COMPLEX_QP = '(es44.35e4,1x,es44.35e4)'
69+
!> Default delimiter for loadtxt, savetxt and number_of_columns
70+
character(len=1), parameter :: delimiter_default = " "
6971

7072
public :: FMT_INT, FMT_REAL_SP, FMT_REAL_DP, FMT_REAL_XDP, FMT_REAL_QP
7173
public :: FMT_COMPLEX_SP, FMT_COMPLEX_DP, FMT_COMPLEX_XDP, FMT_COMPLEX_QP
@@ -143,9 +145,9 @@ contains
143145
!! 11 12 13
144146
!! ...
145147
!!
146-
character(len=1), parameter :: delimiter_default = " "
147148
integer :: s
148-
integer :: nrow, ncol, i, ios, skiprows_, max_rows_
149+
integer :: nrow, ncol, i, j, ios, skiprows_, max_rows_, istart, iend
150+
character(len=:), allocatable :: line, iomsg_
149151
character(len=1024) :: iomsg, msgout
150152

151153
skiprows_ = max(optval(skiprows, 0), 0)
@@ -163,10 +165,11 @@ contains
163165
ncol = 0
164166
if ( skiprows_ < nrow ) ncol = number_of_columns(s, skiprows=skiprows_, delimiter=delimiter_)
165167
#:if 'complex' in t1
166-
if (is_blank(delimiter_)) ncol = ncol / 2
168+
ncol = ncol / 2
167169
#:endif
168170

169171
allocate(d(max_rows_, ncol))
172+
if (max_rows_ == 0 .or. ncol == 0) return
170173

171174
do i = 1, skiprows_
172175
read(s, *, iostat=ios, iomsg=iomsg)
@@ -190,15 +193,44 @@ contains
190193

191194
if ( fmt_ == '*' ) then
192195
! Use list directed read if user has specified fmt='*'
193-
do i = 1, max_rows_
194-
read (s,*,iostat=ios,iomsg=iomsg) d(i, :)
195-
196-
if (ios/=0) then
197-
write(msgout,2) trim(iomsg),size(d,2),i,trim(filename)
198-
call error_stop(msg=trim(msgout))
199-
end if
200-
201-
enddo
196+
if (is_blank(delimiter_) .or. delimiter_ == ",") then
197+
do i = 1, max_rows_
198+
read (s,*,iostat=ios,iomsg=iomsg) d(i, :)
199+
200+
if (ios/=0) then
201+
write(msgout,2) trim(iomsg),size(d,2),i,trim(filename)
202+
call error_stop(msg=trim(msgout))
203+
end if
204+
205+
enddo
206+
! Otherwise read each value separately
207+
else
208+
do i = 1, max_rows_
209+
call get_line(s, line, ios, iomsg_)
210+
if (ios/=0) then
211+
write(msgout,2) trim(iomsg_),size(d,2),i,trim(filename)
212+
call error_stop(msg=trim(msgout))
213+
end if
214+
215+
istart = 0
216+
do j = 1, ncol - 1
217+
iend = index(line(istart+1:), delimiter_)
218+
read (line(istart+1:istart+iend-1),*,iostat=ios,iomsg=iomsg) d(i, j)
219+
if (ios/=0) then
220+
write(msgout,2) trim(iomsg),size(d,2),i,trim(filename)
221+
call error_stop(msg=trim(msgout))
222+
end if
223+
istart = istart + iend
224+
end do
225+
226+
read (line(istart+1:),*,iostat=ios,iomsg=iomsg) d(i, ncol)
227+
if (ios/=0) then
228+
write(msgout,2) trim(iomsg),size(d,2),i,trim(filename)
229+
call error_stop(msg=trim(msgout))
230+
end if
231+
232+
enddo
233+
end if
202234
else
203235
! Otherwise pass default or user specified fmt string.
204236
do i = 1, max_rows_
@@ -241,7 +273,6 @@ contains
241273
!! call savetxt("log.txt", data)
242274
!!```
243275
!!
244-
character(len=1), parameter :: delimiter_default = " "
245276
integer :: s, i, ios
246277
character(len=1) :: delimiter_
247278
character(len=3) :: delim_str
@@ -253,7 +284,7 @@ contains
253284
#:if 'real' in t1
254285
fmt_ = "(*"//FMT_REAL_${k1}$(1:len(FMT_REAL_${k1}$)-1)//",:,"//delim_str//"))"
255286
#:elif 'complex' in t1
256-
fmt_ = "(*"//FMT_COMPLEX_${k1}$(1:len(FMT_COMPLEX_${k1}$)-1)//",:,"//delim_str//"))"
287+
fmt_ = "(*"//FMT_COMPLEX_${k1}$(1:11)//delim_str//FMT_COMPLEX_${k1}$(14:23)//",:,"//delim_str//"))"
257288
#:elif 'integer' in t1
258289
fmt_ = "(*"//FMT_INT(1:len(FMT_INT)-1)//",:,"//delim_str//"))"
259290
#:endif
@@ -289,7 +320,6 @@ contains
289320
integer, intent(in), optional :: skiprows
290321
character(len=1), intent(in), optional :: delimiter
291322

292-
character(len=1), parameter :: delimiter_default = " "
293323
integer :: ios, skiprows_, i
294324
character :: c
295325
character(len=:), allocatable :: line

test/io/test_loadtxt.f90

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ subroutine test_loadtxt_int32(error)
4949
if (allocated(error)) return
5050
call savetxt('test_int32.txt', input, delimiter=',')
5151
call loadtxt('test_int32.txt', expected, delimiter=',')
52-
call check(error, all(input == expected),'User specified delimiter read failed')
52+
call check(error, all(input == expected),'User specified delimiter `,` read failed')
53+
if (allocated(error)) return
54+
call savetxt('test_int32.txt', input, delimiter='-')
55+
call loadtxt('test_int32.txt', expected, delimiter='-')
56+
call check(error, all(input == expected),'User specified delimiter `-` read failed')
5357
if (allocated(error)) return
5458
end do
5559

@@ -80,7 +84,11 @@ subroutine test_loadtxt_sp(error)
8084
if (allocated(error)) return
8185
call savetxt('test_sp.txt', input, delimiter=',')
8286
call loadtxt('test_sp.txt', expected, delimiter=',')
83-
call check(error, all(input == expected),'User specified delimiter read failed')
87+
call check(error, all(input == expected),'User specified delimiter `,` read failed')
88+
if (allocated(error)) return
89+
call savetxt('test_sp.txt', input, delimiter=';')
90+
call loadtxt('test_sp.txt', expected, delimiter=';')
91+
call check(error, all(input == expected),'User specified delimiter `;` read failed')
8492
if (allocated(error)) return
8593
end do
8694

@@ -288,6 +296,10 @@ subroutine test_loadtxt_complex(error)
288296
call loadtxt('test_complex.txt', expected, delimiter=',')
289297
call check(error, all(input == expected))
290298
if (allocated(error)) return
299+
call savetxt('test_complex.txt', input, delimiter=';')
300+
call loadtxt('test_complex.txt', expected, delimiter=';')
301+
call check(error, all(input == expected))
302+
if (allocated(error)) return
291303
end do
292304

293305
end subroutine test_loadtxt_complex

0 commit comments

Comments
 (0)