1

I am trying to read a text file using a Fortran code. I have a file with 1999 rows and the number of columns vary with each row. Can someone please tell me how one can code such a problem. This is my code for reading a 4*2 text file but I am using do loops which I can't use in my current case.

PROGRAM myread2
IMPLICIT NONE

  INTEGER, DIMENSION(100) :: a, b
  INTEGER :: row,col,max_rows,max_cols

  OPEN(UNIT=11, file='text.txt')

  DO row = 1,4
    READ(11,*) a(row), b(row)
  END DO
  PRINT *, a(1)
  PRINT *, a(4)
  PRINT*, b(4)
END PROGRAM myread2
Ian Bush
  • 6,996
  • 1
  • 21
  • 27
  • Why do you want to use a while loop? What features does a while loop give you that a normal do loop doesn't? Do you want to read data while there is some still to be read in the file? – Ian Bush Jul 26 '21 at 09:02
  • @IanBush I am trying to read a file with a 1999 rows and the number of columns varies. I am not sure how to use a do loop in this case and hence thought maybe while loop might solve my problem. – Timetraveler Jul 26 '21 at 09:05
  • 1
    I think you want to read each row as a string (or `character`) and then split using something like https://scivision.github.io/fortran2018-examples/sourcefile/split_string.f90.html and convert to integers with `iachar` – Ed Smith Jul 26 '21 at 09:34
  • 1
    That would be my guess from the description, except convert to integers via an internal file, which is the way to do it in Fortran – Ian Bush Jul 26 '21 at 10:00
  • Does this answer your question? [Reading an unknown size array from input file](https://stackoverflow.com/questions/49427519/reading-an-unknown-size-array-from-input-file) – veryreverie Jul 26 '21 at 13:37
  • 1
    @veryreverie That (seems to) assume the number of columns is the same in each row. This question explicitly says the number of columns varies. That's potentially much more tricky – Ian Bush Jul 27 '21 at 07:38

1 Answers1

1

The best way of reading a file like this depends on how you want to store the data. I'm going to use a ragged array as it's probably simplest, although other container types may be better suited depending on your requirements.

Fortran doesn't have ragged arrays natively, so first you need to define a type to hold each row. This can be done as

type :: RowData
  integer, allocatable :: cols(:)
end type

type(RowData), allocatable :: rows(:)

When this container is filled out, the value in the i'th column of the j'th row will be accessed as

value = rows(j)%cols(i)

We can then write a program to read the file, e.g.

type :: RowData
  integer, allocatable :: cols(:)
end type

type(RowData), allocatable :: rows(:)

integer :: no_rows
integer :: i

open(unit=11, file='text.txt')

no_rows = count_lines(11)
allocate(rows(no_rows))

do i=1,no_rows
  rows(i)%cols = read_row(11)
enddo

Now we just need to write the functions count_lines, which counts the number of lines in the file, and read_row, which reads a line from the file and returns the contents of that line as an array of integers.

Following this question, count_lines can be written as

! Takes a file unit, and returns the number of lines in the file.
! N.B. the file must be at the start of the file.
function count_lines(file_unit) result(output)
  integer, intent(in) :: file_unit
  integer :: output
  
  integer :: iostat
  
  output = 0
  iostat = 0
  do while (iostat==0)
    read(file_unit, *, iostat=iostat)
    if (iostat==0) then
      output = output+1
    endif
  enddo
  rewind(file_unit)
end function

Writing read_row, to parse a line of unknown length from a file, can be done by following this question.

veryreverie
  • 2,871
  • 2
  • 13
  • 26