Search code examples
fortrangfortranfortran90

Extract Specified Line from File Using Fortran


I am trying to write a function that extracts a specified line from a given file. My function to do so takes two arguments:

  1. fUnit: this is the numerical identifier of the given file.
  2. fLine: this is the line number that I'd like to extract. If the value of this input is -1, then the function will return the last line of the file (in my work, this is the functionality I need the most).

I have wrapped this function inside a module (routines.f95), as shown:

module routines

contains

function getLine(fUnit, fLine)

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ! Get the nth line of a file. It is assumed that the file is    !
    ! numerical only. The first argument is the unit number of the  !
    ! file, and the second number is the line number. If -1 is      !
    ! passed to the second argument, then the program returns the   !
    ! final line of the program. It is further assumed that each    !
    ! line of the file contains two elements.                       !
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    implicit none

    integer, intent(in) :: fUnit, fLine
    integer :: i
    real, dimension(2) :: tmp, getLine

    if (fline .eq. -1) then
        do
            read(fUnit, *, end=10) tmp
        end do
    else
        do i = 1, fLine
            read(fUnit, *, end=10) tmp
        end do
    end if

10  getLine = tmp

end function getLine

end module routines

To test this function, I set up the following main program (test.f95):

program test

use routines
implicit none

integer :: i
real, dimension(2) :: line

open(21, file = 'data.dat')

do i = 1, 5
    line = getLine(21, i)
    write(*, *) i, line
end do

close(21)
end program test

The file data.dat contains the following information:

1.0 1.00
2.0 0.50
3.0 0.33
4.0 0.25
5.0 0.20

This code is a simplified version of the one I've written, but it reflects all the errors I obtain in my primary code. When I compile the above code with the commands

gfortran -c routines.f95
gfortran -c test.f95
gfortran -o test test.o routines.o

I do not obtain any syntax errors. The output of the program gives the following:

       1   1.00000000       1.00000000    
       2   3.00000000      0.330000013    
       3   5.00000000      0.200000003    
At line 28 of file routines.f95 (unit = 21, file = 'data.dat')
Fortran runtime error: Sequential READ or WRITE not allowed after EOF marker, possibly use REWIND or BACKSPACE

Error termination. Backtrace:
#0  0x7f2425ea15cd in ???
#1  0x7f2425ea2115 in ???
#2  0x7f2425ea287a in ???
#3  0x7f242601294b in ???
#4  0x400ccb in ???
#5  0x4009f0 in ???
#6  0x400b32 in ???
#7  0x7f2425347f49 in ???
#8  0x400869 in ???
    at ../sysdeps/x86_64/start.S:120
#9  0xffffffffffffffff in ???

I understand that the error is being thrown because the program tries to extract a line that is past the EOF marker. The reason for this is because the program is skipping every other line, and thus skipping over the last line in the program.

Could someone please help me to understand why my program is skipping every other line of the input file? I am unable to find the issue in my code.


Solution

  • The position of a connected external file is a global state. In this case, the function getline changes the position of the file after it has searched. The next time the function is called, searching commences from the position it was left.

    What you see, then, is not so much "skipping" of lines, but:

    • in the first iteration, the first line is read;
    • in the second iteration, a line (the second) is skipped, then a line (the third) is read;
    • in the third iteration, two lines are skipped and a third is attempted to be read.

    However, the third line in the third iteration (the sixth of the file) is after an end-of-file condition. You see the result of reading the fifth line.

    To enable seeking as you desire it, ensure that you position the file at its initial point before skipping lines. The rewind statement places a connected file at its initial position.

    Instead of rewinding, you may close the file and re-open with position='rewind' to ensure it is positioned at its initial point, but the rewind statement is a better way to reposition. If you re-open without a position= specifier you see an effect similar to position='asis'. This leaves the position in the file unspecified by the Fortran standard.