1
1
module stdlib_experimental_io
2
- use iso_fortran_env, only: sp= >real32, dp= >real64, qp= >real128
2
+ use stdlib_experimental_kinds, only: sp, dp, qp
3
+ use stdlib_experimental_error, only: error_stop
4
+ use stdlib_experimental_optval, only: optval
5
+ use stdlib_experimental_ascii, only: is_blank
3
6
implicit none
4
7
private
5
- public :: loadtxt, savetxt
8
+ ! Public API
9
+ public :: loadtxt, savetxt, open
10
+
11
+ ! Private API that is exposed so that we can test it in tests
12
+ public :: parse_mode
13
+
6
14
7
15
interface loadtxt
8
16
module procedure sloadtxt
@@ -46,7 +54,7 @@ subroutine sloadtxt(filename, d)
46
54
integer :: s
47
55
integer :: nrow,ncol,i
48
56
49
- open (newunit = s, file = filename, status = " old " , action = " read " )
57
+ s = open (filename )
50
58
51
59
! determine number of columns
52
60
ncol = number_of_columns(s)
@@ -89,7 +97,7 @@ subroutine dloadtxt(filename, d)
89
97
integer :: s
90
98
integer :: nrow,ncol,i
91
99
92
- open (newunit = s, file = filename, status = " old " , action = " read " )
100
+ s = open (filename )
93
101
94
102
! determine number of columns
95
103
ncol = number_of_columns(s)
@@ -132,7 +140,7 @@ subroutine qloadtxt(filename, d)
132
140
integer :: s
133
141
integer :: nrow,ncol,i
134
142
135
- open (newunit = s, file = filename, status = " old " , action = " read " )
143
+ s = open (filename )
136
144
137
145
! determine number of columns
138
146
ncol = number_of_columns(s)
@@ -164,7 +172,7 @@ subroutine ssavetxt(filename, d)
164
172
! call savetxt("log.txt", data)
165
173
166
174
integer :: s, i
167
- open (newunit = s, file = filename, status = " replace " , action = " write " )
175
+ s = open ( filename, " w " )
168
176
do i = 1 , size (d, 1 )
169
177
write (s, * ) d(i, :)
170
178
end do
@@ -187,7 +195,7 @@ subroutine dsavetxt(filename, d)
187
195
! call savetxt("log.txt", data)
188
196
189
197
integer :: s, i
190
- open (newunit = s, file = filename, status = " replace " , action = " write " )
198
+ s = open ( filename, " w " )
191
199
do i = 1 , size (d, 1 )
192
200
write (s, * ) d(i, :)
193
201
end do
@@ -210,9 +218,12 @@ subroutine qsavetxt(filename, d)
210
218
! call savetxt("log.txt", data)
211
219
212
220
integer :: s, i
213
- open (newunit= s, file= filename, status= " replace" , action= " write" )
221
+ character (len= 14 ) :: format_string
222
+
223
+ write (format_string, ' (a1,i06,a7)' ) ' (' , size (d, 2 ), ' f40.34)'
224
+ s = open (filename, " w" )
214
225
do i = 1 , size (d, 1 )
215
- write (s, * ) d(i, :)
226
+ write (s, format_string ) d(i, :)
216
227
end do
217
228
close (s)
218
229
end subroutine
@@ -224,16 +235,16 @@ integer function number_of_columns(s)
224
235
225
236
integer :: ios
226
237
character :: c
227
- logical :: lastwhite
238
+ logical :: lastblank
228
239
229
240
rewind(s)
230
241
number_of_columns = 0
231
- lastwhite = .true.
242
+ lastblank = .true.
232
243
do
233
244
read (s, ' (a)' , advance= ' no' , iostat= ios) c
234
245
if (ios /= 0 ) exit
235
- if (lastwhite .and. .not. whitechar (c)) number_of_columns = number_of_columns + 1
236
- lastwhite = whitechar (c)
246
+ if (lastblank .and. .not. is_blank (c)) number_of_columns = number_of_columns + 1
247
+ lastblank = is_blank (c)
237
248
end do
238
249
rewind(s)
239
250
@@ -258,14 +269,128 @@ integer function number_of_rows_numeric(s)
258
269
259
270
end function
260
271
261
- logical function whitechar (char ) ! white character
262
- ! returns .true. if char is space (32) or tab (9), .false. otherwise
263
- character , intent (in ) :: char
264
- if (iachar (char) == 32 .or. iachar (char) == 9 ) then
265
- whitechar = .true.
272
+ integer function open (filename , mode , iostat ) result(u)
273
+ ! Open a file
274
+ !
275
+ ! To open a file to read:
276
+ !
277
+ ! u = open("somefile.txt") # The default `mode` is "rt"
278
+ ! u = open("somefile.txt", "r")
279
+ !
280
+ ! To open a file to write:
281
+ !
282
+ ! u = open("somefile.txt", "w")
283
+
284
+ ! To append to the end of the file if it exists:
285
+ !
286
+ ! u = open("somefile.txt", "a")
287
+
288
+ character (* ), intent (in ) :: filename
289
+ character (* ), intent (in ), optional :: mode
290
+ integer , intent (out ), optional :: iostat
291
+
292
+ integer :: io_
293
+ character (3 ) :: mode_
294
+ character (:),allocatable :: action_, position_, status_, access_, form_
295
+
296
+
297
+ mode_ = parse_mode(optval(mode, " " ))
298
+
299
+ select case (mode_(1 :2 ))
300
+ case (' r' )
301
+ action_= ' read'
302
+ position_= ' asis'
303
+ status_= ' old'
304
+ case (' w' )
305
+ action_= ' write'
306
+ position_= ' asis'
307
+ status_= ' replace'
308
+ case (' a' )
309
+ action_= ' write'
310
+ position_= ' append'
311
+ status_= ' old'
312
+ case (' x' )
313
+ action_= ' write'
314
+ position_= ' asis'
315
+ status_= ' new'
316
+ case (' r+' )
317
+ action_= ' readwrite'
318
+ position_= ' asis'
319
+ status_= ' old'
320
+ case (' w+' )
321
+ action_= ' readwrite'
322
+ position_= ' asis'
323
+ status_= ' replace'
324
+ case (' a+' )
325
+ action_= ' readwrite'
326
+ position_= ' append'
327
+ status_= ' old'
328
+ case (' x+' )
329
+ action_= ' readwrite'
330
+ position_= ' asis'
331
+ status_= ' new'
332
+ case default
333
+ call error_stop(" Unsupported mode: " // mode_(1 :2 ))
334
+ end select
335
+
336
+ select case (mode_(3 :3 ))
337
+ case (' t' )
338
+ form_= ' formatted'
339
+ case (' b' )
340
+ form_= ' unformatted'
341
+ case default
342
+ call error_stop(" Unsupported mode: " // mode_(3 :3 ))
343
+ end select
344
+
345
+ access_ = ' stream'
346
+
347
+ if (present (iostat)) then
348
+ open (newunit= u, file= filename, &
349
+ action = action_, position = position_, status = status_, &
350
+ access = access_, form = form_, &
351
+ iostat = iostat)
266
352
else
267
- whitechar = .false.
353
+ open (newunit= u, file= filename, &
354
+ action = action_, position = position_, status = status_, &
355
+ access = access_, form = form_)
268
356
end if
357
+
358
+ end function
359
+
360
+ character (3 ) function parse_mode(mode) result(mode_)
361
+ character (* ), intent (in ) :: mode
362
+
363
+ integer :: i
364
+ character (:),allocatable :: a
365
+ logical :: lfirst(3 )
366
+
367
+ mode_ = ' r t'
368
+
369
+ if (len_trim (mode) == 0 ) return
370
+ a= trim (adjustl (mode))
371
+
372
+ lfirst = .true.
373
+ do i= 1 ,len (a)
374
+ if (lfirst(1 ) &
375
+ .and. (a(i:i) == ' r' .or. a(i:i) == ' w' .or. a(i:i) == ' a' .or. a(i:i) == ' x' ) &
376
+ ) then
377
+ mode_(1 :1 ) = a(i:i)
378
+ lfirst(1 )= .false.
379
+ else if (lfirst(2 ) .and. a(i:i) == ' +' ) then
380
+ mode_(2 :2 ) = a(i:i)
381
+ lfirst(2 )= .false.
382
+ else if (lfirst(3 ) .and. (a(i:i) == ' t' .or. a(i:i) == ' b' )) then
383
+ mode_(3 :3 ) = a(i:i)
384
+ lfirst(3 )= .false.
385
+ else if (a(i:i) == ' ' ) then
386
+ cycle
387
+ else if (any (.not. lfirst)) then
388
+ call error_stop(" Wrong mode: " // trim (a))
389
+ else
390
+ call error_stop(" Wrong character: " // a(i:i))
391
+ endif
392
+ end do
393
+
269
394
end function
270
395
271
396
end module
0 commit comments