ftlString - SCM-NV/ftl GitHub Wiki
ftlString is a variable length Fortran string that provides a superset of the Fortran, C++ and Python methods for string manipulation, and integrates nicely with the rest of the Fortran Template Library.
NOTE: Not all Python methods have already been implemented. Help is much appreciated!
Let's face it: String processing is not exactly one of Fortran's strengths. The
pre-2003 fixed length strings are just awful. Trim cluttered code everywhere and
no matter how large you make them, some user will always find a way to make them
overflow. Fortran 2003 deferred-length strings are a huge improvement already
but you can't have arrays of them and read
also does not work. Furthermore
the Fortran standard library is pretty basic when it comes to string
manipulation methods and if you are used to Python string manipulation, going
back to Fortran feels like a huge limitation. Luckily modern Fortran is flexible
enough to implement a fairly convincing string as a derived type. If you are
just looking for a standalone string type for modern Fortran programs, check out
StringiFor, which is exactly that. If you are already using the Fortran
Template Library then ftlString is probably your best bet, as it integrates
nicely with the rest of the FTL, e.g. when used as a key for a dictionary.
Just to get things started: Here is just a piece of code that checks if a string contains the word 'important' and then increments the first integer it finds after the colon. Looks like Python, doesn't it?
integer :: i
type(ftlString) :: line
type(ftlString), allocatable :: words(:)
line = 'Some important number: 41'
if ('important' .in. line) write (*,*) 'line is important!'
words = line%Split()
do i = 1, size(words)
if (words(i)%EndsWith(':')) then
words(i+1) = ftlString( int(words(i+1)) + 1)
exit
endif
enddo
line = Join(' ', words)
write (*,*) line ! prints: 'Some important number: 42'
Note that an ftlString is just a wrapper (with methods) around a single, public
deferred-length character string that can be accessed as ftlString%raw
. So
if you have old Fortran subroutines that expect a raw Fortran string as an
argument, it is always ok to just take the wrapping off and pass
ftlString%raw
, even if the subroutine writes to it. There is never any
danger of leaving the ftlString
in an inconsistent state. In this sense
using ftlString is not a huge commitment, as you can still use all of your old
string manipulation code.
ftlString also provides the container interface (iterators and such) that the other FTL container templates provide. That is probably not too useful in itself, but it means that ftlAlgorithms can operate on ftlStrings as containers of single characters, which might be useful in one way or another.
The full API documentation can be found below, but for most people a quick overview of ftlString is probably enough to get started. So here it is:
! declaration of strings
type(ftlString) :: string
! ... and arrays of string:
type(ftlString), allocatable :: arrayOfStrings(:)
! and some old school Fortran strings to show the interoperability
character(len=:), allocatable :: rawDefLen
character(len=10) :: rawFixLen
! initialization is easiest through assignment
string = 'this is my test string'
! reading into ftlStrings also works
read (unit,*) string
! ^--- that would read until the end of the record, so one line in practice
! reading from a unit until the end-of-file
call string%ReadUntilEOF(unit)
! string now contains multiple lines separated by newline characters
! writing the string to a unit
write (unit,*) string
! slicing (unfortunately has to be done on the raw string)
string = string%raw(1:3)
! assignment to deferred-length raw Fortran strings
rawDefLen = string
! assignment to fixed-length raw Fortran strings (possibly truncating)
rawFixLen = string%raw
! passing ftlStrings to subroutines that expect plain Fortran strings
call myOldSubroutine(string%raw)
! it's ok if myOldSubroutine changes the string!
! concatenation with ftlString as a result
string = string + 'bla'
! concatenation with a raw Fortran string as a result
call myOldSubroutine(string // 'bla')
! querying the size
i = size(string)
! ... or ...
i = len(string)
! produces exactly the same result
! string comparison
if (string == 'bla') ...
if (string /= 'bla') ...
! Note that trailing spaces are taken into consideration (unlike for normal
! Fortran strings, where they are ignored). If len(str1) /= len(str2), the two
! strings will ALWAYS compare unequal, even if the difference is just
! trailing spaces ...
! conversion from/to numeric types
string = ftlString(42)
if (string%IsInt()) ...
i = int(string)
string = ftlString(13.37)
if (string%IsReal()) ...
r = real(string)
string = ftlString((0.0,1.0))
c = complex(string)
if (string%IsComplex()) ...
! Fortran string methods:
string = trim(string)
string = adjustl(string)
! ... and others
! Python string methods:
arrayOfStrings = string%Split(' ')
string = Join(' ', arrayOfStrings)
arrayOfStrings = string%SplitLines()
string = string%Replace('before', 'after')
! ... and others
TODO