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.

Quick start cheat sheet

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

Methods and public data members

TODO

⚠️ **GitHub.com Fallback** ⚠️