Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read free format with no advance

In a given file record, I need to read the first two integer elements at first, and then the rest of the line (a large number of real elements), because the assignment depend on the first 2. Suppose the format of the first two integer elements is not really well defined.

The best way to solve the problem could be something:

read(unitfile, "(I0,I0)", advance='no') ii, jj
read(unitfile,*) aa(ii,jj,:)

But it seems to me the "(I0)" specification is not allowed in gfortran.

Basically the file read in unitfile could be something like:

0   0    <floats>
0   10   <floats>
10   0    <floats>
100   0    <floats>
100   100    <floats>

which is hard to be read with any fortran-like fixed field format specification.

Is there any other way to get around this, apparently trivial, problem?

like image 820
gluuke Avatar asked Jan 16 '14 18:01

gluuke


2 Answers

This applies string manipulations to get the individual components, separated by blanks ' ' and/or tabs (char(9)):

program test
  implicit none
  character(len=256) :: string, substring
  integer            :: ii, jj, unitfile, stat, posBT(2), pos
  real, allocatable  :: a(:)

  open(file='in.txt', newunit=unitfile, status='old' )
  read(unitfile,'(a)') string

  ! Crop whitespaces
  string = adjustl(trim(string))

  ! Get first part:
  posBT(1) = index(string,' ')      ! Blank
  posBT(2) = index(string,char(9))  ! Tab
  pos = minval( posBT, posBT > 0 )

  substring = string(1:pos)
  string = adjustl(string(pos+1:))
  read(substring,*) ii

  ! Get second part:
  posBT(1) = index(string,' ')      ! Blank
  posBT(2) = index(string,char(9))  ! Tab
  pos = minval( posBT, posBT > 0 )

  substring = string(1:pos)
  string = adjustl(string(pos+1:))
  read(substring,*) jj

  ! Do stuff
  allocate( a(ii+jj), stat=stat )
  if (stat/=0) stop 'Cannot allocate memory'

  read(string,*) a

  print *,a

  ! Clean-up
  close(unitfile)
  deallocate(a)
end program

For a file in.txt like:

1 2 3.0 4.0 5.0

This results in

./a.out 
   3.00000000       4.00000000       5.00000000

NOTE: This is just a quick&dirty example, adjust it to your needs.

like image 117
Alexander Vogt Avatar answered Nov 26 '22 09:11

Alexander Vogt


[This answer has been significantly revised: the original was unsafe. Thanks to IanH for pointing that out.]

I generally try to avoid doing formatted input which isn't list-directed, when I can afford it. There's already an answer with string parsing for great generality, but I'll offer some suggestions for a simpler setting.

When you are relaxed about trusting the input, such as when it's just the formatting that's a bit tricky (or you 're happy leaving it to your compiler's bounds checking), you can approach your example case with

read(unitfile, *) ii, jj, aa(ii, jj, :)

Alternatively, if the array section is more complicated than given directly by the first two columns, it can be by an expression, or even by functions

read(unitfile, *) ii, jj, aa(fi(ii,jj), fj(ii,jj), :fn(ii,jj))

with pure integer function fi(ii,jj) etc. There is even some possibility of having range validation in those functions (returning a size 0 section, for example).

In a more general case, but staying list-directed, one could use a buffer for the real variables

read(unitfile, *) ii, jj, buffer(:) ! Or ... buffer(:fn(ii,jj))
! Validate ii and jj before attempting to access aa with them
aa(.., .., :) = buffer

where buffer is of suitable size.

Your first considered approach suggests you have some reasonable idea of the structure of the lines, including length, but when the number of reals is unknown from ii and jj, or when the type (and polymorphism reading isn't allowed) is not known, then things do indeed get tricky. Also, if one is very sensitive about validating input, or even providing meaningful detailed user feedback on error, this is not optimal.

Finally, iostat helps.

like image 25
francescalus Avatar answered Nov 26 '22 08:11

francescalus