MOM6
MOM_string_functions.F90
Go to the documentation of this file.
2 !***********************************************************************
3 !* GNU General Public License *
4 !* This file is a part of MOM. *
5 !* *
6 !* MOM is free software; you can redistribute it and/or modify it and *
7 !* are expected to follow the terms of the GNU General Public License *
8 !* as published by the Free Software Foundation; either version 2 of *
9 !* the License, or (at your option) any later version. *
10 !* *
11 !* MOM is distributed in the hope that it will be useful, but WITHOUT *
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
13 !* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public *
14 !* License for more details. *
15 !* *
16 !* For the full text of the GNU General Public License, *
17 !* write to: Free Software Foundation, Inc., *
18 !* 675 Mass Ave, Cambridge, MA 02139, USA. *
19 !* or see: http://www.gnu.org/licenses/gpl.html *
20 !***********************************************************************
21 
22 !********+*********+*********+*********+*********+*********+*********+**
23 !* *
24 !* By Alistair Adcroft and Robert Hallberg, last updated Sept. 2013. *
25 !* *
26 !* The functions here perform a set of useful manipulations of *
27 !* character strings. Although they are a part of MOM6, the do not *
28 !* require any other MOM software to be useful. *
29 !* *
30 !********+*********+*********+*********+*********+*********+*********+**
31 
32 implicit none ; private
33 
34 public lowercase, uppercase
35 public left_int, left_ints
36 public left_real, left_reals
38 public extractword
39 public extract_word
40 public extract_integer
41 public extract_real
42 public remove_spaces
43 public slasher
44 
45 contains
46 
47 function lowercase(input_string)
48 ! This function returns a string in which all uppercase letters have been
49 ! replaced by their lowercase counterparts. It is loosely based on the
50 ! lowercase function in mpp_util.F90.
51  ! Arguments
52  character(len=*), intent(in) :: input_string
53  character(len=len(input_string)) :: lowercase
54  ! Local variables
55  integer, parameter :: co=iachar('a')-iachar('A') ! case offset
56  integer :: k
57 
58  lowercase = input_string
59  do k=1, len_trim(input_string)
60  if (lowercase(k:k) >= 'A' .and. lowercase(k:k) <= 'Z') &
61  lowercase(k:k) = achar(ichar(lowercase(k:k))+co)
62  end do
63 end function lowercase
64 
65 function uppercase(input_string)
66  character(len=*), intent(in) :: input_string
67  character(len=len(input_string)) :: uppercase
68 ! This function returns a string in which all lowercase letters have been
69 ! replaced by their uppercase counterparts. It is loosely based on the
70 ! uppercase function in mpp_util.F90.
71  ! Arguments
72  integer, parameter :: co=iachar('A')-iachar('a') ! case offset
73  integer :: k
74 
75  uppercase = input_string
76  do k=1, len_trim(input_string)
77  if (uppercase(k:k) >= 'a' .and. uppercase(k:k) <= 'z') &
78  uppercase(k:k) = achar(ichar(uppercase(k:k))+co)
79  end do
80 end function uppercase
81 
82 function left_int(i)
83 ! Returns a character string of a left-formatted integer
84 ! e.g. "123 " (assumes 19 digit maximum)
85  ! Arguments
86  character(len=19) :: left_int
87  integer, intent(in) :: i
88  ! Local variables
89  character(len=19) :: tmp
90  write(tmp(1:19),'(I19)') i
91  write(left_int(1:19),'(A)') adjustl(tmp)
92 end function left_int
93 
94 function left_ints(i)
95 ! Returns a character string of a comma-separated, compact formatted,
96 ! integers e.g. "1, 2, 3, 4"
97  ! Arguments
98  character(len=1320) :: left_ints
99  integer, intent(in) :: i(:)
100  ! Local variables
101  character(len=1320) :: tmp
102  integer :: j
103  write(left_ints(1:1320),'(A)') trim(left_int(i(1)))
104  if (size(i)>1) then
105  do j=2,size(i)
106  tmp=left_ints
107  write(left_ints(1:1320),'(A,", ",A)') trim(tmp),trim(left_int(i(j)))
108  enddo
109  endif
110 end function left_ints
111 
112 function left_real(val)
113  real, intent(in) :: val
114  character(len=32) :: left_real
115 ! Returns a left-justified string with a real formatted like '(G)'
116  integer :: l, ind
117 
118  if ((abs(val) < 1.0e4) .and. (abs(val) >= 1.0e-3)) then
119  write(left_real, '(F30.11)') val
120  if (.not.isformattedfloatequalto(left_real,val)) then
121  write(left_real, '(F30.12)') val
122  if (.not.isformattedfloatequalto(left_real,val)) then
123  write(left_real, '(F30.13)') val
124  if (.not.isformattedfloatequalto(left_real,val)) then
125  write(left_real, '(F30.14)') val
126  if (.not.isformattedfloatequalto(left_real,val)) then
127  write(left_real, '(F30.15)') val
128  if (.not.isformattedfloatequalto(left_real,val)) then
129  write(left_real, '(F30.16)') val
130  endif
131  endif
132  endif
133  endif
134  endif
135  do
136  l = len_trim(left_real)
137  if ((l<2) .or. (left_real(l-1:l) == ".0") .or. &
138  (left_real(l:l) /= "0")) exit
139  left_real(l:l) = " "
140  enddo
141  elseif (val == 0.) then
142  left_real = "0.0"
143  else
144  write(left_real(1:32), '(ES23.14)') val
145  if (.not.isformattedfloatequalto(left_real,val)) then
146  write(left_real(1:32), '(ES23.15)') val
147  endif
148  do
149  ind = index(left_real,"0E")
150  if (ind == 0) exit
151  if (left_real(ind-1:ind-1) == ".") exit
152  left_real = left_real(1:ind-1)//left_real(ind+1:)
153  enddo
154  endif
155  left_real = adjustl(left_real)
156 end function left_real
157 
158 function left_reals(r,sep)
159 ! Returns a character string of a comma-separated, compact formatted, reals
160 ! e.g. "1., 2., 5*3., 5.E2"
161  ! Arguments
162  character(len=1320) :: left_reals
163  real, intent(in) :: r(:)
164  character(len=*), optional :: sep
165  ! Local variables
166  integer :: j, n, b, ns
167  logical :: doWrite
168  character(len=10) :: separator
169  n=1 ; dowrite=.true. ; left_reals='' ; b=1
170  if (present(sep)) then
171  separator=sep ; ns=len(sep)
172  else
173  separator=', ' ; ns=2
174  endif
175  do j=1,size(r)
176  dowrite=.true.
177  if (j<size(r)) then
178  if (r(j)==r(j+1)) then
179  n=n+1
180  dowrite=.false.
181  endif
182  endif
183  if (dowrite) then
184  if (b>1) then ! Write separator if a number has already been written
185  write(left_reals(b:),'(A)') separator
186  b=b+ns
187  endif
188  if (n>1) then
189  write(left_reals(b:),'(A,"*",A)') trim(left_int(n)),trim(left_real(r(j)))
190  else
191  write(left_reals(b:),'(A)') trim(left_real(r(j)))
192  endif
193  n=1 ; b=len_trim(left_reals)+1
194  endif
195  enddo
196 end function left_reals
197 
198 function isformattedfloatequalto(str, val)
199 ! Returns True if the string can be read/parsed to give the exact
200 ! value of "val"
201  character(len=*), intent(in) :: str
202  real, intent(in) :: val
203  logical :: isFormattedFloatEqualTo
204  ! Local variables
205  real :: scannedVal
206 
207  isformattedfloatequalto=.false.
208  read(str(1:),*,err=987) scannedval
209  if (scannedval == val) isformattedfloatequalto=.true.
210  987 return
211 end function isformattedfloatequalto
212 
213 !> Returns the string corresponding to the nth word in the argument
214 !! or "" if the string is not long enough. Both spaces and commas
215 !! are interpreted as separators.
216 character(len=120) function extractword(string, n)
217  character(len=*), intent(in) :: string
218  integer, intent(in) :: n
219 
220  extractword = extract_word(string, ' ,', n)
221 
222 end function extractword
223 
224 !> Returns the string corresponding to the nth word in the argument
225 !! or "" if the string is not long enough. Words are delineated
226 !! by the mandatory separators argument.
227 character(len=120) function extract_word(string, separators, n)
228  character(len=*), intent(in) :: string !< String to scan
229  character(len=*), intent(in) :: separators !< Characters to use for delineation
230  integer, intent(in) :: n !< Number of word to extract
231  ! Local variables
232  integer :: ns, i, b, e, nw
233  logical :: lastCharIsSeperator
234  extract_word = ''
235  lastcharisseperator = .true.
236  ns = len_trim(string)
237  i = 0; b=0; e=0; nw=0;
238  do while (i<ns)
239  i = i+1
240  if (lastcharisseperator) then ! search for end of word
241  if (verify(string(i:i),separators)==0) then
242  continue ! Multiple separators
243  else
244  lastcharisseperator = .false. ! character is beginning of word
245  b = i
246  continue
247  endif
248  else ! continue search for end of word
249  if (verify(string(i:i),separators)==0) then
250  lastcharisseperator = .true.
251  e = i-1 ! Previous character is end of word
252  nw = nw+1
253  if (nw==n) then
254  extract_word = trim(string(b:e))
255  return
256  endif
257  endif
258  endif
259  enddo
260  if (b<=ns .and. nw==n-1) extract_word = trim(string(b:ns))
261 end function extract_word
262 
263 !> Returns the integer corresponding to the nth word in the argument.
264 integer function extract_integer(string, separators, n, missing_value)
265  character(len=*), intent(in) :: string !< String to scan
266  character(len=*), intent(in) :: separators !< Characters to use for delineation
267  integer, intent(in) :: n !< Number of word to extract
268  integer, optional, intent(in) :: missing_value !< Value to assign if word is missing
269  ! Local variables
270  integer :: ns, i, b, e, nw
271  character(len=20) :: word
272 
273  word = extract_word(string, separators, n)
274 
275  if (len_trim(word)>0) then
276  read(word(1:len_trim(word)),*) extract_integer
277  else
278  if (present(missing_value)) then
279  extract_integer = missing_value
280  else
281  extract_integer = 0
282  endif
283  endif
284 
285 end function extract_integer
286 
287 !> Returns the real corresponding to the nth word in the argument.
288 real function extract_real(string, separators, n, missing_value)
289  character(len=*), intent(in) :: string !< String to scan
290  character(len=*), intent(in) :: separators !< Characters to use for delineation
291  integer, intent(in) :: n !< Number of word to extract
292  real, optional, intent(in) :: missing_value !< Value to assign if word is missing
293  ! Local variables
294  integer :: ns, i, b, e, nw
295  character(len=20) :: word
296 
297  word = extract_word(string, separators, n)
298 
299  if (len_trim(word)>0) then
300  read(word(1:len_trim(word)),*) extract_real
301  else
302  if (present(missing_value)) then
303  extract_real = missing_value
304  else
305  extract_real = 0
306  endif
307  endif
308 
309 end function extract_real
310 
311 !> Returns string with all spaces removed.
312 character(len=120) function remove_spaces(string)
313  character(len=*), intent(in) :: string !< String to scan
314  ! Local variables
315  integer :: ns, i, o
316  logical :: lastCharIsSeperator
317  lastcharisseperator = .true.
318  ns = len_trim(string)
319  i = 0; o = 0
320  do while (i<ns)
321  i = i+1
322  if (string(i:i) /= ' ') then ! Copy character to output string
323  o = o + 1
324  remove_spaces(o:o) = string(i:i)
325  endif
326  enddo
327  do i = o+1, 120
328  remove_spaces(i:i) = ' ' ! Wipe any non-empty characters
329  enddo
331 end function remove_spaces
332 
333 !> Returns true if a unit test of string_functions fails.
334 logical function string_functions_unit_tests(verbose)
335  ! Arguments
336  logical, intent(in) :: verbose !< If true, write results to stdout
337  ! Local variables
338  integer :: i(5) = (/ -1, 1, 3, 3, 0 /)
339  real :: r(8) = (/ 0., 1., -2., 1.3, 3.e-11, 3.e-11, 3.e-11, -5.1e12 /)
340  logical :: fail, v
341  fail = .false.
342  v = verbose
343  write(*,*) '==== MOM_string_functions: string_functions_unit_tests ==='
344  fail = fail .or. localtests(v,left_int(-1),'-1')
345  fail = fail .or. localtests(v,left_ints(i(:)),'-1, 1, 3, 3, 0')
346  fail = fail .or. localtests(v,left_real(0.),'0.0')
347  fail = fail .or. localtests(v,left_reals(r(:)),'0.0, 1.0, -2.0, 1.3, 3*3.0E-11, -5.1E+12')
348  fail = fail .or. localtests(v,left_reals(r(:),sep=' '),'0.0 1.0 -2.0 1.3 3*3.0E-11 -5.1E+12')
349  fail = fail .or. localtests(v,left_reals(r(:),sep=','),'0.0,1.0,-2.0,1.3,3*3.0E-11,-5.1E+12')
350  fail = fail .or. localtests(v,extractword("One Two,Three",1),"One")
351  fail = fail .or. localtests(v,extractword("One Two,Three",2),"Two")
352  fail = fail .or. localtests(v,extractword("One Two,Three",3),"Three")
353  fail = fail .or. localtests(v,extractword("One Two, Three",3),"Three")
354  fail = fail .or. localtests(v,extractword(" One Two,Three",1),"One")
355  fail = fail .or. localtests(v,extract_word("One,Two,Three",",",3),"Three")
356  fail = fail .or. localtests(v,extract_word("One,Two,Three",",",4),"")
357  fail = fail .or. localtests(v,remove_spaces("1 2 3"),"123")
358  fail = fail .or. localtests(v,remove_spaces(" 1 2 3"),"123")
359  fail = fail .or. localtests(v,remove_spaces("1 2 3 "),"123")
360  fail = fail .or. localtests(v,remove_spaces("123"),"123")
361  fail = fail .or. localtests(v,remove_spaces(" "),"")
362  fail = fail .or. localtests(v,remove_spaces(""),"")
363  fail = fail .or. localtesti(v,extract_integer("1","",1),1)
364  fail = fail .or. localtesti(v,extract_integer("1,2,3",",",1),1)
365  fail = fail .or. localtesti(v,extract_integer("1,2",",",2),2)
366  fail = fail .or. localtesti(v,extract_integer("1,2",",",3),0)
367  fail = fail .or. localtesti(v,extract_integer("1,2",",",4,4),4)
368  fail = fail .or. localtestr(v,extract_real("1.","",1),1.)
369  fail = fail .or. localtestr(v,extract_real("1.,2.,3.",",",1),1.)
370  fail = fail .or. localtestr(v,extract_real("1.,2.",",",2),2.)
371  fail = fail .or. localtestr(v,extract_real("1.,2.",",",3),0.)
372  fail = fail .or. localtestr(v,extract_real("1.,2.",",",4,4.),4.)
373  if (.not. fail) write(*,*) 'Pass'
375 end function string_functions_unit_tests
376 
377 !> True if str1 does not match str2. False otherwise.
378 logical function localtests(verbose,str1,str2)
379  logical, intent(in) :: verbose !< If true, write results to stdout
380  character(len=*), intent(in) :: str1 !< String
381  character(len=*), intent(in) :: str2 !< String
382  localtests=.false.
383  if (trim(str1)/=trim(str2)) localtests=.true.
384  if (localtests .or. verbose) then
385  write(*,*) '>'//trim(str1)//'<'
386  if (localtests) write(*,*) trim(str1),':',trim(str2), '<-- FAIL'
387  endif
388 end function localtests
389 
390 !> True if i1 is not equal to i2. False otherwise.
391 logical function localtesti(verbose,i1,i2)
392  logical, intent(in) :: verbose !< If true, write results to stdout
393  integer, intent(in) :: i1 !< Integer
394  integer, intent(in) :: i2 !< Integer
395  localtesti=.false.
396  if (i1/=i2) localtesti=.true.
397  if (localtesti .or. verbose) then
398  write(*,*) i1,i2
399  if (localtesti) write(*,*) i1,'!=',i2, '<-- FAIL'
400  endif
401 end function localtesti
402 
403 !> True if r1 is not equal to r2. False otherwise.
404 logical function localtestr(verbose,r1,r2)
405  logical, intent(in) :: verbose !< If true, write results to stdout
406  real, intent(in) :: r1 !< Float
407  real, intent(in) :: r2 !< Float
408  localtestr=.false.
409  if (r1/=r2) localtestr=.true.
410  if (localtestr .or. verbose) then
411  write(*,*) r1,r2
412  if (localtestr) write(*,*) r1,'!=',r2, '<-- FAIL'
413  endif
414 end function localtestr
415 
416 !> Returns a directory name that is terminated with a "/" or "./" if the
417 !! argument is an empty string.
418 function slasher(dir)
419  character(len=*), intent(in) :: dir !< A directory to be terminated with a "/"
420  !! or changed to "./" if it is blank.
421  character(len=len(dir)+2) :: slasher
422 
423  if (len_trim(dir) == 0) then
424  slasher = "./"
425  elseif (dir(len_trim(dir):len_trim(dir)) == '/') then
426  slasher = trim(dir)
427  else
428  slasher = trim(dir)//"/"
429  endif
430 end function slasher
431 
432 end module mom_string_functions
integer function, public extract_integer(string, separators, n, missing_value)
Returns the integer corresponding to the nth word in the argument.
character(len=120) function, public extract_word(string, separators, n)
Returns the string corresponding to the nth word in the argument or "" if the string is not long enou...
logical function localtests(verbose, str1, str2)
True if str1 does not match str2. False otherwise.
logical function localtestr(verbose, r1, r2)
True if r1 is not equal to r2. False otherwise.
character(len=19) function, public left_int(i)
character(len=32) function, public left_real(val)
logical function isformattedfloatequalto(str, val)
logical function localtesti(verbose, i1, i2)
True if i1 is not equal to i2. False otherwise.
character(len=len(input_string)) function, public lowercase(input_string)
character(len=len(dir)+2) function, public slasher(dir)
Returns a directory name that is terminated with a "/" or "./" if the argument is an empty string...
character(len=len(input_string)) function, public uppercase(input_string)
logical function, public string_functions_unit_tests(verbose)
Returns true if a unit test of string_functions fails.
character(len=120) function, public extractword(string, n)
Returns the string corresponding to the nth word in the argument or "" if the string is not long enou...
character(len=1320) function, public left_ints(i)
character(len=120) function, public remove_spaces(string)
Returns string with all spaces removed.
character(len=1320) function, public left_reals(r, sep)
real function, public extract_real(string, separators, n, missing_value)
Returns the real corresponding to the nth word in the argument.