How to read ":" (colon) as a character in fortran 90?

Viewed 80

I'm trying to read .csv files with Fortran 90, but I've gotten back this error message.

forrtl: severe (59): list-directed I/O syntax error, unit 21, file /data3/khee/data/AWS/ASCII/STATION/2021/csvfile/test.csv
Image              PC                Routine            Line        Source             
a.out              000000000043B9EE  Unknown               Unknown  Unknown
a.out              0000000000412AB0  Unknown               Unknown  Unknown
a.out              00000000004036D3  Unknown               Unknown  Unknown
a.out              00000000004034DE  Unknown               Unknown  Unknown
libc-2.17.so       000000333901F84D  __libc_start_main     Unknown  Unknown
a.out              00000000004033E9  Unknown               Unknown  Unknown

And I soon realized that "the colon" (:) of the input file causes this problem. The problem is my Fortran code can't read "the colon" as a character. But I can't edit the input files.

How can I fix it?

here's my Fortran code, and sample phrase of the input file.

program read_data

  implicit none

  integer :: HSID, DSID
  real :: HLAT, HLON, HALT, DTEMP, DWDEG, DWSPD, DPRCP, DSPRS, DMSLP, DHMD, DSR, DSRH
  character(len=16) :: DATE

  open(21,file='/data3/khee/data/AWS/ASCII/STATION/2021/csvfile/test.csv',form='formatted',access='sequential',status='old')

10  read(21,*,end=90) DSID, DATE, DTEMP, DWDEG, DWSPD, DPRCP, DSPRS, DMSLP, DHMD, DSR, DSRH
           write(*,*) DSID, DATE, DTEMP, DWDEG, DWSPD, DPRCP, DSPRS, DMSLP, DHMD, DSR, DSRH
           
go to 10
90 continue

  write(*,*) "EOF"
  stop
  
end program read_data

848,2021-01-01 00:00,-4.1,9.1,2,0,,,,0,0

thank you!

2 Answers

When CSV files consist of character fields separated by one (or more) nominated separators (usually a comma) then you probably need a dedicated reader to break them down. Suggest:

  • read a line at a time (as @Yosukesabai);
  • split that line into fields at the separators and put them into an array. Once you have an array of strings you can convert them however you like.

The code below makes heavy use of automatic allocation (which gives me the eebie-jeebies about memory leak) and, since characters may have varying length, wraps them in a "String" derived type (sorry: shades of C++!). It stops at a blank line (which might not be what you want) or end of file.

All the "background stuff" is done in a module.

!==========================================================

module ModString
   use iso_fortran_env
   implicit none

   type String
      character(len=:), allocatable :: data
   end type String

contains
   function readLine( unit )
      character(len=:), allocatable :: readLine
      integer, intent(in) :: unit
      character(len=1000) buffer
      integer stat

      read( unit, "(a)", iostat=stat ) buffer
      if ( stat /= 0 ) then
         readline = ""
      else
         readLine = trim( buffer )
      end if

   end function readLine

   !----------------------------------------------

   function splitString( line, separators )
      type(String), allocatable :: splitString(:)
      character(len=*), intent(in) :: line
      character(len=*), intent(in) :: separators

      integer numFields
      integer L
      integer i, p, q

      L = len( line )
      numFields = 1          ! should end with 1 more than number of separators
      do i = 1, len( line )
         if ( line(i:i) == separators ) numFields = NumFields + 1
      end do

      allocate( splitString( numFields ) )
      p = 1
      do i = 1, numFields - 1
         q = p - 1 + scan( line(p:L), separators )
         splitString(i)%data = line(p:q-1)
         p = q + 1
      end do
      splitString(numFields)%data = line(p:L)

   end function splitString

end module ModString

!==========================================================

program main
   use iso_fortran_env
   use ModString
   implicit none
   character(len=:), allocatable :: line
   type(String), allocatable :: fields(:)
   integer i

   open( 10, file="in" )

   do
      ! Read a single line
      line = readLine( 10 )
      if ( len( line ) == 0 ) exit
      write( *, * ) line

      ! Split line into fields at each comma
      fields = splitString( line, ',' )
      do i = 1, size( fields )
         write( *, "( 1x, 'Field ', i2, ': ', a )" ) i, fields(i)%data
      end do

      write( *, * )
   end do

   close( 10 )

end program main
          
!==========================================================

For the input
848,2020-01-01 00:00,-4.1,9.1,2,0,,,,0,0
000,2021-09-15 18:30,-4.1,9.1,2,0,A,B,C,1,
547,2022-01-01 00:00,-4.1,9.1,2,0,,,,0,5

it gives output
848,2020-01-01 00:00,-4.1,9.1,2,0,,,,0,0
Field 1: 848
Field 2: 2020-01-01 00:00
Field 3: -4.1
Field 4: 9.1
Field 5: 2
Field 6: 0
Field 7:
Field 8:
Field 9:
Field 10: 0
Field 11: 0

000,2021-09-15 18:30,-4.1,9.1,2,0,A,B,C,1,
Field 1: 000
Field 2: 2021-09-15 18:30
Field 3: -4.1
Field 4: 9.1
Field 5: 2
Field 6: 0
Field 7: A
Field 8: B
Field 9: C
Field 10: 1
Field 11:

547,2022-01-01 00:00,-4.1,9.1,2,0,,,,0,5
Field 1: 547
Field 2: 2022-01-01 00:00
Field 3: -4.1
Field 4: 9.1
Field 5: 2
Field 6: 0
Field 7:
Field 8:
Field 9:
Field 10: 0
Field 11: 5

This is what I would do....

Or , actually, i would write a preprocessor in python or something nice to work with, and format what fortran is happy with, and let fortran code to work with cleaned input.

program read_data

  implicit none

  integer :: HSID, DSID
  real :: HLAT, HLON, HALT, DTEMP, DWDEG, DWSPD, DPRCP, DSPRS, DMSLP, DHMD, DSR, DSRH
  character(len=16) :: DATE
  
  character(len=9999):: buf
  integer :: cnt, i

  open(21,file='test.csv',form='formatted',access='sequential',status='old')
  !read(11,*)

  10 read(21,'(a)',end=90) buf
  ! find first two commas
  cnt=0
  do i=1,9999
    if (buf(i:i)==',') cnt=cnt+1
    if (cnt==2)exit
  enddo


  read(buf(1:i),*) DSID, DATE
  read(buf(i+1:9999),*) DTEMP, DWDEG, DWSPD, DPRCP, DSPRS, DMSLP, DHMD, DSR, DSRH
  write(*,*) DSID, DATE, DTEMP, DWDEG, DWSPD, DPRCP, DSPRS, DMSLP, DHMD, DSR, DSRH

  go to 10
  90 continue

  write(*,*) "EOF"
  stop

end program read_data
Related