parsingfortran

Fortran parsing data values from a text line


I have following line in the middle of data:

Lattice="14.118460851 0.0 0.0 0.0 14.296182713 0.0 0.0 0.0 13.970592923" Properties=species:S:1:pos:R:3:forces:R:3 energy=-1558.14096522 stress="0.01917032092545686 0.004679034697346147 -0.002715150092396106 0.004679034697346147 0.02270959988283459 -0.0033934086475967138 -0.002715150092396106 -0.0033934086475967138 0.02437748091748962" free_energy=-1558.0193295 pbc="T T T"

In this line, each keyword contains specific properties.

Here, I want to read this line and define some values to variable or array. I hope to read 9 real numbers after 'Lattice="' and store them as a, b, c, d, e, f, and I hope to read a single real number after 'energy=' and store it as g.

But I don't know how to write a Fortran script to read and recognize 'Lattice="' and 'energy=', and then select 9 or one next numbers.

How can I do this using Fortran?


Solution

  • As with Steve's answer: a combination of index(), plus reading from an internal file (i.e from a character string). Whether it works will be a bit sensitive to how precise your input file is (right case; no spaces around '=' etc.) You might want to pre-process the string to ensure that.

    It's possible that Fortran's newer stream IO might give an easier solution, but I haven't tried that.

    subroutine getLatticeEnergy( str, L, E )
       use iso_fortran_env, only: real64
       implicit none
       character(len=*), intent(in) :: str
       real(real64), intent(out) :: L(9)
       real(real64), intent(out) :: E
       integer start, finish
    
       ! Find start of lattice components
       start = index( str, 'Lattice="') + 9
       finish = start + index( str(start:), '"' ) - 2
       read( str(start:finish), * ) L
    
       ! Find start of energy
       start = index( str, 'energy=') + 7
       read( str(start:), * ) E
    
    end subroutine getLatticeEnergy
    
    !=======================================================================
    
    program test
       use iso_fortran_env, only: real64
       implicit none
       character(len=:), allocatable :: str
       real(real64) L(9)
       real(real64) E
    
       str = 'Lattice="14.118460851 0.0 0.0 0.0 14.296182713 0.0 0.0 0.0 13.970592923"'                     &
          // ' Properties=species:S:1:pos:R:3:forces:R:3 energy=-1558.14096522 stress="0.01917032092545686' &
          // ' 0.004679034697346147 -0.002715150092396106 0.004679034697346147 0.02270959988283459'         &
          // ' -0.0033934086475967138 -0.002715150092396106 -0.0033934086475967138 0.02437748091748962"'    &
          // ' free_energy=-1558.0193295 pbc="T T T"'
    
       call getLatticeEnergy( str, L, E )
       write( *, * ) "Lattice = ", L
       write( *, * ) "Energy = ", E
    
    end program test
    

    Output:

     Lattice =    14.118460851000000        0.0000000000000000        0.0000000000000000        0.0000000000000000        14.296182713000000        0.0000000000000000        0.0000000000000000        0.0000000000000000        13.970592923000000     
     Energy =   -1558.1409652200000