7

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?

tshepang
  • 12,111
  • 21
  • 91
  • 136
gluuke
  • 1,179
  • 6
  • 15
  • 22
  • 1
    The `i0` is not allowed for *reading* files in gfortran, but is certainly acceptable for *writing* files. Have you tried something like `i3` instead? – Kyle Kanos Jan 16 '14 at 18:52
  • Ok: thanks @Kyle. No, it doesn't work i3 : look at the last case: you will not correctly read the second element. – gluuke Jan 16 '14 at 18:54
  • ... and however I would like to find the most general solution possible. – gluuke Jan 16 '14 at 18:56
  • That is really odd because it worked on my gfortran 4.4 on my computer. I created a `(100,100,3)` array and did `write(10,*) ii,jj,real(ii+jj), real(ii-jj), real(jj-ii)`, rewound it, then read it via `read(10,'(i3,i3)',advance='no',iostat=ierr) ii,jj; read(10,*)aa(ii,jj,:)` and not a segfault or error reading – Kyle Kanos Jan 16 '14 at 19:04
  • @KyleKanos: thanks a lot, but the problem is that the input file has *not* been created by gfortran. – gluuke Jan 16 '14 at 19:11
  • 1
    Well then your option is to either use @AlexanderVogt's read the line as a string & repeat as necessary or use a different compiler. I changed up the code& wrote the file using `gcc` (`fprintf(fp,"%d %d %f %f %f\n",i,j,(double)(i+j),(double)(i-j),(double)(j-i));`) and read it in using `ifort` with zero problems using `read(10,'(i0,i0)',advance='no')`. – Kyle Kanos Jan 16 '14 at 19:18
  • 2
    @KyleKanos I0 in an input format specification is not standard F2008. – IanH Jan 16 '14 at 22:31

2 Answers2

3

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.

Alexander Vogt
  • 17,879
  • 13
  • 52
  • 68
3

[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.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • List directed input might be ok for internal purposes, where you have good control over the quality of input, but for general user supplied input it is seriously deficient in terms of validation and its otherwise "surprising" features. – IanH Jan 16 '14 at 22:11
  • Thanks. I have test the most simple `read(unitfile, *) ii, jj, aa(ii, jj, :)` with gfortran but i am not sure it is correctly working. – gluuke Jan 17 '14 at 15:10
  • @gluuke How large is the number of reals you want to read? Also, what size is `aa`? And, does it work/fail with a smaller case which you can paste here? – francescalus Jan 17 '14 at 15:22
  • @francescalus I have finally solved the problem using a buffer-like array as you suggested, with free format, and doing the assignation in the next step. The problem is also coming by the fact that I don't know how many records I have to read, i.e. I have to use `iostat` as well. – gluuke Jan 17 '14 at 16:34