user2667847
user2667847

Reputation:

How to read a text file into an array in Fortran

Is there any way to condense this code?

.
.
real*4 a4,e4,inc4,capom4,omega4,capm4
integer*2 id2
real*8 array(1e7,8)
.
.
row=0
do n=1,nleft+nbod2-1
 row=row+1
 read(iu) id2,a4,e4,inc4,capom4,omega4,capm4    
 array(row,1)=id2
 array(row,2)=a4
 array(row,3)=e4
 array(row,4)=inc4
 array(row,5)=capom4
 array(row,6)=omega4
 array(row,7)=capm4
end do
.
.

I've tried about five different ways starting with an implied DO loop and all result in "forrtl: severe (67): input statement requires too much data...".

Upvotes: 1

Views: 2312

Answers (3)

casey
casey

Reputation: 6915

The reason you cannot condense

 read(iu) id2,a4,e4,inc4,capom4,omega4,capm4    
 array(row,1)=id2
 array(row,2)=a4
 array(row,3)=e4
 array(row,4)=inc4
 array(row,5)=capom4
 array(row,6)=omega4
 array(row,7)=capm4

into

read(iu) (array(row,i), i=1:7)

is because this is causes a mismatch of your data file to your read arguments. In the source provided in the comments below you have these variables declared as:

real*4 ttmp,a4,e4,inc4,capom4,omega4,capm4
integer*2 nleft,nbod2,id2
real*8 array(1e7,8)

This means your read call read(iu) id2,a4,e4,inc4,capom4,omega4,capm4 is requesting 26 bytes (1*2 bytes + 6*4 bytes) and this corresponds to the record size in the unformatted file provided in the comments.

When you change the read to:

read(iu) (array(row,i), i=1:7)

which is completely valid on its own, it no longer matches the binary file and the read fails. This read requests 56 bytes (7*8 bytes) and produces the fatal runtime error you are reporting (you are requesting 56 bytes from a 26 byte record). The temporary variables used in the read are necessary so that you can read a 2 byte integer and 4 byte floats and then assign them to 8 byte real variables. The read cannot accomplish this directly because the underlying binary data in your file are not 8 byte reals.


So what can you do?

In your code you posted below your read is slightly different

  read(iu) id2,a4,e4,inc4,capom4,omega4,capm4

  array(row,1)=id2
  array(row,2)=ttmp
  array(row,3)=a4
  array(row,4)=e4
  array(row,5)=inc4
  array(row,6)=capom4
  array(row,7)=omega4
  array(row,8)=capm4

You could do away with the 6 named temporary variables and instead use an array of 6 reals for this. e.g.

real*4 :: readtemp(6)
.
.
do n=1,nleft+nbod2-1
  read(iu) id2,readtemp 
  array(n,1)=id2
  array(n,2)=ttmp
  array(n,3:8)= readtemp
end do

This lets you get rid of 6 individual reals in exchange for one array and condenses 6 of the assignments to just 1 assignment. Not a total collapse of the read/assign combo as in the other answer, but this does avoid needing to define a type to accomplish it.

Upvotes: 2

roygvib
roygvib

Reputation: 7395

Since the OP's code involves mixed data types including integer*2, direct use of read(iu) array( n, 1:7 ) seems not working. So I made another two trials to condense the code. The first one is

read(iu) id2, a4, e4, inc4, capom4, omega4, capm4
array( n, : ) = [ real*8 :: id2, a4, e4, inc4, capom4, omega4, capm4 ]

which saves some lines of code, but still a bit lengthy. So the second trial is

type dat_t
    sequence
    integer*2 :: id2
    real*4    :: a4, e4, inc4, capom4, omega4, capm4
endtype

type(dat_t) :: dat( 10000 )   !! or allocate() if necessary

and read the unformated file as

do n = 1, nleft + nbod2 - 1
    read(iu) dat( n )
enddo

The obtained data can be used as usual, e.g., dat( n )% a4. Here it is important to put the keyword sequence to ensure contiguous arrangement of variables in memory. Otherwise the same error occurs as in the OP's case...

I have tested this with a code below (using gfortran4.8 and ifort14.0)

program main
    integer, parameter :: dp = 8

    type dat_t
        sequence                                                                   
        character(5) :: s
        integer      :: k
        real(dp)     :: x, y
    endtype

    type(dat_t) :: d( 500 )
    real(dp)    :: a( 100 )

    !! write test data                                                              
    open( 10, file="test.dat", form="unformatted" )
    write( 10 ) "hello", 100, 777.0_dp, 888.0_dp
    close( 10 )

    !! read test data                                                               
    open( 20, file="test.dat", form="unformatted", status="old" )

    read( 20 ) d( 50 )
    print *, d( 50 )

    a( 1:3 ) = [ real(dp) :: d( 50 )% k, d( 50 )% x, d( 50 )% y ]
    print *, a( 1:3 )

    close( 20 )
end

Upvotes: 0

High Performance Mark
High Performance Mark

Reputation: 78364

You haven't really shown us enough to offer very good advice. Leaving that to one side, this might work:

Replace the block

row=0
do n=1,nleft+nbod2-1
 row=row+1
 read(iu) id2,a4,e4,inc4,capom4,omega4,capm4    
 array(row,1)=id2
 array(row,2)=a4
 array(row,3)=e4
 array(row,4)=inc4
 array(row,5)=capom4
 array(row,6)=omega4
 array(row,7)=capm4
end do

with

do n=1,nleft+nbod2-1
 read(iu) array(n,1:7)    
end do

In any further questions post exactly the code you are having trouble with, and exactly the errors reported by your compiler or at run-time.

Upvotes: 0

Related Questions