diff --git a/docs/BOOK_M_strings.html b/docs/BOOK_M_strings.html index 8a8082bb..d2963658 100755 --- a/docs/BOOK_M_strings.html +++ b/docs/BOOK_M_strings.html @@ -4,7 +4,7 @@ - + + +
is a resource-intensive routine. Once the end of
+ the string is found, it is probably best to keep track of it in
+ order to avoid repeated calls to len_white. Because they
+ might be more efficient, consider looking for vendor-supplied or
+ system-optimized equivalents. For example:
+
+ o lnblnk - Solaris f77
+ o len_trim - FORTRAN 90
+
+ subroutine message(s)
+ character(len=*) :: s ! s is of variable length
+ lgth=len(s) ! get total length of variable
+ ! explicitly specify a substring instead of just variable name
+ lastnb = len_white(s(:lgth))
+ write(*,*)'error:[',s(:lastnb),']'
+ end subroutine messages
+
+John S. Urban
+Public Domain
+ +listout(3f) - [M_strings:NUMERIC] expand a list of numbers where negative numbers denote range ends (1 -10 means 1 thru 10) (LICENSE:PD)
+subroutine listout(icurve_lists,icurve_expanded,inums,ierr)
+ integer,intent(in) :: icurve_lists(:)
+ integer,intent(out) :: icurve_expanded(:)
+ integer,intent(out) :: inums
+ integer,intent(out) :: ierr
+
+expand a list of whole numbers where negative numbers indicate a range. So [10,-20] would be expanded to [10,11,12,13,14,15,16,17,18,19,20].
+icurve_expanded(:)
+output array; assumed large enough to hold returned list
inums
+number of icurve_expanded numbers on output
ierr
+zero if no error occurred
Sample program:
+ program demo_listout
+ use M_strings, only : listout
+ implicit none
+ integer,allocatable :: icurve_lists(:)
+ integer :: icurve_expanded(1000)
+ ! icurve_lists is input array
+ integer :: inums
+ ! icurve_expanded is output array
+ integer :: i
+ ! number of icurve_lists values on input,
+ ! number of icurve_expanded numbers on output
+ integer :: ierr
+ icurve_lists=[1, 20, -30, 101, 100, 99, 100, -120, 222, -200]
+ inums=size(icurve_lists)
+ call listout(icurve_lists,icurve_expanded,inums,ierr)
+ if(ierr == 0)then
+ write(*,'(i0)')(icurve_expanded(i),i=1,inums)
+ else
+ write(*,'(a,i0)')'error occurred in *listout* ',ierr
+ write(*,'(i0)')(icurve_expanded(i),i=1,inums)
+ endif
+ end program demo_listout
+
+Results:
+ > 1 20 21 22 23
+ > 24 25 26 27 28
+ > 29 30 101 100 99
+ > 100 101 102 103 104
+ > 105 106 107 108 109
+ > 110 111 112 113 114
+ > 115 116 117 118 119
+ > 120 222 221 220 219
+ > 218 217 216 215 214
+ > 213 212 211 210 209
+ > 208 207 206 205 204
+ > 203 202 201 200
+
+John S. Urban
+Public Domain
+longest_common_substring(3f) - [M_strings:COMPARE] function that returns the longest common substring of two strings.
+function longest_common_substring(a,b) result(match)
+ character(len=*),intent(in) :: a, b
+ character(len=:),allocatable :: match
+
+function that returns the longest common substring of two strings.
+Note that substrings are consecutive characters within a string. This distinguishes them from subsequences, which is any sequence of characters within a string, even if there are extraneous characters in between them.
+Hence, the longest common subsequence between "thisisatest" and "testing123testing" is "tsitest", whereas the longest common substring is just "test".
+Sample program
+ program demo_longest_common_substring
+ use M_strings, only : longest_common_substring
+ implicit none
+ call compare('testing123testingthing','thisis', 'thi')
+ call compare('testing', 'sting', 'sting')
+ call compare('thisisatest_stinger','testing123testingthing','sting')
+ call compare('thisisatest_stinger', 'thisis', 'thisis')
+ call compare('thisisatest', 'testing123testing', 'test')
+ call compare('thisisatest', 'thisisatest', 'thisisatest')
+ contains
+
+ subroutine compare(a,b,answer)
+ character(len=*),intent(in) :: a, b, answer
+ character(len=:),allocatable :: match
+ character(len=*),parameter :: g='(*(g0))'
+ match=longest_common_substring(a,b)
+ write(*,g) 'comparing "',a,'" and "',b,'"'
+ write(*,g) merge('(PASSED) "','(FAILED) "',answer == match), &
+ & match,'"; expected "',answer,'"'
+ end subroutine compare
+
+ end program demo_longest_common_substring
+
+expected output
+ comparing "testing123testingthing" and "thisis"
+ (PASSED) "thi"; expected "thi"
+ comparing "testing" and "sting"
+ (PASSED) "sting"; expected "sting"
+ comparing "thisisatest_stinger" and "testing123testingthing"
+ (PASSED) "sting"; expected "sting"
+ comparing "thisisatest_stinger" and "thisis"
+ (PASSED) "thisis"; expected "thisis"
+ comparing "thisisatest" and "testing123testing"
+ (PASSED) "test"; expected "test"
+ comparing "thisisatest" and "thisisatest"
+ (PASSED) "thisisatest"; expected "thisisatest"
+
+lower(3f) - [M_strings:CASE] changes a string to lowercase over specified range (LICENSE:PD)
+elemental pure function lower(str,begin,end) result (string)
+ character(*), intent(in) :: str
+ integer,optional :: begin, end
+ character(len(str)) :: string ! output string
+
+lower(string) returns a copy of the input string with all characters converted to miniscule over the specified range, assuming ASCII character sets are being used. If no range is specified the entire string is converted to miniscule.
+str
+string to convert to miniscule
begin
+optional starting position in "str" to begin converting to miniscule
end
+optional ending position in "str" to stop converting to miniscule
The terms "uppercase" and "lowercase" date back to the early days of the mechanical printing press. Individual metal alloy casts of each needed letter, or punctuation symbol, were meticulously added to a press block, by hand, before rolling out copies of a page. These metal casts were stored and organized in wooden cases. The more often needed miniscule letters were placed closer to hand, in the lower cases of the work bench. The less often needed, capitalized, majuscule letters, ended up in the harder to reach upper cases.
+Sample program:
+ program demo_lower
+ use M_strings, only: lower
+ implicit none
+ character(len=:),allocatable :: s
+ s=' ABCDEFG abcdefg '
+ write(*,*) 'mixed-case input string is ```.',s
+ write(*,*) 'lower-case output string is ```',lower(s)
+ end program demo_lower
+
+Expected output
+ mixed-case input string is ```. ABCDEFG abcdefg
+ lower-case output string is ``` abcdefg abcdefg
+
+John S. Urban
+Public Domain
+lpad(3f) - [M_strings:LENGTH] convert to a cropped string and then blank-pad on the left to requested length (LICENSE:PD)
+function lpad(valuein,length) result(strout)
+ class*,intent(in) :: valuein(..)
+ integer,intent(in) :: length
+
+lpad(3f) converts a scalar value to a cropped string and then pads it on the left with spaces to at least the specified length. If the trimmed input is longer than the requested length the string is returned trimmed of leading and trailing spaces.
+str
+The input may be scalar or a vector. the input value to return as a string, padded on the left to the specified length if shorter than length. The input may be any intrinsic scalar which is converted to a cropped string much as if written with list-directed output.
length
+The minimum string length to return
Sample Program:
+ program demo_lpad
+ use M_strings, only : lpad
+ implicit none
+ write(*,'("[",a,"]")') lpad( 'my string', 20)
+ write(*,'("[",a,"]")') lpad( 'my string ', 20)
+ write(*,'("[",a,"]")') lpad( ' my string', 20)
+ write(*,'("[",a,"]")') lpad( ' my string ', 20)
+ write(*,'("[",a,"]")') lpad( valuein=42 , length=7)
+ write(*,'("[",a,"]")') lpad( valuein=1.0/9.0 , length=20)
+ end program demo_lpad
+
+Results:
+ > [ my string]
+ > [ my string]
+ > [ my string]
+ > [ my string]
+ > [ 42]
+ > [ 0.111111112]
+
+John S. Urban
+Public Domain
+matching_delimiter(3f) - [M_strings:QUOTES] find position of matching delimiter (LICENSE:PD)
+impure elemental subroutine matching_delimiter(str,ipos,imatch)
+ character(len=*),intent(in) :: str
+ integer,intent(in) :: ipos
+ integer,intent(out) :: imatch
+
+Sets imatch to the position in string of the delimiter matching the delimiter in position ipos. Allowable delimiters are (), [], {}, <>.
+str
+input string to locate delimiter position in
ipos
+position of delimiter to find match for
imatch
+location of matching delimiter. If no match is found, zero (0) is returned.
Sample program:
+ program demo_matching_delimiter
+ use M_strings, only : matching_delimiter
+ implicit none
+ character(len=128) :: str
+ integer :: imatch
+
+ str=' a [[[[b] and ] then ] finally ]'
+ write(*,*)'string=',str
+ call matching_delimiter(str,1,imatch)
+ write(*,*)'location=',imatch
+ call matching_delimiter(str,4,imatch)
+ write(*,*)'location=',imatch
+ call matching_delimiter(str,5,imatch)
+ write(*,*)'location=',imatch
+ call matching_delimiter(str,6,imatch)
+ write(*,*)'location=',imatch
+ call matching_delimiter(str,7,imatch)
+ write(*,*)'location=',imatch
+ call matching_delimiter(str,32,imatch)
+ write(*,*)'location=',imatch
+
+ end program demo_matching_delimiter
+
+John S. Urban
+Public Domain
+merge_str(3f) - [M_strings:LENGTH] pads strings to same length and then calls MERGE(3f) (LICENSE:PD)
+function merge_str(str1,str2,expr) result(strout)
+ character(len=*),intent(in),optional :: str1
+ character(len=*),intent(in),optional :: str2
+ logical,intent(in) :: expr
+ character(len=:),allocatable :: strout
+
+merge_str(3f) pads the shorter of str1 and str2 to the longest length of str1 and str2 and then calls MERGE(padded_str1,padded_str2,expr). It trims trailing spaces off the result and returns the trimmed string. This makes it easier to call MERGE(3f) with strings, as MERGE(3f) requires the strings to be the same length.
+NOTE: STR1 and STR2 are always required even though declared optional. this is so the call "STR_MERGE(A,B,present(A))" is a valid call. The parameters STR1 and STR2 when they are optional parameters can be passed to a procedure if the options are optional on the called procedure.
+STR1
+string to return if the logical expression EXPR is true
STR2
+string to return if the logical expression EXPR is false
EXPR
+logical expression to evaluate to determine whether to return STR1 when true, and STR2 when false.
Sample Program:
+ program demo_merge_str
+ use M_strings, only : merge_str
+ implicit none
+ character(len=:), allocatable :: answer
+ answer=merge_str('first string', &
+ & 'second string is longer',10 == 10)
+ write(*,'("[",a,"]")') answer
+ answer=merge_str('first string', &
+ & 'second string is longer',10 /= 10)
+ write(*,'("[",a,"]")') answer
+ end program demo_merge_str
+
+Expected output
+ [first string]
+ [second string is longer]
+
+John S. Urban
+Public Domain
+modif(3f) - [M_strings:EDITING] emulate the MODIFY command from the line editor XEDIT (LICENSE:PD)
+subroutine modif(cline,cmod)
+ character(len=*) :: cline ! input string to change
+ ! directive provides directions on changing string
+ character(len=*) :: cmod
+
+MODIF(3f) Modifies the line currently pointed at using a directive that acts much like a line editor directive. Primarily used to create interactive utilities such as input history editors for interactive line-mode programs.
+the modify directives are as follows-
+^STRING#
+Causes the string of characters between the ^ and the next # to be inserted before the characters pointed to by the ^. an ^ or & within the string is treated as a regular character. If the closing # is not specified, MODIF(3f) inserts the remainder of the line as if a # was specified after the last nonblank character.
There are two exceptions. the combination ^# causes a # to be inserted before the character pointed to by the ^, and an ^ as the last character of the directives causes a blank to be inserted.
#
+(When not the first # after an ^) causes the character above it to be deleted.
&
+Replaces the character above it with a space.
(SPACE)
+A space below a character leaves it unchanged.
Any other character replaces the character above it.
+Example input/output:
+ THE INPUT LINE```..... 10 THIS STRING TO BE MORTIFD
+ THE DIRECTIVES LINE``` ^ IS THE# D# ^IE
+ ALTERED INPUT LINE```. 10 THIS IS THE STRING TO BE MODIFIED
+
+Sample program:
+ program demo_modif
+ use M_strings, only : modif
+ implicit none
+ character(len=256) :: line
+ integer :: ios
+ integer :: count
+ integer :: COMMAND_LINE_LENGTH
+ character(len=:),allocatable :: COMMAND_LINE
+ ! get command name length
+ call get_command_argument(0,length=count)
+ ! get command line length
+ call get_command(length=COMMAND_LINE_LENGTH)
+ ! allocate string big enough to hold command line
+ allocate(character(len=COMMAND_LINE_LENGTH+200) :: COMMAND_LINE)
+ ! get command line as a string
+ call get_command(command=COMMAND_LINE)
+ ! trim leading spaces just in case
+ COMMAND_LINE=adjustl(COMMAND_LINE)
+ ! remove command name
+ COMMAND_LINE=adjustl(COMMAND_LINE(COUNT+2:))
+ INFINITE: do
+ read(*,'(a)',iostat=ios)line
+ if(ios /= 0)exit
+ call modif(line,COMMAND_LINE)
+ write(*,'(a)')trim(line)
+ enddo INFINITE
+ end program demo_modif
+
+John S. Urban
+Public Domain
+msg(3f) - [M_strings:TYPE] converts any standard scalar type to a string (LICENSE:PD)
+function msg(g1,g2g3,g4,g5,g6,g7,g8,g9,sep)
+ class(*),intent(in),optional :: g1,g2,g3,g4,g5,g6,g7,g8,g9
+ character(len=*),intent(in),optional :: sep
+ character(len=:),allocatable :: msg
+
+msg(3f) builds a space-separated string from up to nine scalar values.
+g[1-9]
+optional value to print the value of after the message. May be of type INTEGER, LOGICAL, REAL, DOUBLEPRECISION, COMPLEX, or CHARACTER.
sep
+separator between values. Defaults to a space
Sample program:
+ program demo_msg
+ use M_strings, only : msg
+ implicit none
+ character(len=:),allocatable :: pr
+ character(len=:),allocatable :: frmt
+ integer :: biggest
+
+ pr=msg('HUGE(3f) integers',huge(0),&
+ & 'and real',huge(0.0),'and double',huge(0.0d0))
+ write(*,'(a)')pr
+ pr=msg('real :',&
+ & huge(0.0),0.0,12345.6789,tiny(0.0) )
+ write(*,'(a)')pr
+ pr=msg('doubleprecision :',&
+ & huge(0.0d0),0.0d0,12345.6789d0,tiny(0.0d0) )
+ write(*,'(a)')pr
+ pr=msg('complex :',&
+ & cmplx(huge(0.0),tiny(0.0)) )
+ write(*,'(a)')pr
+
+ ! create a format on the fly
+ biggest=huge(0)
+ ! +0 for gfortran-11 bug
+ frmt=msg('(*(i',int(log10(real(biggest)))+0,':,1x))',sep='')
+ write(*,*)'format=',frmt
+
+ ! although it will often work, using msg(3f) in an I/O statement
+ ! is not recommended
+ write(*,*)msg('program will now stop')
+
+ end program demo_msg
+
+Output
+ HUGE(3f) integers 2147483647 and real 3.40282347E+38
+ and double 1.7976931348623157E+308
+ real : 3.40282347E+38 0.00000000
+ 12345.6787 1.17549435E-38
+ doubleprecision : 1.7976931348623157E+308 0.0000000000000000
+ 12345.678900000001 2.2250738585072014E-308
+ complex : (3.40282347E+38,1.17549435E-38)
+ format=(*(i9:,1x))
+ program will now stop
+
+John S. Urban
+Public Domain
+M_strings(3f) - [M_strings::INTRO] Fortran string module
+The M_strings(3fm) module is a collection of Fortran procedures that supplement the built-in intrinsic string routines. Routines for parsing, tokenizing, changing case, substituting new strings for substrings, locating strings with simple wildcard expressions, removing tabs and line terminators and other string manipulations are included.
+M_strings__oop(3fm) is a companion module that provides an OOP interface to the M_strings module.
+public entities:
+ use M_strings,only : split, slice, sep, delim, chomp, strtok
+ use M_strings,only : split2020, find_field
+ use M_strings,only : substitute, change, modif, transliterate, &
+ & reverse, squeeze
+ use M_strings,only : replace, join
+ use M_strings,only : upper, lower, upper_quoted
+ use M_strings,only : rotate13, percent_encode
+ use M_strings,only : adjustc, compact, nospace, indent
+ use M_strings,only : crop, clip, unquote, quote, matching_delimiter
+ use M_strings,only : len_white, pad, lpad, cpad, rpad, zpad, &
+ & stretch, lenset, merge_str
+ use M_strings,only : switch, s2c, c2s
+ use M_strings,only : noesc, notabs, dilate, expand, visible
+ use M_strings,only : longest_common_substring
+ use M_strings,only : string_to_value, string_to_values, s2v, s2vs
+ use M_strings,only : int, real, dble, nint
+ use M_strings,only : atoi, atol, aton
+ use M_strings,only : value_to_string, v2s, msg
+ use M_strings,only : listout, getvals
+ use M_strings,only : glob, ends_with
+ use M_strings,only : paragraph
+ use M_strings,only : base, decodebase, codebase, base2
+ use M_strings,only : isalnum, isalpha, iscntrl, isdigit
+ use M_strings,only : isgraph, islower, isprint, ispunct
+ use M_strings,only : isspace, isupper, isascii, isblank, isxdigit
+ use M_strings,only : isnumber
+ use M_strings,only : fortran_name
+ use M_strings,only : describe
+ use M_strings,only : edit_distance
+ use M_strings,only : bundle
+
+split
+subroutine parses string using specified delimiter characters and stores tokens into an array
sep
+function interface to split(3f)
slice
+subroutine parses string using specified delimiter characters and stores beginning and ending positions in arrays
delim
+subroutine parses string using specified delimiter characters and store tokens into an array and records beginning and end
chomp
+function consumes input line as it returns next token in a string using specified delimiters
paragraph
+convert a string into a paragraph
strtok
+tokenize a string like C strtok(3c) routine
split2020
+split a string using prototype of proposed standard procedure
find_field
+token a string
substitute
+subroutine non-recursively globally replaces old substring with new substring
replace
+function non-recursively globally replaces old substring with new substring using allocatable string (version of substitute(3f) without limitation on length of output string)
change
+subroutine non-recursively globally replaces old substring with new substring with a directive like line editor
modif
+subroutine modifies a string with a directive like the XEDIT line editor MODIFY command
transliterate
+replace characters found in set one with characters from set two
reverse
+reverse character order in a string
join
+join an array of CHARACTER variables with specified separator
rotate13
+apply trivial encryption algorithm ROT13 to a string percent_encode apply percent-encryption (aka. URL encryption) to characters
squeeze
+delete adjacent duplicate characters from a string
upper
+function converts string to uppercase
lower
+function converts string to miniscule
upper_quoted
+function converts string to uppercase skipping strings quoted per Fortran rules
len_white
+find location of last non-whitespace character
lenset
+return a string of specified length
pad
+return a string of at least specified length
zpad
+pad integer or string to length with zero characters on left
lpad
+convert scalar intrinsic to a string padded on left to specified length
cpad
+convert scalar intrinsic to a centered string of the specified length
rpad
+convert scalar intrinsic to a string padded on right to specified length
stretch
+return a string of at least specified length with suffix
merge_str
+make strings of equal length and then call MERGE(3f) intrinsic
adjustc
+elemental function centers text within the length of the input string
compact
+left justify string and replace duplicate whitespace with single characters or nothing
nospace
+function replaces whitespace with nothing
indent
+find number of leading spaces
crop
+function trims leading and trailing spaces and control characters
clip
+function trims leading and trailing spaces
See Also: squeeze
+matching_delimiter
+find position of matching delimiter
unquote
+remove quotes from string as if read with list-directed input
quote
+add quotes to string as if written with list-directed output
switch
+switch between a string and an array of single characters
s2c
+convert string to array of single characters and add null terminator for passing to C
c2s
+convert null-terminated array of single characters to string for converting strings returned from C
noesc
+convert non-printable ASCII8 characters to a space
notabs
+convert tabs to spaces while maintaining columns, assuming tabs are set every 8 characters
dilate
+function to convert tabs to spaces assuming tabs are set every 8 characters
expand
+expand escape sequences in a string
visible
+expand escape sequences in a string to "control" and meta-control representations
string_to_value
+generic subroutine returns numeric value (REAL, DOUBLEPRECISION, INTEGER) from string
string_to_values
+subroutine reads an array of numbers from a string
getvals
+subroutine reads a relatively arbitrary number of values from a string using list-directed read
s2v
+function returns DOUBLEPRECISION numeric value from string
s2vs
+function returns a DOUBLEPRECISION array of numbers from a string
s2vs
+function returns a DOUBLEPRECISION array of numbers from a string
atoi
+function returns INTEGER(kind=int32) from a string
atol
+function returns INTEGER(kind=int64) from a string
aton
+changes string to numeric value
msg
+append the values of up to nine values into a string
value_to_string
+generic subroutine returns string given numeric value (REAL, DOUBLEPRECISION, INTEGER, LOGICAL )
v2s
+generic function returns string from numeric value (REAL, DOUBLEPRECISION, INTEGER )
listout
+expand a list of numbers where negative numbers denote range ends (1 -10 means 1 thru 10)
isnumber
+determine if string represents a number
glob
+compares given string for match to pattern which may contain wildcard characters
ends_with
+test whether strings ends with one of the specified suffixes
isalnum returns .true. if character is a letter or digit
isalpha returns .true. if character is a letter and �false. otherwise
iscntrl returns .true. if character is a delete character or ordinary control character
isdigit returns .true. if character is a digit (0,1,```,9) and .false. otherwise
isgraph returns .true. if character is a printable character except a space is considered non-printable
islower returns .true. if character is a miniscule letter (a-z)
isprint returns .true. if character is an ASCII printable character
ispunct returns .true. if character is a printable punctuation character
isspace returns .true. if character is a null, space, tab, carriage return, new line, vertical tab, or formfeed
isupper returns .true. if character is an uppercase letter (A-Z)
isascii returns .true. if the character is in the range char(0) to char(127)
isblank returns .true. if character is a blank character (space or horizontal tab.
isxdigit returns .true. if character is a hexadecimal digit (0-9, a-f, or A-F).
base
+convert whole number string in base [2-36] to string in alternate base [2-36]
base2
+convert INTEGER to a string representing a binary value
codebase
+convert whole number string in base [2-36] to base 10 number decodebase convert whole number in base 10 to string in base [2-36]
bundle
+return up to twenty strings of arbitrary length as an array
describe
+returns a string describing the name of a single character
edit_distance
+returns a naive edit distance using the Levenshtein distance algorithm
longest_common_substring
+function that returns the longest common substring of two strings.
The M_strings(3fm) module supplements and works in combination with the Fortran built-in intrinsics. Stand-alone Fortran lets you access the characters in a string using ranges much like they are character arrays, assignment, comparisons with standard operators, supports dynamically allocatable strings and supports concatenation using the // operator, as well as a number of intrinsic string routines:
+ adjustl Left adjust a string
+ adjustr Right adjust a string
+ index Position of a substring within a string
+ repeat Repeated string concatenation
+ scan Scan a string for the presence of a set
+ of characters
+ trim Remove trailing blank characters of a string
+ verify Scan a string for the absence of a set of
+ characters
+ len It returns the length of a character string
+ achar converts an integer into a character
+ iachar converts a character into an integer
+ len_trim finds length of string with trailing spaces
+ ignored
+ new_line Newline character
+ selected_char_kind Choose character kind
+ lge Lexical greater than or equal
+ lgt Lexical greater than
+ lle Lexical less than or equal
+ llt Lexical less than
+
+The M_strings__oop(3fm) module (included with the M_strings(3fm) module) provides an OOP (Object-Oriented Programming) interface to the M_strings(3fm) module.
+There are additional routines in other GPF modules for working with expressions (M_calculator), time strings (M_time), random strings (M_random, M_uuid), lists (M_list), and interfacing with the C regular expression library (M_regex).
+Each of the procedures includes an [example](example/) program in the corresponding man(1) page for the function.
+John S. Urban
+Public Domain
+M_strings__oop(3f) - [M_strings::INTRO::OOPS] OOP Fortran string module
+use M_strings__oop
+The M_strings(3fm) module is a collection of Fortran procedures that supplement the built-in intrinsic string routines. Routines for parsing, tokenizing, changing case, substituting new strings for substrings, locating strings with simple wildcard expressions, removing tabs and line terminators and other string manipulations are included.
+M_strings__oop(3fm) is a companion module that provides an OOP interface to the M_strings module.
+There are additional routines in other GPF modules for working with expressions (M_calculator), time strings (M_time), random strings (M_random, M_uuid), lists (M_list), and interfacing with the C regular expression library (M_regex).
+Each of the procedural functions in M_strings(3fm) includes an example program in the corresponding man(1) page for the function. The object-oriented interface does not have individual man(1) pages, but is instead demonstrated using the following example program:
+ program demo_M_strings__oop
+ !
+ ! This is an example using the object-oriented class/type model
+ ! defined in M_strings__oop
+ !
+ ! This is essentially the same functionality as the procedures
+ ! combined with several Fortran intrinsics and overloaded operators
+ !
+ use M_strings__oop,only : string, p
+ implicit none
+ TYPE(string) :: str1, str2, str3, str4
+
+ write(*,*)'Call methods of type(STRING)'
+
+ ! define TYPE(STRING) with constructor
+ str2=string(' This is a String! ')
+ str4=string(' a String ')
+
+ write(*,101)'str2%str is ```............. ', &
+ & str2%str ! print string member of type
+ write(*,202)'len ```..................... ', &
+ & str2%len() ! same as intrinsic LEN()
+ write(*,202)'len_trim ```................ ', &
+ & str2%len_trim() ! same as intrinsic LEN_TRIM()
+ write(*,202)'index("is")```.............. ', &
+ & str2%index("is") ! same as intrinsic INDEX()
+ write(*,202)'index("is",back=.T.) ```.... ', &
+ & str2%index("is",back=.TRUE.) ! same as intrinsic INDEX()
+ write(*,101)'upper ```................... ', &
+ & p(str2%upper()) ! call upper()
+ write(*,101)'lower ```................... ', &
+ & p(str2%lower()) ! call lower()
+ write(*,101)'reverse ```................. ', &
+ & p(str2%reverse()) ! call reverse()
+ write(*,101)'adjustl ```................. ', &
+ & p(str2%adjustl()) ! same as intrinsic ADJUSTL()
+ write(*,101)'adjustr ```................. ', &
+ & p(str2%adjustr()) ! same as intrinsic ADJUSTR()
+ write(*,101)'adjustc ```................. ', &
+ & p(str2%adjustc()) ! center string in current string length
+ write(*,101)'adjustc(40) ```............. ', &
+ & p(str2%adjustc(40)) ! center string in string length of NN
+ write(*,101)'lenset(40) ```.............. ', &
+ & p(str2%lenset(40)) ! call pad() to force minimal string length
+ write(*,101)'trim ```.................... ', &
+ & p(str2%trim()) ! same as intrinsic TRIM()
+ write(*,101)'crop ```.................... ', &
+ & p(str2%crop()) ! trim leading and trailing spaces
+ write(*,101)'substitute("This","Here") .. ', &
+ & p(str2%substitute("This","Here")) ! call SUBSTITUTE()
+ write(*,101)'compact ```................. ', &
+ & p(str2%compact()) ! call COMPACT()
+ write(*,101)'compact("") ```............. ', &
+ & p(str2%compact(""))
+ write(*,101)'compact(":") ```............ ', &
+ & p(str2%compact(":"))
+ ! calls M_strings procedure TRANSLITERATE()
+ write(*,101)'transliterate("aei","VWX") . ', &
+ & p(str2%transliterate("aei","VWX"))
+ write(*,101)'transliterate("aeiou"," ") . ', &
+ & p(str2%transliterate("aeiou"," "))
+ write(*,101)'transliterate("aeiou","") .. ', &
+ & p(str2%transliterate("aeiou",""))
+ write(*,101)'transliterate(" aeiou","") . ', &
+ & p(str2%transliterate(" aeiou",""))
+ write(*,404)'chars ```................. . ', &
+ & str4%chars() ! call SWITCH()
+
+ str2%str='\t\tSome tabs\t x\bX '
+ write(*,101)'str2%str ```................ ',str2%str
+ write(*,101)'expand ```.................. ', &
+ & p(str2%expand())
+ str2=str2%expand()
+ write(*,101)'notabs ```.................. ', &
+ & p(str2%notabs()) ! calls NOTABS()
+ write(*,101)'noesc ```................... ', &
+ & p(str2%noesc()) ! calls NOESC()
+
+ write(*,*)repeat('=',68)
+ write(*,*)'Casting to numeric variables'
+ str3=string(' 12.345678901234567e1 ')
+ write(*,101)'str3%str ```................ ',str3%str
+ ! calls to M_strings procedure STRING_TO_VALUE()
+ write(*,*)'int ```.................... ', str3%int()
+ write(*,*)'nint ```.................... ', str3%nint()
+ write(*,*)'real ```.................... ', str3%real()
+ write(*,*)'dble ```.................... ', str3%dble()
+
+ write(*,*)repeat('=',68)
+ write(*,*)'Matching simple globbing patterns'
+ str3=string(' 12.345678901234567e1 ')
+ str3=string('Four score and seven years ago')
+ write(*,101)'str3%str ```................ ',str3%str
+ ! %match calls M_strings procedure GLOB
+ write(*,*)'match("Fo*") ```............ ', str3%match("Fo*")
+ write(*,*)'match("and") ```............ ', str3%match("and")
+ write(*,*)'match("*and*") ```.......... ', str3%match("*and*")
+
+ 101 format(1x,a,"[",a,"]")
+ 202 format(1x,a,i0)
+ 303 format(1x,*(l3))
+ 404 format(1x,a,*("[",a1,"]":))
+
+ write(*,*)repeat('=',68)
+ write(*,*)'OVERLOADED OPERATORS (add and subtract,return TYPE(STRING))'
+ str1%str='123.456'
+ str2%str='AaBbCcDdEeFfGgHhIi AaBbCcDdEeFfGgHhIi'
+ write(*,101)'str1%str ```................ ',str1%str
+ write(*,101)'str2%str ```................ ',str2%str
+ write(*,*)'str1 + str2 ```............. ',p(str1 + str2)
+ ! a string that looks like a numeric value can have a value added
+ write(*,*)'str1 + 20000 ```............ ',p(str1 +20000)
+ write(*,*)'str1 - 20.0 ```............. ',p(str1 -20.0)
+ write(*,*)'str2 - "Aa" (removes ALL) .. ',p(str2 - 'Aa')
+
+ write(*,*)repeat('=',68)
+ write(*,*)'OVERLOADED OPERATORS (multiply,return TYPE(STRING))'
+ str1%str='AaBbCcDdEeFfGgHhIi'
+ write(*,101)'str1%str ```................ ',str1%str
+ write(*,*)'str1 * 2 ```................ ',p(str1 * 2)
+
+ write(*,*)repeat('=',68)
+ write(*,*)'OVERLOADED OPERATORS (//,return TYPE(STRING))'
+ str1%str='String one:'
+ str2%str='String two:'
+ write(*,101)'str1%str ```................ ',str1%str
+ write(*,101)'str2%str ```................ ',str2%str
+ write(*,*)'str1 // str2 ```............. ',p(str1 // str2)
+ ! numeric values are converted to strings
+ write(*,*)'str1 // 20000 ```............ ',p(str1 // 20000)
+ write(*,*)'str1 // 20.0 ```............. ',p(str1 // 20.0)
+
+ write(*,*)repeat('=',68)
+ write(*,*)'OVERLOADED OPERATORS (logical comparisons,return logical)'
+ ! NOTE: comparisons are performed on the character variable members
+ ! of the type(string)
+ str1%str='abcdefghij'
+ str2%str='klmnopqrst'
+ write(*,101)'str1%str ```................ ',str1%str
+ write(*,101)'str2%str ```................ ',str2%str
+ write(*,*)': EQ LT GT LE GE NE'
+ write(*,*)'compare str1 to str1'
+ write(*,303)str1 == str1 ,str1 < str1 ,str1 > str1 ,str1 <= str1 &
+ & ,str1 >= str1 ,str1 /= str1
+ write(*,*)'compare str1 to str2'
+ write(*,303)str1 == str2 ,str1 < str2 ,str1 > str2 ,str1 <= str2 &
+ & ,str1 >= str2 ,str1 /= str2
+ write(*,*)'compare str2 to str1'
+ write(*,303)str2 == str1 ,str2 < str1 ,str2 > str1 ,str2 <= str1 &
+ & ,str2 >= str1 ,str2 /= str1
+
+ write(*,*)repeat('=',68)
+
+ end program demo_M_strings__oop
+
+Expected output
+ exercise the M_STRING_OOP module interface
+ ===================================================================
+ Call methods of type(STRING)
+ ===================================================================
+ str2%str is ```............. [ This is a String! ]
+ len ```..................... 36
+ len_trim ```................ 23
+ index("is")```.............. 6
+ index("is",back=.T.) ```.... 10
+ upper ```................... [ THIS IS A STRING! ]
+ lower ```................... [ this is a string! ]
+ reverse ```................. [ !gnirtS a si sihT ]
+ adjustl ```................. [This is a String! ]
+ adjustr ```................. [ This is a String!]
+ adjustc ```................. [ This is a String! ]
+ adjustc(40) ```............. [ This is a String! ]
+ lenset(40) ```.............. [ This is a String! ]
+ trim ```.................... [ This is a String!]
+ crop ```.................... [This is a String!]
+ substitute("This","Here") .. [ Here is a String! ]
+ compact ```................. [This is a String!]
+ compact("") ```............. [ThisisaString!]
+ compact(":") ```............ [This:is:a:String!]
+ transliterate("aei","VWX") . [ ThXs Xs V StrXng! ]
+ transliterate("aeiou"," ") . [ Th s s Str ng! ]
+ transliterate("aeiou","") .. [ Ths s Strng! ]
+ transliterate(" aeiou","") . [ThssStrng! ]
+ chars ```................. . [ ][a][ ][s][t][r][i][n][g][ ]
+ ===================================================================
+ str2%str ```................ [\t\tSome tabs\t x\bX ]
+ expand ```.................. [ Some tabs x X]
+ notabs ```.................. [ Some tabs x X]
+ noesc ```................... [ Some tabs x X]
+ ===================================================================
+ Casting to numeric variables
+ str3%str ```................ [ 12.345678901234567e1 ]
+ int ```.................... 123
+ real ```.................... 123.456787
+ dble ```.................... 123.45678901234567
+ ===================================================================
+ Matching simple globbing patterns
+ str3%str ```................ [Four score and seven years ago]
+ match("Fo*") ```............ T
+ match("and") ```............ F
+ match("*and*") ```.......... T
+ ====================================================================
+ OVERLOADED OPERATORS (add and subtract, return TYPE(STRING))
+ str1%str ```............... [123.456]
+ str2%str ```............... [AaBbCcDdEeFfGgHhIi AaBbCcDdEeFfGgHhIi]
+ str1 + str2 ```............ 123.456 AaBbCcDdEeFfGgHhIi AaBbCcDdEeFfGgHhIi
+ str1 + 20000 ```........... 20123.455999999998
+ str1 - 20.0 ```............ -103.456
+ str2 - "Aa" (removes ALL) . BbCcDdEeFfGgHhIi BbCcDdEeFfGgHhIi
+ ===================================================================
+ OVERLOADED OPERATORS (multiply, return TYPE(STRING))
+ str1%str ```................ [AaBbCcDdEeFfGgHhIi]
+ str1 * 2 ```................ AaBbCcDdEeFfGgHhIiAaBbCcDdEeFfGgHhIi
+ ===================================================================
+ OVERLOADED OPERATORS (//, return TYPE(STRING))
+ str1%str ```................ [String one:]
+ str2%str ```................ [String two:]
+ str1 // str2 ```............ String one:String two:
+ str1 // 20000 ```........... String one:20000
+ str1 // 20.0 ```............ String one:20.0
+ ===================================================================
+ OVERLOADED OPERATORS (logical comparisons, return logical)
+ str1%str ```................ [abcdefghij]
+ str2%str ```................ [klmnopqrst]
+ : EQ LT GT LE GE NE
+ compare str1 to str1
+ : T F F T T F
+ compare str1 to str2
+ : F T F T F T
+ compare str2 to str1
+ : F F T F T T
+ ===================================================================
+
+John S. Urban
+Public Domain
+nint(3f) - [M_strings:TYPE] overloads NINT(3f) so it can handle character arguments (LICENSE:PD)
+impure elemental function nint(string)
+ character(len=*) :: string
+ integer :: nint
+
+nint(3f) returns an integer when given a numeric representation of a numeric value. This overloads the NINT(3f) intrinsic so that CHARACTER arguments assumed to represent a numeric value may be input.
+Sample program:
+ program demo_nint
+ use,intrinsic :: iso_fortran_env, only : int8, int16, int32, int64
+ use M_strings, only: nint
+ implicit none
+ character(len=*),parameter :: g='(*(g0,1x))'
+ write(*,g)nint('100'),nint('20.4')
+ write(*,g)'intrinsic nint(3f) still works',nint(20.4)
+ write(*,g)'elemental',&
+ & nint([character(len=23) :: '10','20.3','20.5','20.6'])
+ end program demo_nint
+
+Results:
+ > 100 20
+ > intrinsic nint(3f) still works 20
+ > elemental 10 20 21 21
+
+John S. Urban
+Public Domain
+noesc(3f) - [M_strings:NONALPHA] convert non-printable characters to a space (LICENSE:PD)
+elemental function noesc(INSTR)
+ character(len=*),intent(in) :: INSTR
+ character(len=len(instr)) :: noesc
+
+Convert non-printable characters to a space.
+Sample Program:
+ program demo_noesc
+
+ use M_strings, only : noesc
+ implicit none
+ character(len=128) :: ascii
+ character(len=128) :: cleared
+ integer :: i
+ ! fill variable with base ASCII character set
+ do i=1,128
+ ascii(i:i)=char(i-1)
+ enddo
+ cleared=noesc(ascii)
+ write(*,*)'characters and their ADE (ASCII Decimal Equivalent)'
+ call ade(ascii)
+ write(*,*)'Cleared of non-printable characters'
+ call ade(cleared)
+ write(*,*)'Cleared string:'
+ write(*,*)cleared
+ contains
+ subroutine ade(string)
+ implicit none
+ ! the string to print
+ character(len=*),intent(in) :: string
+ ! number of characters in string to print
+ integer :: lgth
+ ! counter used to step thru string
+ integer :: i
+ ! get trimmed length of input string
+ lgth=len_trim(string(:len(string)))
+
+ ! replace lower unprintable characters with spaces
+ write(*,101)(merge(string(i:i),' ',&
+ & iachar(string(i:i)) >= 32 &
+ & .and. &
+ & iachar(string(i:i)) <= 126) &
+ & ,i=1,lgth)
+
+ ! print ADE value of character underneath it
+ write(*,202) (iachar(string(i:i))/100, i=1,lgth)
+ write(*,202)(mod( iachar(string(i:i)),100)/10,i=1,lgth)
+ write(*,202)(mod((iachar(string(i:i))),10), i=1,lgth)
+ ! format for printing string characters
+ 101 format(*(a1:))
+ ! format for printing ADE values
+ 202 format(*(i1:))
+ end subroutine ade
+ end program demo_noesc
+
+ Expected output
+
+ The string is printed with the ADE value vertically beneath.
+ The original string has all the ADEs from 000 to 127. After
+ NOESC(3f) is called on the string all the "non-printable"
+ characters are replaced with a space (ADE of 032).
+
+characters and their ADE (ASCII Decimal Equivalent)
+ > !"#$%&'()*+,-./0123456789
+ :;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+ >0000000000000000000000000000000000000000000000000000000000
+ 0000000000000000000000000000000000000000001111111111111111111111111111
+ >00000000001111111111222222222233333333334444444444555555555566666666
+ 667777777777888888888899999999990000000000111111111122222222
+ >012345678901234567890123456789012345678901234567890123456789012345678
+ 90123456789012345678901234567890123456789012345678901234567
+
+Cleared of non-printable characters
+ > !"#$%&'()*+,-./0123456789
+ :;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+ >0000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000111111111111111111111111111
+ >3333333333333333333333333333333333333333444444444455555555
+ 556666666666777777777788888888889999999999000000000011111111112222222
+ >2222222222222222222222222222222223456789012345678901234567
+ 890123456789012345678901234567890123456789012345678901234567890123456
+
+Cleared string:
+ > !"#$%&'()*+,-./0123456789:;<=>?@
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+
+John S. Urban
+Public Domain
+nospace(3f) - [M_strings:WHITESPACE] remove all whitespace from input string (LICENSE:PD)
+function nospace(str) - remove all whitespace from input string
+ character(len=*),intent(in) :: str
+ character(len=:),allocatable :: nospace
+
+nospace(3f) removes space, tab, carriage return, new line, vertical tab, formfeed and null characters (called "whitespace"). The output is returned trimmed.
+Sample program:
+ program demo_nospace
+ use M_strings, only: nospace
+ implicit none
+ character(len=:),allocatable :: s
+ s=' This is a test '
+ write(*,*) 'original input string is ```.',s
+ write(*,*) 'processed output string is ```',nospace(s)
+ if(nospace(s) == 'Thisisatest')then
+ write(*,*)'nospace test passed'
+ else
+ write(*,*)'nospace test error'
+ endif
+ end program demo_nospace
+
+Expected output
+ original input string is ```. This is a test
+ processed output string is ```Thisisatest
+ nospace test passed
+
+John S. Urban
+Public Domain
+notabs(3f) - [M_strings:NONALPHA] expand tab characters (LICENSE:PD)
+subroutine notabs(INSTR,OUTSTR,lgth)
+ character(len=*),intent=(in) :: INSTR
+ character(len=*),intent=(out) :: OUTSTR
+ integer,intent=(out) :: lgth
+
+NOTABS() converts tabs in INSTR to spaces in OUTSTR while maintaining columns. It assumes a tab is set every 8 characters. Trailing spaces are removed.
+In addition, trailing carriage returns and line feeds are removed (they are usually a problem created by going to and from MSWindows).
+What are some reasons for removing tab characters from an input line? Some Fortran compilers have problems with tabs, as tabs are not part of the Fortran character set. Some editors and printers will have problems with tabs. It is often useful to expand tabs in input files to simplify further processing such as tokenizing an input line.
+outstr
+Output string with tabs expanded. Assumed to be of sufficient length
lgth
+Significant length of returned string
Sample program:
+ program demo_notabs
+
+ ! test filter to remove tabs and trailing white space from input
+ ! on files up to 1024 characters wide
+ use M_strings, only : notabs
+ character(len=1024) :: in,out
+ integer :: ios,iout
+ do
+ read(*,'(A)',iostat=ios)in
+ if(ios /= 0) exit
+ call notabs(in,out,iout)
+ write(*,'(a)')out(:iout)
+ enddo
+ end program demo_notabs
+
+GNU/Unix commands expand(1) and unexpand(1)
+John S. Urban
+Public Domain
+pad(3f) - [M_strings:LENGTH] return string padded to at least specified length (LICENSE:PD)
+function pad(str,length,pattern,right,clip) result(strout)
+ character(len=*) :: str
+ integer,intent(in) :: length
+ character(len=max(length,len(trim(line)))) :: strout
+ character(len=*),intent(in),optional :: pattern
+ logical,intent(in),optional :: right
+ logical,intent(in),optional :: clip
+
+pad(3f) pads a string with a pattern to at least the specified length. If the trimmed input string is longer than the requested length the trimmed string is returned.
+str
+the input string to return trimmed, but then padded to the specified length if shorter than length
length
+The minimum string length to return
pattern
+optional string to use as padding. Defaults to a space.
right
+if true pads string on the right, else on the left
clip
+trim spaces from input string but otherwise retain length. Except for simple cases you typically would trim the input yourself.
Sample Program:
+ program demo_pad
+ use M_strings, only : pad
+ implicit none
+ character(len=10) :: string='abcdefghij'
+ character(len=:),allocatable :: answer
+ integer :: i
+ character(len=*),parameter :: g='(*(g0))'
+ answer=pad(string,5)
+ write(*,'("[",a,"]")') answer
+ answer=pad(string,20)
+ write(*,'("[",a,"]")') answer
+ i=30
+ write(*,g)
+ write(*,'(1x,a,1x,i0)') &
+ & pad('CHAPTER 1 : The beginning ',i,'.'), 1 , &
+ & pad('CHAPTER 2 : The end ',i,'.'), 1234, &
+ & pad('APPENDIX ',i,'.'), 1235
+ write(*,*)
+ write(*,'(1x,a,i7)') &
+ & pad('CHAPTER 1 : The beginning ',i,'.'), 1 , &
+ & pad('CHAPTER 2 : The end ',i,'.'), 1234, &
+ & pad('APPENDIX ',i,'.'), 1235
+
+ write(*,g)pad('12',5,'0',right=.false.)
+
+ write(*,g)pad('12345 ',30,'_',right=.false.)
+ write(*,g)pad('12345 ',30,'_',right=.false.,clip=.true.)
+ write(*,g)pad('12345 ',7,'_',right=.false.)
+ write(*,g)pad('12345 ',7,'_',right=.false.,clip=.true.)
+ write(*,g)pad('12345 ',6,'_',right=.false.)
+ write(*,g)pad('12345 ',6,'_',right=.false.,clip=.true.)
+ write(*,g)pad('12345 ',5,'_',right=.false.)
+ write(*,g)pad('12345 ',5,'_',right=.false.,clip=.true.)
+ write(*,g)pad('12345 ',4,'_',right=.false.)
+ write(*,g)pad('12345 ',4,'_',right=.false.,clip=.true.)
+ end program demo_pad
+
+Results:
+> [abcdefghij]
+> [abcdefghij
+>
>
+CHAPTER 1 : The beginning ```. 1
>
+CHAPTER 2 : The end ```....... 1234
>
+APPENDIX ```.................. 1235 >
>
+CHAPTER 1 : The beginning ```. 1
>
+CHAPTER 2 : The end ```....... 1234
>
+APPENDIX ```.................. 1235 > 00012 > ________________________12345 > _________________________12345 > _12345 > __12345 > 12345 > _12345 > 12345 > 12345 > 12345 > 12345
adjustl(3f), adjustr(3f), repeat(3f), trim(3f), len_trim(3f), len(3f)
+adjustc(3f), stretch(3f), lpad(3f), rpad(3f), cpad(3f), zpad(3f), lenset(3f)
+John S. Urban
+Public Domain
+paragraph(3f) - [M_strings:TOKENS] break a long line into a paragraph (LICENSE:PD)
+function paragraph(source_string,length)
+ character(len=*),intent(in) :: source_string
+ integer,intent(in) :: length
+ character(allocatable(len=length) :: paragraph(:)
+
+paragraph(3f) breaks a long line into a simple paragraph of specified line length.
+Given a long string break it on spaces into an array such that no variable is longer than the specified length. Individual words longer than LENGTH will be placed in variables by themselves.
+SOURCE_STRING
+input string to break into an array of shorter strings on blank delimiters
LENGTH
+length of lines to break the string into.
sample program
+ program demo_paragraph
+ use M_strings, only : paragraph
+ implicit none
+ character(len=:),allocatable :: paragrph(:)
+ character(len=*),parameter :: string= '&
+ &one two three four five &
+ &six seven eight &
+ &nine ten eleven twelve &
+ &thirteen fourteen fifteen sixteen &
+ &seventeen'
+
+ write(*,*)'LEN=',len(string)
+ write(*,*)'INPUT:'
+ write(*,*)string
+
+ paragrph=paragraph(string,40)
+ write(*,*)'LEN=',len(paragrph),' SIZE=',size(paragrph)
+ write(*,*)'OUTPUT:'
+ write(*,'(a)')paragrph
+
+ write(*,'(a)')paragraph(string,0)
+ write(*,'(3x,a)')paragraph(string,47)
+
+ end program demo_paragraph
+
+Results:
+ LEN= 106
+ INPUT:
+ one two three four five six seven eight nine ten eleven twelve
+ thirteen fourteen fifteen sixteen seventeen
+ LEN= 40 SIZE= 3
+ OUTPUT:
+
+one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen
+John S. Urban
+Public Domain
+percent_encode(3f) - [M_strings:ENCODE] percent-encode strings and character arrays (LICENSE:PD)
+function percent_encode(text)
+ character(len=1),intent(in) :: text(:)
+ character(len=;),allocatable :: percent_encode
+
+or
+function percent_encode(text)
+ character(len=*),intent(in) :: text
+ character(len=;),allocatable :: percent_encode
+
+This function percent-encodes ASCII strings or ASCII character arrays. "Reserved" characters are encoded.
+URI containing spaces or most other non-alphanumeric characters must be encoded using percent encoding (aka. URL encoding).
+The characters allowed in a URI are either reserved or unreserved (or a percent character as part of a percent-encoding). Reserved characters are those characters that sometimes have special meaning, while unreserved characters have no such meaning. Using percent-encoding, characters which otherwise would not be allowed are represented using allowed characters. The sets of reserved and unreserved characters and the circumstances under which certain reserved characters have special meaning have changed slightly with each revision of specifications that govern URIs and URI schemes.
+According to RFC 3986, the characters in a URL have to be taken from a defined set of unreserved and reserved ASCII characters. Any other characters are not allowed in a URL.
+The unreserved characters can be encoded, but should not be. The unreserved characters are:
+ > ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ > abcdefghijklmnopqrstuvwxyz
+ > 0123456789-_.~
+
+The reserved characters have to be encoded only under certain circumstances. The reserved characters are:
+ > * ' ( ) ; : @ & = + $ , / ? % # [ ]
+
+Sample program:
+ program demo_percent_encode
+ use M_strings, only : percent_encode
+ use, intrinsic :: iso_fortran_env, only : stdout=>output_unit
+ implicit none
+ write(*,*)percent_encode('[this is a string]')
+ end program demo_percent_encode
+
+Results:
+ > %5Bthis%20is%20a%20string%5D
+
+John S. Urban
+quote(3f) - [M_strings:QUOTES] add quotes to string as if written with list-directed output (LICENSE:PD)
+function quote(str,mode,clip) result (quoted_str)
+ character(len=*),intent(in) :: str
+ character(len=*),optional,intent(in) :: mode
+ logical,optional,intent(in) :: clip
+ character(len=:),allocatable :: quoted_str
+
+Add quotes to a CHARACTER variable as if it was written using list-directed output. This is particularly useful for processing strings to add to CSV files.
+str
+input string to add quotes to, using the rules of list-directed output (single quotes are replaced by two adjacent quotes)
mode
+alternate quoting methods are supported:
DOUBLE default. replace quote with double quotes
+ ESCAPE replace quotes with backslash-quote instead of
+ double quotes
+
+Sample program:
+ program demo_quote
+ use M_strings, only : quote
+ implicit none
+ integer :: i
+ character(len=*),parameter :: f='(*(g0))'
+ character(len=:),allocatable :: str
+ character(len=80),parameter :: data(3)=[character(len=80)::&
+ 'test string',&
+ 'quote="',&
+ '"word1" "word2"']
+ do i=1,size(data)
+ ! the original string
+ write(*,'(a)')'ORIGINAL '//trim(data(i))
+
+ ! the string processed by quote(3f)
+ str=quote(data(i))
+ write(*,'(a)')'QUOTED '//str
+
+ ! write the string list-directed to compare the results
+ write(*,f,advance='no') 'LIST DIRECTED'
+ ! default is often NONE or APOSTROPHE
+ write(*,*,delim='quote') trim(data(i))
+ enddo
+ end program demo_quote
+
+Results:
+ > ORIGINAL test string
+ > QUOTED "test string"
+ > LIST DIRECTED "test string"
+ > ORIGINAL quote="
+ > QUOTED "quote="""
+ > LIST DIRECTED "quote="""
+ > ORIGINAL "word1" "word2"
+ > QUOTED """word1"" ""word2"""
+ > LIST DIRECTED """word1"" ""word2"""
+
+John S. Urban
+Public Domain
+real(3f) - [M_strings:TYPE] overloads REAL(3f) so it can handle character arguments (LICENSE:PD)
+impure elemental function real(string)
+ character(len=*) :: string
+ integer :: real
+
+real(3f) returns a REAL value when given a numeric representation of a numeric value. This overloads the REAL(3f) intrinsic so that CHARACTER arguments assumed to represent a numeric value may be input.
+Sample program:
+ program demo_real
+ use M_strings, only: real
+ implicit none
+ write(*,*)real('100'),real('20.4')
+ write(*,*)'real still works',real(20)
+ write(*,*)'elemental',&
+ & real([character(len=23) :: '10','20.3','20.5','20.6'])
+ end program demo_real
+
+Results:
+ > 100.000000 20.3999996
+ > real still works 20.0000000
+ > elemental 10.0000000 20.2999992 20.5000000 20.6000004
+
+John S. Urban
+Public Domain
+replace(3f) - [M_strings:EDITING] function replaces one substring for another in string (LICENSE:PD)
+syntax:
+ function replace(targetline,old,new,cmd,&
+ & occurrence, &
+ & repeat, &
+ & ignorecase, &
+ & ierr) result (newline)
+ character(len=*) :: targetline
+ character(len=*),intent(in),optional :: old
+ character(len=*),intent(in),optional :: new
+ character(len=*),intent(in),optional :: cmd
+ integer,intent(in),optional :: occurrence
+ integer,intent(in),optional :: repeat
+ logical,intent(in),optional :: ignorecase
+ integer,intent(out),optional :: ierr
+ character(len=:),allocatable :: newline
+
+Replace one substring for another in string. Either CMD or OLD and NEW must be specified.
+targetline
+input line to be changed
old
+old substring to replace
new
+new substring
cmd
+alternate way to specify old and new string, in the form c/old/new/; where "/" can be any character not in "old" or "new".
occurrence
+if present, start changing at the Nth occurrence of the OLD string. If negative start replacing from the left end of the string.
repeat
+number of replacements to perform. Defaults to a global replacement.
ignorecase
+whether to ignore ASCII case or not. Defaults to .false. .
newline
+allocatable string returned
ierr
+error code. iF ier = -1 bad directive, >= 0 then count of changes made.
Sample Program:
+ program demo_replace
+ use M_strings, only : replace
+ implicit none
+ character(len=:),allocatable :: line
+
+ write(*,*)replace('Xis is Xe string','X','th')
+ write(*,*)replace('Xis is xe string','x','th',ignorecase=.true.)
+ write(*,*)replace('Xis is xe string','X','th',ignorecase=.false.)
+
+ ! a null old substring means "at beginning of line"
+ write(*,*) replace('my line of text','','BEFORE:')
+
+ ! a null new string deletes occurrences of the old substring
+ write(*,*) replace('I wonder i ii iii','i','')
+
+ ! Examples of the use of RANGE
+
+ line=replace('aaaaaaaaa','a','A',occurrence=1,repeat=1)
+ write(*,*)'replace first a with A ['//line//']'
+
+ line=replace('aaaaaaaaa','a','A',occurrence=3,repeat=3)
+ write(*,*)'replace a with A for 3rd to 5th occurrence ['//line//']'
+
+ line=replace('ababababa','a','',occurrence=3,repeat=3)
+ write(*,*)'replace a with null instances 3 to 5 ['//line//']'
+
+ line=replace( &
+ & 'a b ab baaa aaaa aa aa a a a aa aaaaaa',&
+ & 'aa','CCCC',occurrence=-1,repeat=1)
+ write(*,*)'replace lastaa with CCCC ['//line//']'
+
+ write(*,*)replace('myf90stuff.f90.f90','f90','for',occurrence=-1,repeat=1)
+ write(*,*)replace('myf90stuff.f90.f90','f90','for',occurrence=-2,repeat=2)
+
+ end program demo_replace
+
+Results:
+ this is the string
+ this is the string
+ this is xe string
+ BEFORE:my line of text
+ I wonder
+ replace first a with A [Aaaaaaaaa]
+ replace a with A for 3rd to 5th occurrence [aaAAAaaaa]
+ replace a with null instances 3 to 5 [ababbb]
+ replace lastaa with CCCC [a b ab baaa aaaa aa aa a a a aa aaaaCCCC]
+ myf90stuff.f90.for
+ myforstuff.for.f90
+
+John S. Urban
+Public Domain
+reverse(3f) - [M_strings:EDITING] Return a string reversed (LICENSE:PD)
+elemental pure function reverse(str) result (string)
+ character(*), intent(in) :: str
+ character(len(str)) :: string
+
+reverse(string) returns a copy of the input string with all characters reversed from right to left.
+Sample program:
+ program demo_reverse
+ use M_strings, only: reverse
+ implicit none
+ character(len=:),allocatable :: s
+ write(*,*)'REVERSE STRINGS:',reverse('Madam, I''m Adam')
+ s='abcdefghijklmnopqrstuvwxyz'
+ write(*,*) 'original input string is ```.',s
+ write(*,*) 'reversed output string is ```',reverse(s)
+ end program demo_reverse
+
+Results:
+ > REVERSE STRINGS:madA m'I ,madaM
+ > original input string is ```.abcdefghijklmnopqrstuvwxyz
+ > reversed output string is ```zyxwvutsrqponmlkjihgfedcba
+
+John S. Urban
+Public Domain
+rotate13(3f) - [M_strings:ENCODE] apply trivial ROT13 encryption to a string (LICENSE:PD)
+rotate13(input) result(output)
+ character(len=*),intent(in) :: input
+ character(len=len(input)) :: output
+
+ROT13 ("rotate by 13 places", sometimes hyphenated ROT-13) is a simple letter substitution cipher that replaces a letter with the 13th letter after it in the alphabet; wrapping around if necessary.
+The transformation can be done using a lookup table, such as the following:
+ Input ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ Output NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm
+
+ROT13 is used in online forums as a means of hiding spoilers, punchlines, puzzle solutions, and offensive materials from the casual glance. ROT13 has inspired a variety of letter and word games on-line, and is frequently mentioned in newsgroup conversations.
+The algorithm provides virtually no cryptographic security, and is often cited as a canonical example of weak encryption.
+ROT13 is a special case of the Caesar cipher which was developed in ancient Rome.
+Applying ROT13 to a piece of text merely requires examining its alphabetic characters and replacing each one by the letter 13 places further along in the alphabet, wrapping back to the beginning if necessary. A becomes N, B becomes O, and so on up to M, which becomes Z, then the sequence continues at the beginning of the alphabet: N becomes A, O becomes B, and so on to Z, which becomes M. Only those letters which occur in the English alphabet are affected; numbers, symbols, whitespace, and all other characters are left unchanged.
+Because there are 26 letters in the English alphabet and 26 = 2 x 13, the ROT13 function is its own inverse: so the same action can be used for encoding and decoding. In other words, two successive applications of ROT13 restore the original text (in mathematics, this is sometimes called an involution; in cryptography, a reciprocal cipher).
+The use of a constant shift means that the encryption effectively has no key, and decryption requires no more knowledge than the fact that ROT13 is in use. Even without this knowledge, the algorithm is easily broken through frequency analysis.
+In encrypted normal English-language text of any significant size, ROT13 is recognizable from some letter/word patterns. The words "n", "V" (capitalized only), and "gur" (ROT13 for "a", "I", and "the"), and words ending in "yl" ("ly") are examples.
+Wikipedia, the free encyclopedia
+Sample program
+ program demo_rotate13
+ use M_strings, only : rotate13
+ implicit none
+ character(len=256) :: line
+ integer :: ios
+ do
+ read(*,'(a)',iostat=ios)line
+ if(ios /= 0)exit
+ write(*,'(a)')rotate13(line)
+ enddo
+ end program demo_rotate13
+
+Sample usage:
+ demo_rotate13
+ United we stand, divided we fall.
+ Havgrq jr fgnaq, qvivqrq jr snyy.
+
+John S. Urban
+Public Domain
+rpad(3f) - [M_strings:LENGTH] convert to a string and pad on the right to requested length (LICENSE:PD)
+function rpad(valuein,length) result(strout)
+ class*,intent(in) :: valuein(..)
+ integer,intent(in) :: length
+
+rpad(3f) converts a scalar intrinsic value to a string and then pads it on the right with spaces to at least the specified length. If the trimmed input string is longer than the requested length the string is returned trimmed of leading and trailing spaces.
+str
+The input may be scalar or a vector. the input value to return as a string, padded on the left to the specified length if shorter than length. The input may be any intrinsic scalar which is converted to a cropped string much as if written with list-directed output.
length
+The minimum string length to return
Sample Program:
+ program demo_rpad
+ use M_strings, only : rpad
+ implicit none
+ write(*,'("[",a,"]")') rpad( 'my string', 20)
+ write(*,'("[",a,"]")') rpad( 'my string ', 20)
+ write(*,'("[",a,"]")') rpad( ' my string', 20)
+ write(*,'("[",a,"]")') rpad( ' my string ', 20)
+ write(*,'("[",a,"]")') rpad( valuein=42 , length=7)
+ write(*,'("[",a,"]")') rpad( valuein=1.0/9.0 , length=20)
+ end program demo_rpad
+
+Results:
+ > [my string ]
+ > [my string ]
+ > [my string ]
+ > [my string ]
+ > [42 ]
+ > [0.111111112 ]
+
+John S. Urban
+Public Domain
+s2c(3f) - [M_strings:ARRAY] convert character variable to array of characters with last element set to null (LICENSE:PD)
+function s2c(string)
+ character(len=*),intent=(in) :: string
+ character(len=1),allocatable :: s2c(:)
+
+Given a character variable convert it to an array of single-character character variables with the last element set to a null character. This is generally used to pass character variables to C procedures.
+character(len=3),allocatable :: array(:)
+integer
+:: i ! put one character into each 3-character element of array array = [(string(i:i),i=1,len(string))] ! write array with ASCII Decimal Equivalent below it except show ! unprintable characters like NULL as "XXX" write(*,g) merge('XXX',array,iachar(array(:)(1:1)) < 32) write(*,g) iachar(array(:)(1:1))
Sample Program:
+ program demo_s2c
+ use M_strings, only : s2c
+ implicit none
+ character(len=*),parameter :: string="single string"
+ character(len=*),parameter :: g= '(1x,*("[",g3.3,"]":))'
+ character(len=3),allocatable :: array(:)
+ write(*,*)'INPUT STRING ',trim(string)
+ ! put one character into each 3-character element of array
+ array=s2c(string)
+ ! write array with ASCII Decimal Equivalent below it except show
+ ! unprintable characters like NULL as "XXX"
+ write(*,g) merge('XXX',array,iachar(array(:)(1:1)) < 32)
+ write(*,g) iachar(array(:)(1:1))
+ end program demo_s2c
+
+Expected output:
+ INPUT STRING single string
+ [s ][i ][n ][g ][l ][e ][ ][s ][t ][r ][i ][n ][g ][XXX]
+ [115][105][110][103][108][101][ 32][115][116][114][105][110][103][ 0]
+
John S. Urban
+Public Domain
+s2v(3f) - [M_strings:TYPE] function returns doubleprecision numeric value from a string (LICENSE:PD)
+function s2v(string[,ierr][,onerr])
+ character(len=*) :: string
+ doubleprecision :: s2v
+ integer,intent(out),optional :: ierr
+ class(*),intent(in),optional :: onerr
+
+This function converts a string to a DOUBLEPRECISION numeric value.
+The intrinsics INT(3f), REAL(3f), and DBLE(3f) are also extended to take CHARACTER variables. The KIND= keyword is not supported on the extensions.
+string
+holds string assumed to represent a numeric value
ierr
+If an error occurs the program is stopped if the optional parameter IERR is not present. If IERR returns a non-zero value an error occurred.
onerr
+The value to return on error. A value of NaN is returned on error by default.
Sample Program:
+ program demo_s2v
+
+ use M_strings, only: s2v, int, real, dble
+ implicit none
+ character(len=8) :: s=' 10.345 '
+ integer :: i
+ character(len=14),allocatable :: strings(:)
+ doubleprecision :: dv
+ integer :: errnum
+
+ ! different strings representing INTEGER, REAL, and DOUBLEPRECISION
+ strings=[&
+ &' 10.345 ',&
+ &'+10 ',&
+ &' -3 ',&
+ &' -4.94e-2 ',&
+ &'0.1 ',&
+ &'12345.678910d0',&
+ &' ',& ! Note: will return zero without an error message
+ &'1 2 1 2 1 . 0 ',& ! Note: spaces will be ignored
+ &'WHAT? '] ! Note: error messages will appear, zero returned
+
+ ! a numeric value is returned,
+ ! so it can be used in numeric expression
+ write(*,*) '1/2 value of string is ',s2v(s)/2.0d0
+ write(*,*)
+ write(*,*)' STRING VALUE ERROR_NUMBER'
+ do i=1,size(strings)
+ ! Note: not a good idea to use s2v(3f) in a WRITE(3f) statement,
+ ! as it does I/O when errors occur, so called on a separate line
+ dv=s2v(strings(i),errnum)
+ write(*,*) strings(i)//'=',dv,errnum
+ enddo
+ write(*,*)"Extended intrinsics"
+ write(*,*)'given inputs:',s,strings(:8)
+ write(*,*)'INT(3f):',int(s),int(strings(:8))
+ write(*,*)'REAL(3f):',real(s),real(strings(:8))
+ write(*,*)'DBLE(3f):',dble(s),dble(strings(:8))
+ write(*,*)"That's all folks!"
+
+ end program demo_s2v
+
+ Expected output
+
+ >1/2 value of string is 5.1725000000000003
+ >
+ > STRING VALUE ERROR_NUMBER
+ > 10.345 = 10.345000000000001 0
+ >+10 = 10.000000000000000 0
+ > -3 = -3.0000000000000000 0
+ > -4.94e-2 = -4.9399999999999999E-002 0
+ >0.1 = 0.10000000000000001 0
+ >12345.678910d0= 12345.678910000001 0
+ > = 0.0000000000000000 0
+ >1 2 1 2 1 . 0 = 12121.000000000000 0
+ >*a2d* - cannot produce number from string [WHAT?]
+ >*a2d* - [Bad value during floating point read]
+ >WHAT? = 0.0000000000000000 5010
+ >Extended intrinsics
+ >given inputs: 10.345 10.345 +10 -3 -4.94e-2 0.1
+ 12345.678910d0 1 2 1 2 1 . 0
+ >INT(3f): 10 10 10 -3 0 0 12345 0 12121
+ >REAL(3f): 10.3450003 10.3450003 10.0000000 -3.00000000
+ -4.94000018E-02
+ > 0.100000001 12345.6787 0.00000000 12121.0000
+ >DBLE(3f): 10.345000000000001 10.345000000000001
+ 10.000000000000000
+ > -3.0000000000000000 -4.9399999999999999E-002
+ 0.10000000000000001
+ > 12345.678910000001 0.0000000000000000
+ 12121.000000000000
+ >That's all folks!
+
+John S. Urban
+Public Domain
+s2vs(3f) - [M_strings:TYPE] given a string representing numbers return a numeric array (LICENSE:PD)
+function s2vs(line[,delim])
+ character(len=*) :: line
+ doubleprecision,allocatable :: s2vs(:)
+
+The function S2VS(3f) takes a string representing a series of numbers and converts it to a numeric doubleprecision array. The string values may be delimited by spaces, semi-colons, and commas by default.
+LINE
+Input string containing numbers
DELIM
+optional list of delimiter characters. If a space is included, it should appear as the left-most character in the list. The default is " ;," (spaces, semi-colons, and commas).
Sample Program:
+ program demo_s2vs
+ use M_strings, only : s2vs
+ implicit none
+ character(len=80) :: s=' 10 20e3;3.45 -400.3e-2;1234; 5678 '
+ real,allocatable :: values(:)
+ integer,allocatable :: ivalues(:)
+ integer :: ii
+
+ values=s2vs(s)
+ ivalues=int(s2vs(s))
+ call reportit()
+
+ contains
+ subroutine reportit()
+ write(*,*)'S2VS:'
+ write(*,*)'input string```..........',&
+ & trim(s)
+ write(*,*)'number of values found```',&
+ & size(values)
+ write(*,*)'values```................',&
+ & (values(ii),ii=1,size(values))
+ write(*,'(*(g0,1x))')'ivalues```...............',&
+ & (ivalues(ii),ii=1,size(values))
+ end subroutine reportit
+ end program demo_s2vs
+
+Expected output
+ S2VS:
+ input string```.......... 10 20e3;3.45 -400.3e-2;1234; 5678
+ number of values found``` 6
+ values```................ 10.0000000 20000.0000 3.45000005
+ -4.00299978 1234.00000 5678.00000
+
+ivalues```............... 10 20000 3 -4 1234 5678
+John S. Urban
+Public Domain
+sep(3f) - [M_strings:TOKENS] function to parse string into an array using specified delimiters (LICENSE:PD)
+function sep(input_line,delimiters,nulls)
+ character(len=*),intent(in) :: input_line
+ character(len=*),optional,intent(in) :: delimiters
+ character(len=*),optional,intent(in) :: nulls
+ character(len=:),allocatable :: sep(:)
+
+sep(3f) parses a string using specified delimiter characters and store tokens into an allocatable array
+INPUT_LINE
+Input string to tokenize
DELIMITERS
+List of delimiter characters. The default delimiters are the "whitespace" characters (space, tab,new line, vertical tab, formfeed, carriage return, and null). You may specify an alternate set of delimiter characters.
Multi-character delimiters are not supported (Each character in the DELIMITERS list is considered to be a delimiter).
+Quoting of delimiter characters is not supported.
NULLS=IGNORE|RETURN|IGNOREEND
+Treatment of null fields. By default adjacent delimiters in the input string do not create an empty string in the output array. if NULLS='return' adjacent delimiters create an empty element in the output ARRAY. If NULLS='ignoreend' then only trailing delimiters at the right of the string are ignored.
ORDER='ASCENDING'|'DESCENDING'
+by default the tokens are returned from last to first; order='ASCENDING' returns them from first to last (left to right).
Sample program:
+ program demo_sep
+ use M_strings, only: sep
+ character(len=*),parameter :: fo='(/,a,*(/,"[",g0,"]":,","))'
+ character(len=*),parameter :: line=&
+ ' aBcdef ghijklmnop qrstuvwxyz 1:|:2 333|333 a B cc '
+ write(*,'(a)') 'INPUT LINE:['//LINE//']'
+ write(*,fo) 'typical call:',sep(line)
+ write(*,fo) 'delimiters ":|":',sep(line,':|')
+ write(*,fo) 'count null fields ":|":',sep(line,':|','return')
+ end program demo_sep
+
+Output
+ INPUT LINE:[ aBcdef ghijklmnop qrstuvwxyz 1:|:2 333|333 a B cc ]
+
+ typical call:
+ [cc ],
+ [B ],
+ [a ],
+ [333|333 ],
+ [1:|:2 ],
+ [qrstuvwxyz],
+ [ghijklmnop],
+ [aBcdef ]
+
+ delimiters ":|":
+ [333 a B cc ],
+ [2 333 ],
+ [ aBcdef ghijklmnop qrstuvwxyz 1]
+
+ count null fields ":|":
+ [333 a B cc ],
+ [2 333 ],
+ [ ],
+ [ ],
+ [ aBcdef ghijklmnop qrstuvwxyz 1]
+
+John S. Urban
+Public Domain
+slice(3f) - [M_strings:TOKENS] parse string into an array using specified delimiters (LICENSE:PD)
+subroutine slice(input_line,ibegin,iend,delimiters,nulls)
+ character(len=*),intent(in) :: input_line
+ integer,allocatable,intent(out) :: ibegin(:),iend(:)
+ character(len=*),optional,intent(in) :: delimiters
+ character(len=*),optional,intent(in) :: nulls
+
+slice(3f) parses a string using specified delimiter characters and store token beginning and ending positions into allocatable arrays
+INPUT_LINE
+Input string to tokenize
IBEGIN,IEND
+arrays containing start and end positions of tokens. IEND(I)<IBEGIN(I) designates a null token.
DELIMITERS
+List of delimiter characters. The default delimiters are the "whitespace" characters (space, tab,new line, vertical tab, formfeed, carriage return, and null). You may specify an alternate set of delimiter characters.
Multi-character delimiters are not supported (Each character in the DELIMITERS list is considered to be a delimiter).
+Quoting of delimiter characters is not supported.
NULLS="IGNORE"|"RETURN"|"IGNOREEND"
+Treatment of null fields. By default adjacent delimiters in the input string do not create an empty string in the output array. if NULLS='return' adjacent delimiters create an empty element in the output ARRAY. If NULLS='ignoreend' then only trailing delimiters at the right of the string are ignored.
Sample program:
+ program demo_slice
+ use M_strings, only: slice
+ implicit none
+ integer :: i
+ character(len=*),parameter :: &
+ & line=' aBcdef ghijklmnop qrstuvwxyz 1:|:2 333|333 a B cc '
+ integer,allocatable :: ibegin(:), iend(:) ! output arrays of positions
+ character(len=*),parameter :: title='(80("="),t1,a)'
+ write(*,*)'INPUT LINE:['//line//']'
+ !
+ write(*,title)'typical call: '
+ call slice(line,ibegin,iend)
+ call printme()
+ !
+ write(*,title)'custom list of delimiters=":|" : '
+ call slice(line,ibegin,iend,delimiters=':|',nulls='ignore')
+ call printme()
+ !
+ write(*,title)'delimiters=":|", and count null fields: '
+ call slice(line,ibegin,iend,delimiters=':|',nulls='return')
+ call printme()
+ !
+ write(*,title)'default delimiters and return null fields: '
+ call slice(line,ibegin,iend,delimiters='',nulls='return')
+ call printme()
+ contains
+ subroutine printme()
+ write(*,'((*(:/,3x,"[",g0,"]")))')&
+ & (line(ibegin(i):iend(i)),i=1,size(ibegin))
+ write(*,'(*(g0,1x))')'SIZE:',size(ibegin)
+ end subroutine printme
+ end program demo_slice
+
+Results:
+ > INPUT LINE:
+ > [ aBcdef ghijklmnop qrstuvwxyz 1:|:2 333|333 a B cc ]
+ > typical call: ========================================================
+ >
+ > [aBcdef]
+ > [ghijklmnop]
+ > [qrstuvwxyz]
+ > [1:|:2]
+ > [333|333]
+ > [a]
+ > [B]
+ > [cc]
+ > SIZE: 8
+ > custom list of delimiters=":|" : =====================================
+ >
+ > [ aBcdef ghijklmnop qrstuvwxyz 1]
+ > [2 333]
+ > [333 a B cc ]
+ > SIZE: 3
+ > delimiters=":|", and count null fields: ==============================
+ >
+ > [ aBcdef ghijklmnop qrstuvwxyz 1]
+ > []
+ > []
+ > [2 333]
+ > [333 a B cc ]
+ > SIZE: 5
+ > default delimiters and return null fields: ===========================
+ >
+ > []
+ > []
+ > [aBcdef]
+ > []
+ > []
+ > [ghijklmnop]
+ > [qrstuvwxyz]
+ > []
+ > [1:|:2]
+ > []
+ > []
+ > []
+ > []
+ > [333|333]
+ > [a]
+ > [B]
+ > [cc]
+ > []
+ > []
+ > []
+ > SIZE: 20
+
+======================================================================
+John S. Urban
+Public Domain
+split2020(3f) - [M_strings:TOKENS] parse a string into tokens using proposed f2023 method (LICENSE:PD)
+TOKEN form
+ subroutine split2020 (string, set, tokens, separator)
+ character(len=*),intent(in) :: string
+ character(len=*),intent(in) :: set
+ character(len=:),allocatable,intent(out) :: tokens(:)
+ character(len=1),allocatable,intent(out),optional :: separator(:)
+
+BOUNDS ARRAY form
+ subroutine split2020 (string, set, first, last)
+ character(len=*),intent(in) :: string
+ character(len=*),intent(in) :: set
+ integer,allocatable,intent(out) :: first(:)
+ integer,allocatable,intent(out) :: last(:)
+
+STEP THROUGH BY POSITION form
+ subroutine split2020 (string, set, pos [, back])
+ character(len=*),intent(in) :: string
+ character(len=*),intent(in) :: set
+ integer,intent(inout) :: pos
+ logical,intent(in),optional :: back
+
+Parse a string into tokens. STRING, SET, TOKENS and SEPARATOR must all be of the same CHARACTER kind type parameter.
+STRING
+string to break into tokens
SET
+Each character in SET is a token delimiter. A sequence of zero or more characters in STRING delimited by any token delimiter, or the beginning or end of STRING, comprise a token. Thus, two consecutive token delimiters in STRING, or a token delimiter in the first or last character of STRING, indicate a token with zero length.
??? how about if null defaults to all whitespace characters
TOKENS
+It is allocated with the lower bound equal to one and the upper bound equal to the number of tokens in STRING, and with character length equal to the length of the longest token. The tokens in STRING are assigned by intrinsic assignment, in the order found, to the elements of TOKENS, in array element order.
???If input is null it still must be of size 1?
SEPARATOR
+Each element in SEPARATOR(i) is assigned the value of the ith token delimiter in STRING. It is allocated with the lower bound equal to one and the upper bound equal to one less than the number of tokens in STRING, and with character length equal to one.
???one less than? '' ' '
FIRST
+It is allocated with the lower bound equal to one and the upper bound equal to the number of tokens in STRING. Each element is assigned, in array element order, the starting position of each token in STRING, in the order found. If a token has zero length, the starting position is equal to one if the token is at the beginning of STRING, and one greater than the position of the preceding delimiter otherwise.
LAST
+It is allocated with the lower bound equal to one and the upper bound equal to the number of tokens in STRING. Each element is assigned, in array element order, the ending position of each token in STRING, in the order found. If a token has zero length, the ending position is one less than the starting position.
POS
+If BACK is present with the value .TRUE., the value
of POS shall be in the range 0 < POS
+LEN (STRING)+1;
otherwise it shall be in the range 0
+POS LEN (STRING).
If BACK is absent or is present with the value .FALSE., POS is assigned the position of the leftmost token delimiter in STRING whose position is greater than POS, or if there is no such character, it is assigned a value one greater than the length of STRING. This identifies a token with starting position one greater than the value of POS on invocation, and ending position one less than the value of POS on return.
+If BACK is present with the value true, POS is assigned the position of the rightmost token delimiter in STRING whose position is less than POS, or if there is no such character, it is assigned the value zero. This identifies a token with ending position one less than the value of POS on invocation, and starting position one greater than the value of POS on return.
+When SPLIT is invoked with a value for POS of 1 <= POS <= LEN(STRING) and STRING(POS:POS) is not a token delimiter present in SET, the token identified by SPLIT does not comprise a complete token as described in the description of the SET argument, but rather a partial token.
BACK
+shall be a logical scalar. It is an INTENT (IN) argument. If POS does not appear and BACK is present with the value true, STRING is scanned backwards for tokens starting from the end. If POS does not appear and BACK is absent or present with the value false, STRING is scanned forwards for tokens starting from the beginning.
Sample of uses
+ program demo_sort2020
+ use M_strings, only : split2020
+ implicit none
+ character(len=*),parameter :: gen='(*("[",g0,"]":,","))'
+
+ ! Execution of TOKEN form
+ block
+ character (len=:), allocatable :: string
+ character (len=:), allocatable :: tokens(:)
+ character (len=*),parameter :: set = " ,"
+ string = 'first,second,third'
+ call split2020(string, set, tokens )
+ write(*,gen)tokens
+
+ ! assigns the value ['first ','second','third ' ]
+ ! to TOKENS.
+ endblock
+
+ ! Execution of BOUNDS form
+
+ block
+ character (len=:), allocatable :: string
+ character (len=*),parameter :: set = " ,"
+ integer, allocatable :: first(:), last(:)
+ string = 'first,second,,forth'
+ call split2020 (string, set, first, last)
+ write(*,gen)first
+ write(*,gen)last
+
+ ! will assign the value [ 1, 7, 14, 15 ] to FIRST,
+ ! and the value [ 5, 12, 13, 19 ] to LAST.
+ endblock
+
+ ! Execution of STEP form
+ block
+ character (len=:), allocatable :: string
+ character (len=*),parameter :: set = " ,"
+ integer :: p, ibegin, iend
+ string = " one, last example "
+ do while (p < len(string))
+ ibegin = p + 1
+ call split2020 (string, set, p)
+ iend=p-1
+ if(iend > ibegin)then
+ print '(t3,a,1x,i0,1x,i0)', string (ibegin:iend),ibegin,iend
+ endif
+ enddo
+ endblock
+ end program demo_sort2020
+
+Results:
+ [first ],[second],[third ]
+ [1],[7],[14],[15]
+ [5],[12],[13],[19]
+ one 2 4
+ last 9 12
+ example 15 21
+
+ > ??? option to skip adjacent delimiters (not return null tokens)
+ > common with whitespace
+ > ??? quoted strings, especially CSV both " and ', Fortran adjacent
+ > is insert versus other rules
+ > ??? escape character like \\ .
+ > ??? multi-character delimiters like \\n, \\t,
+ > ??? regular expression separator
+
+Milan Curcic, "milancurcic@hey.com"
+version 0.1.0, copyright 2020, Milan Curcic
+split(3f) - [M_strings:TOKENS] parse string into an array using specified delimiters (LICENSE:PD)
+subroutine split(input_line,array,delimiters,order,nulls)
+ character(len=*),intent(in) :: input_line
+ character(len=:),allocatable,intent(out) :: array(:)
+ character(len=*),optional,intent(in) :: delimiters
+ character(len=*),optional,intent(in) :: order
+ character(len=*),optional,intent(in) :: nulls
+
+SPLIT(3f) parses a string using specified delimiter characters and store tokens into an allocatable array
+INPUT_LINE
+Input string to tokenize
ARRAY
+Output array of tokens
DELIMITERS
+List of delimiter characters. The default delimiters are the "whitespace" characters (space, tab,new line, vertical tab, formfeed, carriage return, and null). You may specify an alternate set of delimiter characters.
Multi-character delimiters are not supported (Each character in the DELIMITERS list is considered to be a delimiter).
+Quoting of delimiter characters is not supported.
ORDER SEQUENTIAL|REVERSE|RIGHT
+Order of output array. By default ARRAY contains the tokens having parsed the INPUT_LINE from left to right. If ORDER='RIGHT' or ORDER='REVERSE' the parsing goes from right to left. (This can be accomplished with array syntax in modern Fortran, but was more useful pre-fortran90).
NULLS=IGNORE|RETURN|IGNOREEND
+Treatment of null fields. By default adjacent delimiters in the input string do not create an empty string in the output array. if NULLS='return' adjacent delimiters create an empty element in the output ARRAY. If NULLS='ignoreend' then only trailing delimiters at the right of the string are ignored.
Sample program:
+ program demo_split
+ use M_strings, only: split
+ implicit none
+ integer :: i
+ character(len=*),parameter :: title='(80("="),t1,a)'
+ character(len=*),parameter :: line=&
+ ' aBcdef ghijklmnop qrstuvwxyz 1:|:2 333|333 a B cc '
+ character(len=:),allocatable :: array(:) ! output array of tokens
+ write(*,*)'INPUT LINE:['//line//']'
+ !
+ write(*,title)'typical call: '
+ call split(line,array)
+ call printme()
+ !
+ write(*,title)'custom delimiters=":|" : '
+ call split(line,array,delimiters=':|',&
+ & order='sequential',nulls='ignore')
+ call printme()
+ !
+ write(*,title)&
+ 'delimiters=":|",reverse array order and count null fields:'
+ call split(line,array,delimiters=':|',&
+ & order='reverse',nulls='return')
+ call printme()
+ !
+ write(*,title)&
+ 'default delimiters, reverse array order and return null fields:'
+ call split(line,array,delimiters='',&
+ & order='reverse',nulls='return')
+ call printme()
+ contains
+ subroutine printme()
+ write(*,'(i0," ==> ",a)')(i,trim(array(i)),i=1,size(array))
+ write(*,*)'SIZE:',size(array)
+ end subroutine printme
+ end program demo_split
+
+Results:
+ > INPUT LINE:
+ > [ aBcdef ghijklmnop qrstuvwxyz 1:|:2 333|333 a B cc ]
+ > typical call: ========================================================
+ > 1 ==> aBcdef
+ > 2 ==> ghijklmnop
+ > 3 ==> qrstuvwxyz
+ > 4 ==> 1:|:2
+ > 5 ==> 333|333
+ > 6 ==> a
+ > 7 ==> B
+ > 8 ==> cc
+ > SIZE: 8
+ > custom delimiters=":|" : =============================================
+ > 1 ==> aBcdef ghijklmnop qrstuvwxyz 1
+ > 2 ==> 2 333
+ > 3 ==> 333 a B cc
+ > SIZE: 3
+ > delimiters=":|",reverse array order and count null fields:============
+ > 1 ==> 333 a B cc
+ > 2 ==> 2 333
+ > 3 ==>
+ > 4 ==>
+ > 5 ==> aBcdef ghijklmnop qrstuvwxyz 1
+ > SIZE: 5
+ > default delimiters, reverse array order and return null fields:=======
+ > 1 ==>
+ > 2 ==>
+ > 3 ==>
+ > 4 ==> cc
+ > 5 ==> B
+ > 6 ==> a
+ > 7 ==> 333|333
+ > 8 ==>
+ > 9 ==>
+ > 10 ==>
+ > 11 ==>
+ > 12 ==> 1:|:2
+ > 13 ==>
+ > 14 ==> qrstuvwxyz
+ > 15 ==> ghijklmnop
+ > 16 ==>
+ > 17 ==>
+ > 18 ==> aBcdef
+ > 19 ==>
+ > 20 ==>
+ > SIZE: 20
+
+John S. Urban
+Public Domain
+squeeze(3f) - [M_strings:EDITING] delete adjacent duplicate occurrences of a character from a string (LICENSE:PD)
+function squeeze(STR,CHAR) result (OUTSTR)
+ character(len=*),intent(in) :: STR
+ character(len=*),intent(in),optional :: CHAR
+ character(len=len(str)) :: OUTSTR
+
+squeeze(3f) reduces adjacent duplicates of the specified character to a single character
+STR
+input string in which to reduce adjacent duplicate characters to a single character
CHAR
+The character to remove adjacent duplicates of
Sample Program:
+ program demo_squeeze
+ use M_strings, only : squeeze
+ implicit none
+ character(len=:),allocatable :: strings(:)
+
+ strings=[ character(len=72) :: &
+ &'', &
+ &'"If I were two-faced,&
+ &would I be wearing this one?" --- Abraham Lincoln', &
+ &'..1111111111111111111&
+ &111111111111111111111111111111111111111111117777888', &
+ &'I never give ''em hell,&
+ &I just tell the truth, and they think it''s hell.',&
+ &' &
+ & --- Harry S Truman' &
+ &]
+ call printme( trim(strings(1)), ' ' )
+ call printme( strings(2:4), ['-','7','.'] )
+ call printme( strings(5), [' ','-','r'] )
+ contains
+ impure elemental subroutine printme(str,chr)
+ character(len=*),intent(in) :: str
+ character(len=1),intent(in) :: chr
+ character(len=:),allocatable :: answer
+ write(*,'(a)')repeat('=',11)
+ write(*,'("IN: <<<",g0,">>>")')str
+ answer=squeeze(str,chr)
+ write(*,'("OUT: <<<",g0,">>>")')answer
+ write(*,'("LENS: ",*(g0,1x))')"from",len(str),"to",len(answer), &
+ & "for a change of",len(str)-len(answer)
+ write(*,'("CHAR: ",g0)')chr
+ end subroutine printme
+ end program demo_squeeze
+
+John S. Urban
+Public Domain
+stretch(3f) - [M_strings:LENGTH] return string padded to at least specified length (LICENSE:PD)
+function stretch(str,length,pattern,suffix) result(strout)
+ character(len=*),intent(in) :: str
+ integer,intent(in) :: length
+ character(len=*)intent(in),optional :: pattern
+ character(len=*)intent(in),optional :: suffix
+ character(len=:),allocatable :: strout
+
+stretch(3f) pads a string with spaces to at least the specified length. If the trimmed input string is longer than the requested length the original string is returned trimmed of trailing spaces.
+str
+the input string to return trimmed, but then padded to the specified length if shorter than length
length
+The minimum string length to return
pattern
+optional string to use as padding. Defaults to a space.
suffix
+optional string to append to output string
Sample Program:
+ program demo_stretch
+ use M_strings, only : stretch
+ implicit none
+ character(len=10) :: string='abcdefghij'
+ character(len=:),allocatable :: answer
+ integer :: i
+ answer=stretch(string,5)
+ write(*,'("[",a,"]")') answer
+ answer=stretch(string,20)
+ write(*,'("[",a,"]")') answer
+ i=30
+ write(*,*)
+ write(*,'(1x,a,i0)') &
+ & stretch('CHAPTER 1 : The beginning ',i,'.'), 1 ,&
+ & stretch('CHAPTER 2 : The end ',i,'.'), 1234 ,&
+ & stretch('APPENDIX ',i,'.'), 1235
+ write(*,*)
+ write(*,'(1x,a,i7)') &
+ & stretch('CHAPTER 1 : The beginning ',i,'.'), 1 ,&
+ & stretch('CHAPTER 2 : The end ',i,'.'), 1234 ,&
+ & stretch('APPENDIX ',i,'.'), 1235
+ write(*,*)
+ write(*,*) &
+ & stretch('CHAPTER 1 : The beginning ',i,suffix=': '), 1
+ write(*,*) &
+ & stretch('CHAPTER 2 : The end ',i,suffix=': '),1234
+ write(*,*) &
+ & stretch('APPENDIX ',i,suffix=': '), 1235
+ end program demo_stretch
+
+ Results:
+
+ [abcdefghij]
+ [abcdefghij ]
+
+ CHAPTER 1 : The beginning ```.1
+ CHAPTER 2 : The end ```.......1234
+ APPENDIX ```..................1235
+
+ CHAPTER 1 : The beginning ```. 1
+ CHAPTER 2 : The end ```....... 1234
+ APPENDIX ```.................. 1235
+
+ CHAPTER 1 : The beginning : 1
+ CHAPTER 2 : The end : 1234
+ APPENDIX : 1235
+
+John S. Urban
+Public Domain
+string_to_value(3f) - [M_strings:TYPE] subroutine returns numeric value from string (LICENSE:PD)
+subroutine string_to_value(chars,valu,ierr)
+ character(len=*),intent(in) :: chars ! input string
+ integer|real|doubleprecision,intent(out) :: valu
+ integer,intent(out) :: ierr
+
+Returns a numeric value from a numeric character string.
+Works with any g-format input, including integer, real, and exponential. If the input string begins with "B", "Z", or "O" and otherwise represents a positive whole number it is assumed to be a binary, hexadecimal, or octal value. If the string contains commas they are removed. If the string is of the form NN:MMM``` or NN#MMM then NN is assumed to be the base of the whole number.
+If an error occurs in the READ, IOSTAT is returned in IERR and value is set to zero. if no error occurs, IERR=0.
+VALU
+numeric value returned. May be INTEGER, REAL, or DOUBLEPRECISION.
IERR
+error flag (0 == no error)
Sample Program:
+ program demo_string_to_value
+ use M_strings, only: string_to_value
+ implicit none
+ real :: value
+ integer :: ierr
+ character(len=80) :: string
+ string=' -40.5e-2 '
+ call string_to_value(string,value,ierr)
+ write(*,*) 'value of string ['//trim(string)//'] is ',value
+ end program demo_string_to_value
+
+John S. Urban
+Public Domain
+string_to_values(3f) - [M_strings:TYPE] read a string representing numbers into a numeric array (LICENSE:PD)
+subroutine string_to_values(line,iread,values,inums,delims,ierr)
+ character(len=*) :: line
+ integer :: iread
+ real :: values(*)
+ integer :: inums
+ character(len=*) :: delims
+ integer :: ierr
+
+This routine can take a string representing a series of numbers and convert it to a numeric array and return how many numbers were found.
+LINE
+Input string containing numbers
IREAD
+maximum number of values to try to read from input string
VALUES
+real array to be filled with numbers
INUMS
+number of values successfully read (before error occurs if one does)
DELIMS
+delimiter character(s), usually a space. must not be a null string. If more than one character, a space must not be the last character or it will be ignored.
IERR
+error flag (0=no error, else column number string starts at that error occurred on).
Sample Program:
+ program demo_string_to_values
+ use M_strings, only : string_to_values
+ implicit none
+ character(len=80) :: s=' 10 20e3;3.45 -400.3e-2;1234; 5678 '
+ integer,parameter :: isz=10
+ real :: array(isz)
+ integer :: inums, ierr, ii
+
+ call string_to_values(s,10,array,inums,' ;',ierr)
+ call reportit()
+
+ call string_to_values('10;2.3;3.1416',isz,array,inums,' ;',ierr)
+ call reportit()
+
+ contains
+ subroutine reportit()
+ write(*,*)'string_to_values:'
+ write(*,*)'input string```..........',trim(s)
+ write(*,*)'number of values found```',inums
+ write(*,*)'values```................',(array(ii),ii=1,inums)
+ end subroutine reportit
+ end program demo_string_to_values
+
+Expected output
+ string_to_values:
+ input string```.......... 10 20e3;3.45 -400.3e-2;1234; 5678
+ number of values found``` 6
+ values```................ 10.0000000 20000.0000 3.45000005
+ -4.00299978 1234.00000 5678.00000
+ string_to_values:
+ input string```.......... 10 20e3;3.45 -400.3e-2;1234; 5678
+ number of values found``` 3
+ values```................ 10.0000000 2.29999995 3.14159989
+
+John S. Urban
+Public Domain
+strtok(3f) - [M_strings:TOKENS] Tokenize a string (LICENSE:PD)
+function strtok(source_string,itoken,token_start,token_end,delimiters) result(strtok_status)
+ ! returned value
+ logical :: strtok_status
+ ! string to tokenize
+ character(len=*),intent(in) :: source_string
+ ! token count since started
+ integer,intent(inout) :: itoken
+ ! beginning of token
+ integer,intent(out) :: token_start
+ ! end of token
+ integer,intent(inout) :: token_end
+ ! list of separator characters
+ character(len=*),intent(in) :: delimiters
+
+The STRTOK(3f) function is used to isolate sequential tokens in a string, SOURCE_STRING. These tokens are delimited in the string by at least one of the characters in DELIMITERS. The first time that STRTOK(3f) is called, ITOKEN should be specified as zero. Subsequent calls, wishing to obtain further tokens from the same string,
+This routine assumes no other calls are made to it using any other input string while it is processing an input line.
+source_string
+input string to parse
itoken
+token count should be set to zero for a new string
delimiters
+characters used to determine the end of tokens
token_start
+beginning position in SOURCE_STRING where token was found
token_end
+ending position in SOURCE_STRING where token was found strtok_status
Sample program:
+ program demo_strtok
+ use M_strings, only : strtok
+ implicit none
+ character(len=264) :: inline
+ character(len=*),parameter :: delimiters=' ;,'
+ integer :: ios, itoken, ibegin, iend
+ do ! read lines from stdin until end-of-file or error
+ read (unit=*,fmt="(a)",iostat=ios) inline
+ if(ios /= 0)stop
+ ! must set ITOKEN=0 before looping on strtok(3f)
+ ! on a new string.
+ itoken=0
+ do while &
+ &( strtok(inline,itoken,ibegin,iend,delimiters) )
+ print *, itoken,&
+ & 'TOKEN=['//(inline(ibegin:iend))//']',ibegin,iend
+ enddo
+ enddo
+ end program demo_strtok
+
+ sample input file
+
+ this is a test of strtok; A:B :;,C;;
+
+ sample output file
+
+ 1 TOKEN=[this] 2 5
+ 2 TOKEN=[is] 7 8
+ 3 TOKEN=[a] 10 10
+ 4 TOKEN=[test] 12 15
+ 5 TOKEN=[of] 17 18
+ 6 TOKEN=[strtok] 20 25
+ 7 TOKEN=[A:B] 28 30
+ 8 TOKEN=[:] 32 32
+ 9 TOKEN=[C] 35 35
+
+John S. Urban
+Public Domain
+substitute(3f) - [M_strings:EDITING] subroutine globally substitutes one substring for another in string (LICENSE:PD)
+subroutine substitute(targetline,old,new,ierr,start,end)
+ character(len=*) :: targetline
+ character(len=*),intent(in) :: old
+ character(len=*),intent(in) :: new
+ integer,intent(out),optional :: ierr
+ integer,intent(in),optional :: start
+ integer,intent(in),optional :: end
+
+Globally substitute one substring for another in string.
+TARGETLINE
+input line to be changed. Must be long enough to hold altered output.
OLD
+substring to find and replace
NEW
+replacement for OLD substring
IERR
+error code. If IER = -1 bad directive, >= 0 then count of changes made.
START
+sets the left margin to be scanned for OLD in TARGETLINE.
END
+sets the right margin to be scanned for OLD in TARGETLINE.
Sample Program:
+ program demo_substitute
+ use M_strings, only : substitute
+ implicit none
+ ! must be long enough to hold changed line
+ character(len=80) :: targetline
+
+ targetline='this is the input string'
+ write(*,*)'ORIGINAL : '//trim(targetline)
+
+ ! changes the input to 'THis is THe input string'
+ call substitute(targetline,'th','TH')
+ write(*,*)'th => TH : '//trim(targetline)
+
+ ! a null old substring means "at beginning of line"
+ ! changes the input to 'BEFORE:this is the input string'
+ call substitute(targetline,'','BEFORE:')
+ write(*,*)'"" => BEFORE: '//trim(targetline)
+
+ ! a null new string deletes occurrences of the old substring
+ ! changes the input to 'ths s the nput strng'
+ call substitute(targetline,'i','')
+ write(*,*)'i => "" : '//trim(targetline)
+
+ end program demo_substitute
+
+Expected output
+ ORIGINAL : this is the input string
+ th => TH : THis is THe input string
+ "" => BEFORE: BEFORE:THis is THe input string
+ i => "" : BEFORE:THs s THe nput strng
+
+John S. Urban
+Public Domain
+switch(3f) - [M_strings:ARRAY] converts between CHARACTER scalar and array of single characters (LICENSE:PD)
+pure function switch(array) result (string)
+ character(len=1),intent(in) :: array(:)
+ character(len=SIZE(array)) :: string
+
+ or
+
+pure function switch(string) result (array)
+ character(len=*),intent(in) :: string
+ character(len=1) :: array(len(string))
+
+SWITCH(3f): generic function that switches CHARACTER string to an array of single characters or an array of single characters to a CHARACTER string. Useful in passing strings to C. New Fortran features may supersede these routines.
+Sample program:
+ program demo_switch
+ use M_strings, only : switch, isalpha, islower, nospace
+ character(len=*),parameter :: &
+ & dashes='-----------------------------------'
+ character(len=*),parameter :: string='This is a string'
+ character(len=1024) :: line
+
+ ! First, examples of standard Fortran features
+ ! returns array [F,T,T,T,T,T]
+ write(*,*)['A','=','=','=','=','='] == '='
+ ! this would return T
+ write(*,*)all(['=','=','=','=','=','='] == '=')
+ ! this would return F
+ write(*,*)all(['A','=','=','=','=','='] == '=')
+
+ ! so to test if the string DASHES is all dashes
+ ! using SWITCH(3f) is
+ if(all(switch(dashes) == '-'))then
+ write(*,*)'DASHES is all dashes'
+ endif
+
+ ! so to test is a string is all letters
+ ! isalpha(3f) returns .true. only if character is a letter
+ ! false because dashes are not a letter
+ write(*,*) all(isalpha(switch(dashes)))
+ ! false because of spaces
+ write(*,*) all(isalpha(switch(string)))
+ ! true because removed whitespace
+ write(*,*) all(isalpha(switch(nospace(string))))
+
+ ! to see if a string is all uppercase
+ ! show the string
+ write(*,*) string
+ ! converted to character array
+ write(*,'(1x,*("[",a,"]":))') switch(string)
+ write(*,'(*(l3))') islower(switch(string))
+
+ ! we need a string that is all letters
+ line=nospace(string)
+ write(*,*)'LINE=',trim(line)
+ ! all true except first character
+ write(*,*) islower(switch(nospace(string)))
+ ! should be false
+ write(*,*) all(islower(switch(nospace(string))))
+ ! should be true
+ write(*,*) all(islower(switch(nospace(string(2:)))))
+
+ end program demo_switch
+
+Expected output
+DASHES is all dashes
+This is a string [T][h][i][s][ ][i][s][ ][a][ ][s][t][r][i][n][g]
+LINE=Thisisastring
+John S. Urban
+Public Domain
+transliterate(3f) - [M_strings:EDITING] replace characters from old set with new set (LICENSE:PD)
+pure function transliterate(instr,old_set,new_set) result(outstr)
+ character(len=*),intent(in) :: instr
+ character(len=*),intent(in) :: old_set
+ character(len=*),intent(in) :: new_set
+ character(len=len(instr)) :: outstr
+
+Translate, squeeze, and/or delete characters from the input string.
+instr
+input string to change
old_set
+list of letters to change in INSTR if found
Each character in the input string that matches a character
+ in the old set is replaced.
+
+ If the new_set is the empty set the matched characters
+ are deleted.
+
+ If the new_set is shorter than the old set the last character
+ in the new set is used to replace the remaining characters
+ in the new set.
+
+Sample Program:
+ program demo_transliterate
+
+ use M_strings, only : transliterate
+ implicit none
+ character(len=80) :: STRING
+
+ STRING='aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ'
+ write(*,'(a)') STRING
+
+ ! convert a string to uppercase:
+ write(*,*) TRANSLITERATE(STRING, &
+ & 'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')
+
+ ! change all miniscule letters to a colon (":"):
+ write(*,*) TRANSLITERATE(STRING, &
+ & 'abcdefghijklmnopqrstuvwxyz',':')
+
+ ! delete all miniscule letters
+ write(*,*) TRANSLITERATE(STRING, &
+ & 'abcdefghijklmnopqrstuvwxyz','')
+
+ end program demo_transliterate
+
+ Expected output
+
+ > aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
+ > AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ
+ > :A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z
+ > ABCDEFGHIJKLMNOPQRSTUVWXYZ
+
+John S. Urban
+Public Domain
+unquote(3f) - [M_strings:QUOTES] remove quotes from string as if read with list-directed input (LICENSE:PD)
+function unquote(quoted_str,esc) result (unquoted_str)
+ character(len=*),intent(in) :: quoted_str
+ character(len=1),optional,intent(in) :: esc
+ character(len=:),allocatable :: unquoted_str
+
+Remove quotes from a CHARACTER variable as if it was read using list-directed input. This is particularly useful for processing tokens read from input such as CSV files.
+Fortran can now read using list-directed input from an internal file, which should handle quoted strings, but list-directed input does not support escape characters, which UNQUOTE(3f) does.
+quoted_str
+input string to remove quotes from, using the rules of list-directed input (two adjacent quotes inside a quoted region are replaced by a single quote, a single quote or double quote is selected as the delimiter based on which is encountered first going from left to right, ```)
esc
+optional character used to protect the next quote character from being processed as a quote, but simply as a plain character.
Sample program:
+ program demo_unquote
+ use M_strings, only : unquote
+ implicit none
+ character(len=128) :: quoted_str
+ character(len=:),allocatable :: unquoted_str
+ character(len=1),parameter :: esc='\'
+ character(len=1024) :: msg
+ integer :: ios
+ character(len=1024) :: dummy
+ do
+ write(*,'(a)',advance='no')'Enter test string:'
+ read(*,'(a)',iostat=ios,iomsg=msg)quoted_str
+ if(ios /= 0)then
+ write(*,*)trim(msg)
+ exit
+ endif
+
+ ! the original string
+ write(*,'(a)')'QUOTED ['//trim(quoted_str)//']'
+
+ ! the string processed by unquote(3f)
+ unquoted_str=unquote(trim(quoted_str),esc)
+ write(*,'(a)')'UNQUOTED ['//unquoted_str//']'
+
+ ! read the string list-directed to compare the results
+ read(quoted_str,*,iostat=ios,iomsg=msg)dummy
+ if(ios /= 0)then
+ write(*,*)trim(msg)
+ else
+ write(*,'(a)')'LIST DIRECTED['//trim(dummy)//']'
+ endif
+ enddo
+ end program demo_unquote
+
+John S. Urban
+Public Domain
+upper(3f) - [M_strings:CASE] changes a string to uppercase (LICENSE:PD)
+elemental pure function upper(str,begin,end) result (string)
+ character(*), intent(in) :: str
+ integer,optional,intent(in) :: begin,end
+ character(len(str)) :: string ! output string
+
+upper(string) returns a copy of the input string with all characters converted in the optionally specified range to uppercase, assuming ASCII character sets are being used. If no range is specified the entire string is converted to uppercase.
+str
+string to convert to uppercase
begin
+optional starting position in "str" to begin converting to uppercase
end
+optional ending position in "str" to stop converting to uppercase
The terms "uppercase" and "lowercase" date back to the early days of the mechanical printing press. Individual metal alloy casts of each needed letter, or punctuation symbol, were meticulously added to a press block, by hand, before rolling out copies of a page. These metal casts were stored and organized in wooden cases. The more often needed miniscule letters were placed closer to hand, in the lower cases of the work bench. The less often needed, capitalized, majuscule letters, ended up in the harder to reach upper cases.
+Sample program:
+ program demo_upper
+ use M_strings, only: upper
+ implicit none
+ character(len=:),allocatable :: s
+ s=' ABCDEFG abcdefg '
+ write(*,*) 'mixed-case input string is ```.',s
+ write(*,*) 'upper-case output string is ```',upper(s)
+ write(*,*) 'make first character uppercase ``` ',&
+ & upper('this is a sentence.',1,1)
+ write(*,'(1x,a,*(a:,"+"))') 'UPPER(3f) is elemental ==>',&
+ & upper(["abc","def","ghi"])
+ end program demo_upper
+
+Expected output
+ mixed-case input string is ```. ABCDEFG abcdefg
+ upper-case output string is ``` ABCDEFG ABCDEFG
+ make first character uppercase ``` This is a sentence.
+ UPPER(3f) is elemental ==>ABC+DEF+GHI
+
+John S. Urban
+Public Domain
+upper_quoted(3f) - [M_strings:CASE] elemental function converts string to uppercase skipping strings quoted per Fortran syntax rules (LICENSE:PD)
+elemental pure function upper_quoted(str) result (string)
+ character(*), intent(in) :: str
+ character(len(str)) :: string ! output string
+
+upper_quoted(string) returns a copy of the input string with all not-quoted characters converted to uppercase, assuming ASCII character sets are being used. The quoting rules are the same as for Fortran source. Either a single or double quote starts a quoted string, and a quote character of the same type is doubled when it appears internally in the quoted string. If a double quote quotes the string single quotes may appear in the quoted string as single characters, and vice-versa for single quotes.
+Sample program:
+ program demo_upper_quoted
+ use M_strings, only: upper_quoted
+ implicit none
+ character(len=:),allocatable :: s
+ s=' ABCDEFG abcdefg "Double-Quoted" ''Single-Quoted'' "with ""&
+ & Quote" everything else'
+ write(*,*) 'mixed-case input string is ```.',s
+ write(*,*) 'upper-case output string is ```',upper_quoted(s)
+ write(*,'(1x,a,*(a:,"+"))') 'upper_quoted(3f) is elemental ==>', &
+ & upper_quoted(["abc","def","ghi"])
+ end program demo_upper_quoted
+
+Expected output:
+ mixed-case input string is ```. ABCDEFG abcdefg "Double-Quoted"
+ 'Single-Quoted' "with "" Quote" everything else
+ upper-case output string is ``` ABCDEFG ABCDEFG "Double-Quoted"
+ 'Single-Quoted' "with "" Quote" EVERYTHING ELSE
+ upper_quoted(3f) is elemental ==>ABC+DEF+GHI
+
+John S. Urban
+Public Domain
+v2s(3f) - [M_strings:TYPE] return numeric string from a numeric value (LICENSE:PD)
+function v2s(value) result(outstr)
+ integer|real|doubleprecision|logical,intent(in ) :: value
+ character(len=:),allocatable :: outstr
+ character(len=*),optional,intent(in) :: fmt
+
+v2s(3f) returns a representation of a numeric value as a string when given a numeric value of type REAL, DOUBLEPRECISION, INTEGER or LOGICAL. It creates the strings using internal WRITE() statements. Trailing zeros are removed from non-zero values, and the string is left-justified.
+VALUE
+input value to be converted to a string
FMT
+format can be explicitly given, but is limited to generating a string of eighty or less characters.
Sample Program:
+ program demo_v2s
+ use M_strings, only: v2s
+ write(*,*) 'The value of 3.0/4.0 is ['//v2s(3.0/4.0)//']'
+ write(*,*) 'The value of 1234 is ['//v2s(1234)//']'
+ write(*,*) 'The value of 0d0 is ['//v2s(0d0)//']'
+ write(*,*) 'The value of .false. is ['//v2s(.false.)//']'
+ write(*,*) 'The value of .true. is ['//v2s(.true.)//']'
+ end program demo_v2s
+
+Expected output
+ The value of 3.0/4.0 is [0.75]
+ The value of 1234 is [1234]
+ The value of 0d0 is [0]
+ The value of .false. is [F]
+ The value of .true. is [T]
+
+John S. Urban
+Public Domain
+value_to_string(3f) - [M_strings:TYPE] return numeric string from a numeric value (LICENSE:PD)
+subroutine value_to_string(value,chars[,lgth,ierr,fmt,trimz])
+ character(len=*) :: chars ! minimum of 23 characters required
+ !--------
+ ! VALUE may be any <em>one</em> of the following types:
+ doubleprecision,intent(in) :: value
+ real,intent(in) :: value
+ integer,intent(in) :: value
+ logical,intent(in) :: value
+ !--------
+ character(len=*),intent(out) :: chars
+ integer,intent(out),optional :: lgth
+ integer,optional :: ierr
+ character(len=*),intent(in),optional :: fmt
+ logical,intent(in) :: trimz
+
+value_to_string(3f) returns a numeric representation of a numeric value in a string given a numeric value of type REAL, DOUBLEPRECISION, INTEGER or LOGICAL. It creates the string using internal writes. It then removes trailing zeros from non-zero values, and left-justifies the string.
+VALUE
+input value to be converted to a string
FMT
+You may specify a specific format that produces a string up to the length of CHARS; optional.
TRIMZ
+If a format is supplied the default is not to try to trim trailing zeros. Set TRIMZ to .true. to trim zeros from a string assumed to represent a simple numeric value.
CHARS
+returned string representing input value, must be at least 23 characters long; or what is required by optional FMT if longer.
LGTH
+position of last non-blank character in returned string; optional.
IERR
+If not zero, error occurred; optional.
Sample program:
+ program demo_value_to_string
+ use M_strings, only: value_to_string
+ implicit none
+ character(len=80) :: string
+ integer :: lgth
+ call value_to_string(3.0/4.0,string,lgth)
+ write(*,*) 'The value is [',string(:lgth),']'
+
+ call value_to_string(3.0/4.0,string,lgth,fmt='')
+ write(*,*) 'The value is [',string(:lgth),']'
+
+ call value_to_string&
+ &(3.0/4.0,string,lgth,fmt='("THE VALUE IS ",g0)')
+ write(*,*) 'The value is [',string(:lgth),']'
+
+ call value_to_string(1234,string,lgth)
+ write(*,*) 'The value is [',string(:lgth),']'
+
+ call value_to_string(1.0d0/3.0d0,string,lgth)
+ write(*,*) 'The value is [',string(:lgth),']'
+
+ end program demo_value_to_string
+
+Expected output
+ The value is [0.75]
+ The value is [ 0.7500000000]
+ The value is [THE VALUE IS .750000000]
+ The value is [1234]
+ The value is [0.33333333333333331]
+
+John S. Urban
+Public Domain
+visible(3f) - [M_strings:NONALPHA] expand a string to control and meta-control representations (LICENSE:PD)
+function visible(input) result(output)
+ character(len=*),intent(in) :: input
+ character(len=:),allocatable :: output
+
+visible(3f) expands characters to commonly used sequences used to represent the characters as control sequences or meta-control sequences.
+Sample Program:
+ program demo_visible
+ use M_strings, only : visible
+ integer :: i
+ do i=0,255
+ write(*,'(i0,1x,a)')i,visible(char(i))
+ enddo
+ end program demo_visible
+
+The expansion is not reversible, as input sequences such as "M-" or "^a" will look like expanded sequences.
+John S. Urban
+Public Domain
+zpad(3f) - [M_strings:LENGTH] pad a string on the left with zeros to specified length (LICENSE:PD)
+function zpad(valuein,length) result(strout)
+ class*,intent(in) :: valuein(..)
+ integer,intent(in),optional :: length
+
+zpad(3f) crops the input string (or integer, which will be converted to a string) and then pads it on the left with zeros to the specified length.
+Note that if the trimmed input string is already as long or longer than the requested length the trimmed original string is returned.
+For strings representing unsigned numbers this is basically an alias for
+ strout=pad(str,length,'0',clip=.true.,right=.false.)
+
+For integers the same is often done with internal WRITE(3f) statements such as
+ write(strout,'(i5.5)')ivalue
+
+but unlike internal I/O the function call can be used in expressions or passed as a procedure argument.
+valuein
+The input value to left-pad. May be a scalar or vector string or integer. If the leftmost non-blank character is a sign character it is moved to the left-most position of the output.
length
+The minimum string length to return. If not present, the length of the input parameter VALUEIN is used. If the input value VALUEIN is an integer no zero padding occurs if LENGTH is not supplied.
Sample Program:
+ program demo_zpad
+ use M_strings, only : zpad
+ implicit none
+ character(len=*),parameter :: boxed='("[",a,"]",*(g0,1x))'
+ integer :: lun, i
+ print boxed, zpad( '111', 5),'basic use'
+ print boxed, zpad( valuein=42 , length=7),'by argument name'
+ print boxed, zpad( ' 34567 ', 7),'cropped before padding'
+ print boxed, zpad( '123456789', 5),'input longer than length'
+ print boxed, zpad( ' +34567 ', 7),'starts with plus sign'
+ print boxed, zpad( ' -34567 ', 7),'starts with minus sign'
+ print boxed, zpad(1234),'some integers instead of strings'
+ print boxed, zpad(-1234)
+ print boxed, zpad(1234,8)
+ print boxed, zpad(-1234,8)
+ print boxed, zpad(''),'a null gets you nothing'
+ print boxed, zpad('0'),'but blanks are used for default length'
+ print boxed, zpad('0 ')
+ print boxed, zpad(' ')
+ print *, 'input value may be an array:'
+ print '("[",a,"]")', zpad([1,10,100,1000,10000,100000],8)
+
+ ! example usage:
+ ! open output_00085.dat
+ i=85
+ open(newunit=lun,file='output_'//zpad(i,5)//'.dat')
+ close(unit=lun,status='delete')
+
+ end program demo_zpad
+
+Results:
+ > [00111]basic use
+ > [0000042]by argument name
+ > [0034567]cropped before padding
+ > [123456789]input longer than length
+ > [+0034567]starts with plus sign
+ > [-0034567]starts with minus sign
+ > [1234]some integers instead of strings
+ > [-1234]
+ > [00001234]
+ > [-00001234]
+ > []a null gets you nothing
+ > [0]but blanks are used for default length
+ > [00000]
+ > [00000]
+ > input value may be an array:
+ > [00000001]
+ > [00000010]
+ > [00000100]
+ > [00001000]
+ > [00010000]
+ > [00100000]
+
+John S. Urban
+Public Domain
+