Mark S
Mark S

Reputation: 306

Fortran formatted stream read a file with new line causes problems

If I use Fortran to write and read a file in the same manner with stream it works well. If I edit the file with a text editor without changing anything (like return then backspace) and save the file it will not read, quoting end of file before the position I expect it to end.

To investigate, I wrote the following code which writes my table then reads it in one position at a time as a character. It only works correctly if there are no new lines in the file I am reading from. Otherwise, I only get the last n numbers, n being the amount of data per line.

The idea is to learn more powerful ways than sequential read to read in large strangely formatted data files I use at work that come from engineering software and test equipment.

program stream
implicit none

character(len=1)                :: text
integer(kind=4), dimension(100) :: array
integer(kind=4)                 :: status = 0, i



open (unit=11, file='out.txt', form='formatted', access='stream', action='readwrite', status='replace')

array = (/(i, i = 1,100)/)           !  Populate vector from 1 to 100

write(unit=11,fmt='(10i4)') array    !  Write array to file

i=0
do while ( status == 0)              !  Read array back from file, one position at a time until end of file
i=i+1
read(unit=11, fmt='(a1)', pos=i, iostat=status) text
write(*,fmt='(a1)', advance='no') text  !  Write results to screen to see what is in each position
end do


close(unit=11)


end program stream

    File written then read:
       1   2   3   4   5   6   7   8   9  10
      11  12  13  14  15  16  17  18  19  20
      21  22  23  24  25  26  27  28  29  30
      31  32  33  34  35  36  37  38  39  40
      41  42  43  44  45  46  47  48  49  50
      51  52  53  54  55  56  57  58  59  60
      61  62  63  64  65  66  67  68  69  70
      71  72  73  74  75  76  77  78  79  80
      81  82  83  84  85  86  87  88  89  90
      91  92  93  94  95  96  97  98  99 100

    Result: (many spaces here)
8   8   8   8   8   8   8   8   9    91  92  93  94  95  96  97  98  99 100 

Upvotes: 4

Views: 1214

Answers (1)

IanH
IanH

Reputation: 21431

I think what you are seeing is an issue with the compiler runtime, exacerbated by choices you have made with your program.

With formatted stream, a read statement without an ADVANCE specifier advances to the next record, just like a READ with a formatted sequential file. Because of the format specification associated with the READ, you then discard all but the first character read, but the underlying file position is still changed.

Because you have a POS specifier with the READ, each READ then requires the runtime to move backwards from the end of record to your next character of interest. The compiler's runtime is not correctly handling this.

But there is no need for you to do this constant jumping back and forwards in the file - instead, prior to reading reposition the file at its initial point (perhaps using REWIND) and then use non advancing input without the POS specifier to progressively step through the file character by character. You will need to appropriately handle the IOSTAT_EOR status code when the end of a particular record is reached.

Note also that for formatted stream, there is no guarantee (it is processor dependent) that all positive integers correspond to a valid position in the file (F2008 9.3.3.4p4 bullet 5, see also Note 9.10). This is to accommodate file structures that use more than one file position for things like the end of record marker (e.g. perhaps they use CR-LF).

(I note that you are also assuming that a default CHARACTER occupies one file storage unit - that is also processor dependent.)

program stream
  implicit none

  character(len=1)                :: text
  integer(kind=4), dimension(100) :: array
  integer(kind=4)                 :: status = 0, i

  open(  &
      unit=11,  &
      file='out.txt',  &
      form='formatted',  &
      access='stream',  &
      action='readwrite',  &
      status='replace' )

  array = [(i, i = 1,100)]           !  Populate vector from 1 to 100

  write (unit=11, fmt='(10i4)') array    !  Write array to file

  rewind 11
  do
    read (unit=11, fmt='(a1)', iostat=status, advance='no') text
    if (is_iostat_eor(status)) then
      write (*, fmt="()")
      cycle
    else if (status /= 0) then
      exit
    end if
    write (*,fmt='(a1)', advance='no') text  !  write results to screen to see what is in each position
  end do

  close(unit=11)

end program stream

Upvotes: 2

Related Questions