idk1415
idk1415

Reputation: 55

EOF handling for reading raw binary files into an array in Fortran

I'm attempting to write Fortran code that reads a raw binary file [like a .png for example] in chunks of 64 bytes, performs some operations on it and then maybe writes it to another file. The code I have written so far is as follows, written with the help of this SO answer:

        integer(kind = int8), dimension(0:63) :: file_read_buffer
        integer :: input_file_handle
        integer :: io_error

        open(newunit=input_file_handle, file=input_file_name, status="old", access="stream", action="read", iostat=io_error)
        if(io_error /= 0) then
            ! file didn't open properly
        end if

        do
            read(unit = input_file_handle, iostat = io_error) file_read_buffer
            select case(io_error)
            case(0)
                ! consume buffer normally
            case(iostat_end)   
                ! do EOF processing
            case default
                ! error!
            end select    
        end do

If EOF is reached before the array is completely filled, is there any way to know how much of it was filled before EOF was reached? Also, if EOF is raised once, will further calls to read() also return EOF?

I'm using gfortran at the moment if that helps.

Upvotes: 3

Views: 309

Answers (1)

francescalus
francescalus

Reputation: 32366

You cannot determine how much was read by an input statement if an end-of-file condition is caused by that input statement.

However, as your intention is to use that input size to process just the part of the buffer that was filled, then that's not a problem: you cannot do that. That is, when an end-of-file condition is triggered your entire buffer becomes undefined.

Instead, you should throw away the entire buffer and reprocess the input. You have two options:

  • if you reach the end of file, reposition before it and read less data
  • determine how much data remains, and read less if less is available (avoiding the end-of-file condition)

For the first, if you keep track of your "successful" position, you can reposition on failure:

! Read full-sized chunks
do
  read(unit, iostat=iostat) buffer
  if (iostat==iostat_end) exit
  inquire (unit, pos=file_pos)
end do

! reposition
read (unit, pos=pos)

! Read smaller chunks
do
  read (unit, iostat=iostat) buffer(1)
  if (iostat==isotat_end) exit
end

(Processing goes in the obvious places.) This is similar to the idea present in this other answer for a related problem.

For the second, using the file position and its size we can see whether there are sufficient "file storage units" to fill our buffer:

inquire (unit, size=file_size)
do
  inquire (unit, pos=file_pos)
  remaining = (file_size-file_pos+1)*FILE_STORAGE_SIZE/STORAGE_SIZE(buffer)
  if (remaining<SIZE(buffer)) exit
  read (unit) buffer
end do
read (unit) buffer(:remaining)

FILE_STORAGE_SIZE tells us how many bits make up a file storage unit, and STORAGE_SIZE how many bits to store (in memory) an element of the array.

The second option is possibly fine, but isn't really safe in general: we can't be sure that an element of storage size 16-bits corresponds to 16 bits of file storage units. This may, though, be good enough for your purposes. And you can always create a test file to see how many file storage units your buffer occupies.

Upvotes: 2

Related Questions