Soil Water Balance (SWB2)
Loading...
Searching...
No Matches
file_operations.F90
Go to the documentation of this file.
2
3 use iso_c_binding, only : c_int, c_float, c_double, c_bool
4 use iso_fortran_env, only : iostat_end
5 use logfiles
6 use exceptions
8 use fstring
10 implicit none
11
12 private
13
15
16 integer (c_int), parameter :: max_str_len = 65536
17
18 type, public :: ascii_file_t
19
20 character (len=:), allocatable :: sfilename
21 character (len=:), allocatable :: sdelimiters
22 character (len=:), allocatable :: scommentchars
23 type (fstring_list_t) :: slcolnames
24 logical (c_bool) :: remove_extra_delimiters = false
25 integer (c_int) :: icurrentlinenum = 0
26 integer (c_int) :: inumberoflines = 0
27 integer (c_int) :: inumberofrecords = 0
28 integer (c_int) :: inumberofheaderlines = 1
29 logical (c_bool) :: lisopen = false
30 logical (c_bool) :: lreadonly = true
31 logical (c_bool) :: leof = false
32 integer (c_int) :: iunitnum
33 integer (c_int) :: istat
34 character (len=MAX_STR_LEN) :: sbuf
35 character (len=:), allocatable :: stmissingvalue
36
37 contains
38
39 procedure, private :: open_file_read_access_sub
40 procedure, private :: open_file_write_access_sub
41 generic :: open => open_file_read_access_sub, &
43
44 procedure, private :: close_file_sub
45 generic :: close => close_file_sub
46
47 procedure, private :: is_file_open_fn
48 generic :: isopen => is_file_open_fn
49
50 procedure, private :: have_we_reached_the_eof_fn
51 generic :: iseof => have_we_reached_the_eof_fn
52
53 procedure, private :: does_file_exist_fn
54 generic :: exists => does_file_exist_fn
55
56 procedure, private :: is_current_line_a_comment_fn
57 generic :: iscomment => is_current_line_a_comment_fn
58
59 procedure, private :: count_number_of_lines_sub
60 generic :: countlines => count_number_of_lines_sub
61
62 procedure, private :: return_num_lines_fn
63 generic :: numlines => return_num_lines_fn
64
65 procedure, private :: return_num_records_fn
66 generic :: numrecords => return_num_records_fn
67
68 procedure, private :: return_current_linenum_fn
69 generic :: currentlinenum => return_current_linenum_fn
70
71 procedure, private :: return_fortran_unit_number_fn
73
74 procedure, private :: read_header_fn
75 generic, public :: readheader => read_header_fn
76
77 procedure, private :: read_line_of_data_fn
78 generic, public :: readline => read_line_of_data_fn
79
80 procedure, private :: write_line_of_data_sub
81 generic, public :: writeline => write_line_of_data_sub
82
83 end type ascii_file_t
84
85contains
86
87 function return_fortran_unit_number_fn(this) result(iUnitNum)
88
89 class(ascii_file_t) :: this
90 integer (c_int) :: iunitnum
91
92 iunitnum = this%iUnitNum
93
95
96!--------------------------------------------------------------------------------------------------
97
98 function return_num_lines_fn(this) result(iNumLines)
99
100 class(ascii_file_t) :: this
101 integer (c_int) :: inumlines
102
103 inumlines = this%iNumberOfLines
104
105 end function return_num_lines_fn
106
107!--------------------------------------------------------------------------------------------------
108
109 function return_num_records_fn(this) result(iNumRecords)
110
111 class(ascii_file_t) :: this
112 integer (c_int) :: inumrecords
113
114 inumrecords = this%iNumberOfRecords
115
116 end function return_num_records_fn
117
118!--------------------------------------------------------------------------------------------------
119
120 function return_current_linenum_fn(this) result(iCurrentLinenum)
121
122 class(ascii_file_t) :: this
123 integer (c_int) :: icurrentlinenum
124
125 icurrentlinenum = this%iCurrentLinenum
126
127 end function return_current_linenum_fn
128
129!--------------------------------------------------------------------------------------------------
130
131 function have_we_reached_the_eof_fn(this) result(lIsEOF)
132
133 class(ascii_file_t) :: this
134 logical (c_bool) :: liseof
135
136 liseof = this%lEOF
137
138 end function have_we_reached_the_eof_fn
139
140!--------------------------------------------------------------------------------------------------
141
142 function is_current_line_a_comment_fn(this) result(lIsComment)
143
144 class(ascii_file_t) :: this
145 logical (c_bool) :: liscomment
146
147 ! [ LOCALS ]
148 integer (c_int) :: iindex
149 integer (c_int) :: ilen
150 character (len=:), allocatable :: strimmedinput
151 character (len=1) :: sfirstchar
152
153 liscomment = false
154
155 strimmedinput = adjustl(trim(this%sBuf))
156 ilen = len( strimmedinput )
157
158 if (ilen > 0) then
159
160 sfirstchar = strimmedinput(1:1)
161 !-- why 'verify' and not 'scan' in older code??
162 !-----
163 ! SCAN(STRING, SET): If no character of SET is found in STRING, the result is zero.
164 iindex = scan( sfirstchar , this%sCommentChars )
165 if ( iindex > 0 ) liscomment = true
166
167 endif
168
170
171!--------------------------------------------------------------------------------------------------
172
173 subroutine open_file_read_access_sub(this, sFilename, sCommentChars, sDelimiters, lHasHeader )
174
175 class(ascii_file_t), intent(inout) :: this
176 character (len=*), intent(in) :: sfilename
177 character (len=*), intent(in) :: scommentchars
178 character (len=*), intent(in) :: sdelimiters
179 logical (c_bool), intent(in), optional :: lhasheader
180
181 ! [ LOCALS ]
182 character (len=len(sFilename) ) :: sfilename_l
183
184 ! 'fix_pathname' simply replaces forward slashes and backslashes with whatever the native OS
185 ! path delimiter character should be
186 sfilename_l = fix_pathname( sfilename )
187
188 this%sCommentChars = scommentchars
189 this%sDelimiters = sdelimiters
190
191 if (present( lhasheader ) ) then
192 if (.not. lhasheader ) this%iNumberOfHeaderLines = 0
193 endif
194
195 if ( this%isOpen() ) then
196
197 call die( "PROGRAMMING ERROR--file already open: "//dquote( fully_qualified_filename( sfilename_l ) )//"." )
198
199 else
200
201 this%sFilename = fully_qualified_filename(sfilename_l)
202
203 open(newunit=this%iUnitNum, file=fully_qualified_filename( sfilename_l ), iostat=this%iStat, action='READ')
204 call assert(this%iStat == 0, "Failed to open file "//dquote( fully_qualified_filename( sfilename_l ) )//"." &
205 //" Exit code: "//ascharacter( this%iStat )//".", __file__, __line__)
206
207 this%lIsOpen = true
208 this%lEOF = false
209
210 call this%countLines()
211
212 call logs%write( smessage="Opened file "//dquote( fully_qualified_filename( sfilename_l ) ), itab=22, &
213 ilinesbefore=1, iloglevel=log_all )
214 call logs%write( "Comment characters: "//dquote(scommentchars), itab=42 )
215 call logs%write( "Number of lines in file: "//ascharacter( this%numLines() ), itab=37 )
216 call logs%write( "Number of lines excluding blanks, headers and comments: " &
217 //ascharacter( this%numRecords() ), itab=6 )
218
219 endif
220
221 end subroutine open_file_read_access_sub
222
223!--------------------------------------------------------------------------------------------------
224
225 subroutine open_file_write_access_sub(this, sFilename, lQuiet )
226
227 class(ascii_file_t), intent(inout) :: this
228 character (len=*), intent(in) :: sfilename
229 logical (c_bool), intent(in), optional :: lquiet
230
231 ! [ LOCALS ]
232 logical :: lquiet_l
233 character (len=len(sFilename) ) :: sfilename_l
234
235 sfilename_l = fix_pathname( sfilename )
236
237 if ( present( lquiet ) ) then
238 lquiet_l = lquiet
239 else
240 lquiet_l = false
241 endif
242
243 if (.not. this%isOpen() ) then
244 this%sFilename = trim(sfilename_l)
245 open(newunit=this%iUnitNum, file=sfilename_l, iostat=this%iStat, action='WRITE')
246 call assert(this%iStat == 0, "Failed to open file "//dquote(sfilename_l)//".", __file__, __line__)
247
248 this%lIsOpen = true
249 this%lEOF = false
250 this%lReadOnly = false
251 this%sFilename = trim(sfilename_l)
252
253 if ( .not. lquiet_l ) &
254 call logs%write( "Opened file with write access: "//dquote(sfilename_l))
255
256 else
257 call logs%write( "Failed to open file "//dquote(sfilename_l)//" with WRITE access" )
258 endif
259
260 end subroutine open_file_write_access_sub
261
262!--------------------------------------------------------------------------------------------------
263
264 subroutine close_file_sub(this)
265
266 class(ascii_file_t) :: this
267
268
269 close(unit=this%iUnitNum, iostat=this%iStat)
270
271 this%lIsOpen = false
272
273 end subroutine close_file_sub
274
275!--------------------------------------------------------------------------------------------------
276
277 function does_file_exist_fn(this, sFilename) result(lExists)
278
279 class(ascii_file_t) :: this
280 character (len=*), intent(in) :: sfilename
281 logical :: lexists
282
283 inquire(file=fully_qualified_filename( sfilename ), exist=lexists)
284
285 end function does_file_exist_fn
286
287!--------------------------------------------------------------------------------------------------
288
289 pure function is_file_open_fn(this) result(lIsOpen)
290
291 class(ascii_file_t), intent(in) :: this
292 logical(c_bool) :: lisopen
293
294 lisopen = this%lIsOpen
295
296 end function is_file_open_fn
297
298!--------------------------------------------------------------------------------------------------
299
301
302 class(ascii_file_t), intent(inout) :: this
303
304 ! [ LOCALS ]
305 integer (c_int) :: istat
306 integer (c_int) :: inumlines
307 integer (c_int) :: inumrecords
308
309 inumlines = 0
310 inumrecords = 0
311 istat = 0
312
313 if ( this%isOpen() ) then
314
315 rewind( unit = this%iUnitNum )
316
317 do
318
319 read (unit = this%iUnitNum, fmt="(a)", iostat = istat) this%sBuf
320
321 if (istat == iostat_end) exit
322
323 inumlines = inumlines + 1
324
325 if ( .not. this%isComment() ) inumrecords = inumrecords + 1
326
327 enddo
328
329 rewind( unit = this%iUnitNum )
330
331 this%iNumberOfLines= inumlines
332 this%iNumberOfRecords = inumrecords - this%iNumberOfHeaderLines
333
334 endif
335
336 end subroutine count_number_of_lines_sub
337
338!--------------------------------------------------------------------------------------------------
339
340 function read_header_fn(this) result (stList)
341
342 class(ascii_file_t), intent(inout) :: this
343 type (fstring_list_t) :: stlist
344
345 ! [ LOCALS ]
346 character (len=MAX_STR_LEN) :: ssubstring
347 character (len=MAX_STR_LEN) :: ssubstringclean
348
349 this%sBuf = this%readline()
350 call stlist%clear()
351
352 do while ( len_trim( this%sBuf ) > 0)
353
354 call chomp( str=this%sBuf, substr=ssubstring, delimiter_chr=this%sDelimiters, &
355 remove_extra_delimiters=this%remove_extra_delimiters )
356
357 call replace(ssubstring, " ", "_")
358 call replace(ssubstring, ".", "_")
359 ssubstringclean = trim( clean( ssubstring, double_quote ) )
360 call stlist%append( trim( adjustl( ssubstringclean ) ) )
361
362 enddo
363
364 end function read_header_fn
365
366!--------------------------------------------------------------------------------------------------
367
368 subroutine write_line_of_data_sub( this, sText )
369
370 class(ascii_file_t), intent(inout) :: this
371 character (len=*), intent(in) :: sText
372
373 ! [ LOCALS ]
374 integer (c_int) :: iStat
375
376 call assert( .not. this%lReadOnly, "INTERNAL ERROR -- File " &
377 //dquote( fully_qualified_filename( this%sFilename ) ) &
378 //" was opened as READONLY.", __file__, __line__ )
379
380 if (this%isOpen() ) then
381
382 write ( unit = this%iUnitNum, fmt = "(a)", iostat = istat ) trim(stext)
383
384 endif
385
386 end subroutine write_line_of_data_sub
387
388!--------------------------------------------------------------------------------------------------
389
390 function read_line_of_data_fn(this) result(sText)
391
392 class(ascii_file_t), intent(inout) :: this
393 character (len=:), allocatable :: stext
394
395 ! [ LOCALS ]
396 integer (c_int) :: istat
397 logical (c_bool) :: liscomment
398
399 liscomment = true
400
401 do while ( liscomment .and. this%isOpen() )
402
403 if (this%isOpen() ) then
404
405 read (unit = this%iUnitNum, fmt = "(a)", iostat = istat) this%sBuf
406
407 if (istat == iostat_end) then
408 this%lEOF = true
409 stext = ""
410 call this%close()
411 else
412 stext = trim(this%sBuf)
413 this%iCurrentLinenum = this%iCurrentLinenum + 1
414 endif
415
416 liscomment = this%isComment()
417
418 endif
419
420 enddo
421
422 end function read_line_of_data_fn
423
424!--------------------------------------------------------------------------------------------------
425
426 function fully_qualified_filename( filename, pathname )
427
428 character(len=*), intent(in) :: filename
429 character(len=*), intent(in), optional :: pathname
430 character(len=:), allocatable :: fully_qualified_filename
431
432 if (.not. present(pathname) ) then
433
435
436 else
437
438 fully_qualified_filename = trim( pathname )//fix_pathname(filename)
439
440 endif
441
442 end function fully_qualified_filename
443
444
445end module file_operations
This module contains physical constants and convenience functions aimed at performing unit conversion...
character(len=len_trim(input_pathname)) function fix_pathname(input_pathname)
logical(c_bool), parameter, public true
logical(c_bool), parameter, public false
subroutine, public die(smessage, smodule, iline, shints, scalledby, icalledbyline)
subroutine count_number_of_lines_sub(this)
type(fstring_list_t) function read_header_fn(this)
subroutine open_file_write_access_sub(this, sfilename, lquiet)
character(len=:) function, allocatable, public fully_qualified_filename(filename, pathname)
character(len=:) function, allocatable read_line_of_data_fn(this)
subroutine close_file_sub(this)
integer(c_int), parameter max_str_len
subroutine write_line_of_data_sub(this, stext)
integer(c_int) function return_num_records_fn(this)
logical(c_bool) function have_we_reached_the_eof_fn(this)
integer(c_int) function return_num_lines_fn(this)
pure logical(c_bool) function is_file_open_fn(this)
logical(c_bool) function is_current_line_a_comment_fn(this)
subroutine open_file_read_access_sub(this, sfilename, scommentchars, sdelimiters, lhasheader)
integer(c_int) function return_current_linenum_fn(this)
logical function does_file_exist_fn(this, sfilename)
integer(c_int) function return_fortran_unit_number_fn(this)
character(len=1), parameter, public double_quote
Definition fstring.F90:173
type(logfile_t), public logs
Definition logfiles.F90:62