Search code examples
stringfortran

Split Fortran character string of unknown length but known substring format by commas?


I'm trying to split an input variable to some Fortran code by commas. The variable is a character string that contains a variable number of dates, but the dates are always in DD-MMM-YYYY format.

An example of such a string:

04-DEC-2015,10-DEC-2015,23-DEC-2015,25-DEC-2015

It's not always four dates, but it is always an 11-character date in that format.

I simply want to print these dates on separate lines; the current code:

write(outfile,10) '     - ', TRIM(days)

Prints:

     -  04-DEC-2015,10-DEC-2015,23-DEC-2015,25-DEC-2015

And I want to write something that prints:

     -  '04-DEC-2015'
     -  '10-DEC-2015'
     -  '23-DEC-2015'
     -  '25-DEC-2015'

Is there a straightforward way to do this (I'm pretty new to Fortran)? I'm thinking counting the number of commas in the string, adding 1 to get the number of dates, and inside a loop creating a CHARACTER(11) variable that fits each date, getting 11 characters from the string at a time (skipping commas), and printing that date in the format I want. Is that too convoluted?


Solution

  • As has previously been suggested, a list-directed read from an internal file will handle the splitting on commas without more effort. For

    character(11) :: split_days(MAX_DAYS)=''
    ndays = ...
    read(days,*) split_days(1:ndays)
    

    to work we need to know the value of ndays. If you're happy with something like

    ndays = (len_trim(days)+1)/12
    ndays = INDEX(days,',',BACK=.TRUE)/12+1
    

    or

    ndays = COUNT([(days(i:i),i=1,LEN_DAYS)].eq.',')+1
    

    then everything is good.

    Alternatively, you could have an allocatable array

    character(11), allocatable :: split_days(:)
    ndays = ...
    allocate (split_days(ndays))
    read(days,*) split_days
    

    Alternatively, you don't need to read the internal file (list-directed or otherwise), although I probably would if I trusted the form of my input data.

    do i=1,MAX_DAYS
      split_days(i)=days(12*(i-1)+1:)
      if (INDEX(days(12*i:),',').eq.0) exit
    end do
    

    A further possibility with the list-directed read approach is to choose a large number to read, and if that fails, try again reading fewer. This makes sense only in more tricky situations.

    Finally, you can use the usual array shrinking/growing tricks if desired.


    If you're just after printing, not storing, then the loop approach above avoids messing about with an array of unknown length:

    do i=1,MAX_DAYS
      print '("     - ''",A11,"''")', days(12*(i-1)+1:)
      if (INDEX(days(12*i:),',').eq.0) exit
    end do