Number of lines of a text file - fortran

I'm trying to make a function that takes in a file name (i.e. "data.txt") and produces the number of lines of that file.
data.txt :
24 42
45 54
67 76
89 98
12 21
99 99
33 33
The below piece of code is my attempt to build a function that takes in the filename "data.txt" and produces the number of lines of the file. The first part (lines 1-12) of the code is defining the function and the second part (lines 14-19) is where I call the function in a program. The output of the below code is 1 rarther than 7 (look up - data.txt has 7 lines).
function count_lines(filename) result(nlines)
character :: filename
integer :: nlines
open(10,file=filename)
nlines = 0
do
read(10,*,iostat=io)
nlines = nlines + 1
if (io/=0) exit
end do
close(10)
end function count_lines
program myread
integer :: count_lines
print *, count_lines("data.txt")
end program myread
Attempt a debuging:
I think it has something to do with the do loop not working, but I'm unsure. I have changed the if statement to if (io>0) and this gives an output of 2, which I don't understand. At present I'm not getting an error outputs when I compile just the wrong output.

filename (the dummy argument) is only one character long. Anything might happen if you provide a longer one (usually it is capped, though).
This does not result in an error as you did not specify the status of the file, or even checked whether the operation was successful (In fact, you just created a new file d).
You can avoid this by using an assumed length string:
function count_lines(filename) result(nlines)
character(len=*) :: filename
! ...
open(10,file=filename, iostat=io, status='old')
if (io/=0) stop 'Cannot open file! '
function count_lines(filename) result(nlines)
implicit none
character(len=*) :: filename
integer :: nlines
integer :: io
open(10,file=filename, iostat=io, status='old')
if (io/=0) stop 'Cannot open file! '
nlines = 0
do
read(10,*,iostat=io)
if (io/=0) exit
nlines = nlines + 1
end do
close(10)
end function count_lines
[Increment after the check to ensure a correct line count.]
For the second part of your question:
Positive (non-zero) error-codes correspond to any error other than IOSTAT_END (end-of-file) or IOSTAT_EOR (end-of-record). After reading in the (new) file in the first round of the loop (io==IOSTAT_END, I checked with my compiler), you are trying to read beyond the end... This results in a positive error. Since the increment occurs before you exit the loop, the final value is 2.

Related

Extract Specified Line from File Using Fortran

I am trying to write a function that extracts a specified line from a given file. My function to do so takes two arguments:
fUnit: this is the numerical identifier of the given file.
fLine: this is the line number that I'd like to extract. If the value of this input is -1, then the function will return the last line of the file (in my work, this is the functionality I need the most).
I have wrapped this function inside a module (routines.f95), as shown:
module routines
contains
function getLine(fUnit, fLine)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Get the nth line of a file. It is assumed that the file is !
! numerical only. The first argument is the unit number of the !
! file, and the second number is the line number. If -1 is !
! passed to the second argument, then the program returns the !
! final line of the program. It is further assumed that each !
! line of the file contains two elements. !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
implicit none
integer, intent(in) :: fUnit, fLine
integer :: i
real, dimension(2) :: tmp, getLine
if (fline .eq. -1) then
do
read(fUnit, *, end=10) tmp
end do
else
do i = 1, fLine
read(fUnit, *, end=10) tmp
end do
end if
10 getLine = tmp
end function getLine
end module routines
To test this function, I set up the following main program (test.f95):
program test
use routines
implicit none
integer :: i
real, dimension(2) :: line
open(21, file = 'data.dat')
do i = 1, 5
line = getLine(21, i)
write(*, *) i, line
end do
close(21)
end program test
The file data.dat contains the following information:
1.0 1.00
2.0 0.50
3.0 0.33
4.0 0.25
5.0 0.20
This code is a simplified version of the one I've written, but it reflects all the errors I obtain in my primary code. When I compile the above code with the commands
gfortran -c routines.f95
gfortran -c test.f95
gfortran -o test test.o routines.o
I do not obtain any syntax errors. The output of the program gives the following:
1 1.00000000 1.00000000
2 3.00000000 0.330000013
3 5.00000000 0.200000003
At line 28 of file routines.f95 (unit = 21, file = 'data.dat')
Fortran runtime error: Sequential READ or WRITE not allowed after EOF marker, possibly use REWIND or BACKSPACE
Error termination. Backtrace:
#0 0x7f2425ea15cd in ???
#1 0x7f2425ea2115 in ???
#2 0x7f2425ea287a in ???
#3 0x7f242601294b in ???
#4 0x400ccb in ???
#5 0x4009f0 in ???
#6 0x400b32 in ???
#7 0x7f2425347f49 in ???
#8 0x400869 in ???
at ../sysdeps/x86_64/start.S:120
#9 0xffffffffffffffff in ???
I understand that the error is being thrown because the program tries to extract a line that is past the EOF marker. The reason for this is because the program is skipping every other line, and thus skipping over the last line in the program.
Could someone please help me to understand why my program is skipping every other line of the input file? I am unable to find the issue in my code.
The position of a connected external file is a global state. In this case, the function getline changes the position of the file after it has searched. The next time the function is called, searching commences from the position it was left.
What you see, then, is not so much "skipping" of lines, but:
in the first iteration, the first line is read;
in the second iteration, a line (the second) is skipped, then a line (the third) is read;
in the third iteration, two lines are skipped and a third is attempted to be read.
However, the third line in the third iteration (the sixth of the file) is after an end-of-file condition. You see the result of reading the fifth line.
To enable seeking as you desire it, ensure that you position the file at its initial point before skipping lines. The rewind statement places a connected file at its initial position.
Instead of rewinding, you may close the file and re-open with position='rewind' to ensure it is positioned at its initial point, but the rewind statement is a better way to reposition. If you re-open without a position= specifier you see an effect similar to position='asis'. This leaves the position in the file unspecified by the Fortran standard.
After the help from #francescalus, I can answer my own question. The issue with my code was that each time my main program iterated through the function, the position of the read statement picked up at the last location. Because of this, my program skipped lines. Here is my updated code:
test.f95
program test
use routines
implicit none
integer :: i
real, dimension(2) :: line
open(21, file = 'data.dat')
do i = 1, 5
line = getLine(21, i)
write(*, *) i, line
end do
line = getLine(21, -1)
write(*, *) -1, line
close(21)
end program test
routines.f95
module routines
contains
function getLine(fUnit, fLine)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Get the nth line of a file. It is assumed that the file is !
! numerical only. The first argument is the unit number of the !
! file, and the second number is the line number. If -1 is !
! passed to the second argument, then the program returns the !
! final line of the program. It is further assumed that each !
! line of the file contains two elements. !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
implicit none
integer, intent(in) :: fUnit, fLine
integer :: i
real, dimension(2) :: tmp, getLine
rewind(fUnit)
if (fline .eq. -1) then
do
read(fUnit, *, end=10) tmp
end do
else
do i = 1, fLine
read(fUnit, *, end=10) tmp
end do
end if
10 getLine = tmp
end function getLine
end module routines
data.dat
1.0 1.00
2.0 0.50
3.0 0.33
4.0 0.25
5.0 0.20
Compile with
gfortran -c routines.f95
gfortran -c test.f95
gfortran -o test test.o routines.o
The output of this program is
1 1.00000000 1.00000000
2 2.00000000 0.500000000
3 3.00000000 0.330000013
4 4.00000000 0.250000000
5 5.00000000 0.200000003
-1 5.00000000 0.200000003

How to read data from multiple files and write into columns of a single file

I'm completely new in Fortran and I need to write some relatively simple codes.
I have some files (various, for example 200 files); each file for the specific node, with some simplification, each file contains:
T(i), X(i)
these are my input and I want to have an output file contain:
T(i) X(i)1 X(i)2 ... X(i)n
the problem is that I can't separate data in different columns of output file, they comes all after each other in 1 column.
My code is :
PROGRAM Output
implicit none
integer ::nn,n,i,j,l
real,dimension(:),allocatable::t,x,y,z
character(len=10)::TD
open(11,file='outputX.txt')
allocate (t(1000),x(1000),y(1000),z(1000))
n=5 ! Number of Files
nn=50 ! Number of Rows in each File
Do i=1,n ! loop for opening different files
write(TD,10)i
write(*,*)TD
open(1,file=TD)
Do l=1,nn ! loop for reading rows in each file
read(1,*)t(j),x(j)
write(11,*)x(j) !!!! This is my PROBLEM, all the data shows in
! one column, I want each file in separately
Enddo
Enddo
10 format('100',i3.3,'')
deallocate(x,y,z,t)
END PROGRAM Output
The output I get is like this :
11
12
13
21
22
23
31
32
33
But in fact I want :
11 21 31
12 22 32
13 23 33
There are several problems with your code
Do i=1,n ! loop for opening different files
write(TD,10)i
write(*,*)TD
open(1,file=TD)
Do l=1,nn ! loop for reading rows in each file
read(1,*)t(j),x(j)
write(11,*)x(j) !!!! This is my PROBLEM, all the data shows in
! one column, I want each file in separately
End do
End do
The index j is completely undefined. You should put j=1 or 0 and j=j+1 somewhere inside the loop.
The other issue is your output. You are reading the files in sequence. It is very hard to print each file into a separate column. A separate row for each file is easy:
write(11,*) x(1:nn)
after the inner loop.
Or with finer control and avoiding a line wrap
write(11,'999(g0,1x)') x(1:nn)
(g0 is a general edit descriptor which uses only the necessary width). This will only work if you fix the j issue I mentioned above!
To put it into separate columns you must
Open all files at the same time, then read from each of them and print the read data in a single write command.
or
Store all the data from all files into separate columns in a 2D array and print the 2D array afterwards.
So what I got is x(1) belongs to 11, x(2) belongs to 12, x(3) belongs to 13 and so on, isn't it? You could try this one:
write(11,100) x(j), x(j+3), x(j+6)
100 format(1X,11F20.4,11F20.4,11F20.4)
The 2D array... for this problem it is backwards
But it should give you some help... (Hopefully)
PROGRAM Output
IMPLICIT NONE
INTEGER, PARAMETER :: nfiles = 5
INTEGER, PARAMETER :: nrows = 50
integer :: iFile, iRow !File and row counter/#
real,dimension(:,:),allocatable :: t,x,y,z !The are now 2D
INTEGER,dimension(nFiles) :: lFile !logical file indexed by file#
LOGICAL,dimension(nFiles) :: Open4Biz !logical for closing
character(len=10)::TD !Unsure about this
open(11,file='outputX.txt')
allocate (t(iRow,iFile),x(iRow,iFile),y(iRow,iFile),z(iRow,iFile))
Open4Biz(:) = .FALSE. !Initialize
!n=5 ! Number of Files ^^Moved up/renamed^^
!nn=50 ! Number of Rows in each File ^^Moved up/renamed^^
Files_Loop1: Do iFile = 1, nFiles !I think that the loop identifier is std f95 (std ifort anyhow)
write(TD,10) iFile !Unsure about this
write(*,*)TD
lFile(iFile) = 10+iFile
open(lFile(iFile),file=TD) !Probably put in some logic if the file is not found
Open4Biz(iFile) = .TRUE.
ENDDO Files_Loop1
Rows_Loop: Do iRow = 1, nn !The loops are backwards from normal
Files_Loop2: Do iFile = 1, nFiles
read(lFile(iFile),*) t(iRow, iFile), x(iRow, iFile)
Enddo Files_Loop2
write(11,*) x(iRow,:)
Enddo Rows_Loop
10 format('100',i3.3,'')
667 CONTINUE !This is label to 'jump to' from a bad open
Files_Loop3: Do iFile = 1, nFiles
IF(Open4Biz(iFile) CLOSE(lFile(iFile))
ENDDO Files_Open_Loop
IF(ALLOCATED(X)) deallocate(x)
IF(ALLOCATED(Y)) deallocate(y)
IF(ALLOCATED(Z)) deallocate(z)
IF(ALLOCATED(T)) deallocate(t)
END PROGRAM Output

End of record error in file opening

I am currently writing a code to simulate particle collisions. I am trying to open as much files as there are particles (N) and then put the data for positions and velocities in each of these files for each step of the time integration (using Euler's method, but that is not relevant). For that, I tried using a do loop so it will open all the files I need - then I put all the data in them with a different do loop later - and then close them all.
I first tried just doing a do loop to open the files - but it gave errors of the type "file already open in another unit", so I did the following:
module parameters
implicit none
character :: posvel
integer :: i, j, N
real :: tmax
real, parameter :: tmin=0.0, pi=3.14159265, k=500.0*10E3, dt=10.0E-5, dx=10.0E-3, g=9.806, ro=1.5*10E3
real, dimension(:), allocatable :: xold, xnew, vold, vnew, m, F, r
end module parameters
PROGRAM Collision
use parameters
implicit none
write(*,*) 'Enter total number of particles (integer number):'
read(*,*) N
allocate(xold(N))
allocate(vold(N))
allocate(xnew(N))
allocate(vnew(N))
allocate(m(N))
allocate(F(N))
allocate(r(N))
xold(1) = 0.0
vold(1) = 0.0
m(1) = 6.283*10E-9
r(1) = 10E-4
xold(2) = 5.0
vold(2) = 0.0
m(2) = 6.283*10E-9
r(2) = 10E-4
write(*,*) 'Type total time elapsed for the simulation(real number):'
read(*,*) tmax
do i = 1, N
write(posvel,"(a,i3.3,a)") "posveldata",i,".txt"
open(unit=i,file=posvel, status="unknown")
end do
do i = 1, N
close(unit=i)
end do
END PROGRAM Collision
The last ten lines are the ones that regard to my problem.
That worked in codeblocks - it opened just the number of files I needed, but I'm actually using gfortran and it gives me and "end of record" error in the write statement.
How can I make it to execute properly and give me the N different files that I need?
P.S.: It is not a problem of compilation, but after I execute the program.
Your character string in the parameter module has only 1 character length, so it cannot contain the full file name. So please use a longer string, for example
character(100) :: posvel
Then you can open each file as
do i = 1, N
write(posvel,"(a,i0,a)") "posveldata",i,".txt"
open(unit=i,file=trim(posvel), status="unknown")
end do
Here, I have used the format i0 to automatically determine a proper width for integer, and trim() for removing unnecessary blanks in the file name (though they may not be necessary). The write statement can also be written more compactly as
write(posvel,"('posveldata',i0,'.txt')") i
by embedding character literals into the format specification.
The error message "End of record" comes from the above issue. This can be confirmed by the following code
character c
write(c,"(a)") "1"
print *, "c = ", c
write(c,"(a)") "23" !! line 8 in test.f90
print *, "c = ", c
for which gfortran gives
c = 1
At line 8 of file test.f90
Fortran runtime error: End of record
This means that while c is used as an internal file, this "file" does not have enough space to accommodate two characters (here "23"). For comparison, ifort14 gives
c = 1
forrtl: severe (66): output statement overflows record, unit -5, file Internal Formatted Write
while Oracle Fortran12 gives
c = 1
****** FORTRAN RUN-TIME SYSTEM ******
Error 1010: record too long
Location: the WRITE statement at line 8 of "test.f90"
Aborted
(It is interesting that Oracle Fortran reports the record to be "too long", which may refer to the input string.)

Reading a sequence of integer in a line with unknown bound in fortran

I would like to read a sequence of integer in a line with unknown bound in FORTRAN. My question is similar to the following previous post,
Reading a file of lists of integers in Fortran
however I want to read a sequence of unknown numbers of integer in a line and save it in separate arrays. And successive lines of integer should be saved to some other array
My file looks like this
5 7 8 9 10 13 # should be stored f(1)(6) arrays
93 102 92 # c(1)(3)
105 107 110 145 147 112 # f(2)(6)
97 98 # b(1)(2)
12 54 55 # c(2)(3)
15 17 21 23 45 # e(1)(5)
43 47 48 51 62 # d(1)(4)
Thus I have a sequence of integers with maximum length of 6 (to be stored in f array) and minimum length of 2(to be stored in b array). I have hundreds of lines like this, such that I need to classify according to the maximum length and count on them.
Reading a file of lists of integers in Fortran
There are probably many ways to do this, and the following is one such example. Here, the split() makes multiple trials for list-directed input for all values in the line, until non-numeric characters or the end of line is encountered.
subroutine split( line, vals, n )
implicit none
character(*), intent(in) :: line
real*8 :: vals(*), buf( 100 ) !! currently up to 100 items (for test)
integer :: n
n = 1
do
read( line, *, end=100, err=100 ) buf( 1 : n ) !! (See Appendix for why buf is used here)
vals( 1:n ) = buf( 1:n )
n = n + 1
enddo
100 continue
n = n - 1
end
program main
implicit none
character(200) :: line
real*8 :: vals( 100 )
integer :: n
open( 10, file="test.dat", status="old" )
do
read( 10, "(a)", end=500 ) line
call split( line, vals, n )
if ( n == 0 ) then
print *, "comment line"
else
print *, nint( vals( 1 : n ) )
endif
enddo
500 continue
close( 10 )
end
If test.dat contains the whole lines in the Question, plus the following lines
# additional data
1,2,3 , 4 , 5 # comma-separated integers
1.23e2 -4.56e2 , 777 # integer/floating-point mixed case
it gives
comment line
5 7 8 9 10 13
93 102 92
105 107 110 145 147 112
97 98
12 54 55
15 17 21 23 45
43 47 48 51 62
comment line
1 2 3 4 5
123 -456 777
So one can save the result for each line by copying the values in vals(1:n) to a desired array.
[ Appendix (thanks to #francescalus) ]
In the above code, the data are read once into buf(1:n) and then copied to vals(1:n). One might think that it would be more direct to read in the data into vals(1:n) such that
read( line, *, end=100, err=100 ) vals( 1 : n )
However, this direct approach is not recommended because vals(1:n) becomes undefined when the read statement hits the "end" or "err" condition. Although ifort and gfortran seem to retain the data in vals(1:n) even when that condition is met (and so they work even with the direct approach), the same behavior cannot be guaranteed for other compilers. In contrast, the buffer approach avoids this risk by saving the data one step before to vals(1:n), so that undefined data are not used. This is why the buffer approach is used in the above code despite it is one statement longer.
Something like this might satisfy your requirements
INTEGER :: ix, rdstat
INTEGER, DIMENSION(6) :: nums
CHARACTER(len=128) :: aline
...
OPEN(21,file='data.txt')
DO ix = 1,10
READ(21,'(a)',iostat=rdstat) aline
IF (rdstat/=0) catch_errors()
nums = -99 ! a guard
READ(aline,*,iostat=rdstat) nums
IF (rdstat/=0) catch_errors()
! do something with nums
END DO
CLOSE(21)
I haven't thoroughly tested this and I haven't written catch_errors for you -- in practice you may not want to do very much. This first version is probably too brittle, but whether it's suitable or not depends heavily on the uniformity (or otherwise) of the input files.
The strategy is to read each line into a character variable (one long enough for the entire line) and then to use internal, list-directed, reading to read 6 integers from the start of the character variable. This takes advantage of the in-built facility that list-directed input has of finding integers in an input stream with spaces separating values. The approach should work as well with a comma-separated list of integers. The internal read only looks for 6 integers, then catches the error when either no more integers are found, or only material which cannot be interpreted as integers (such as strings like # comment).
Note
I've assumed a maximum line length of 128 characters, you may want to adjust that.
I've specified a fixed upper limit on the number of lines the program will read. You may want to change that, or change to a do/while loop.
The program expects no more than 6 integers in a line, as your question specifies.
At each line read the array nums is filled with -99 at each element; this is a 'guard'. If -99 is likely to occur in your input files you may want to change this.
It's entirely up to you what you do with the numbers in nums once you have them.

Reading formatted data - Fortran runtime error: Bad real number

I am trying to use the code below to read a formatted file and write it into another. However, on running it shows the following error
$ ./conv.sac.farm < i_conv.farm
# stn comp Delta Tr-time Start in record
At line 54 of file Main/conv.sac.farm.f (unit = 5, file = 'stdin')
Fortran runtime error: Bad real number in item 1 of list input
The source code is as follows
PARAMETER (nd0=100000,pi=3.1415926)
IMPLICIT COMPLEX*8 (Z)
CHARACTER name*6,comp*6,fname*60,event*20
- ,cmp(0:3)*5,fname0*60,charac*15,scode*60
REAL*8 GFACT(500),PP0(500),depth0
integer hr0,mnu0,yr,month,day,hr,mnu
REAL x(nd0),y(nd0)
DIMENSION Z(nd0),zpole(50),zero(50)
data np,cmp/8,'disp.','vel. ','acc. ','orig.'/
common /tbl/ip(110,14),is(110,14),secp(110,14),secs(110,14)
read(5,'(a)') event
read(5,*) alats,alons,depth,hr0,mnu0,sec0,id,delmin,delmax
depth0=depth
write(22,'(a,a5,3f7.2,2i3,f6.2)')
# event,cmp(id),alats,alons,depth,hr0,mnu0,sec0
* << J-B travel time table >>
OPEN(11,FILE='jb.ptime')
OPEN(12,FILE='jb.stime')
1000 read(11,*,end=1001) n,(ip(n,i),secp(n,i),i=1,14)
goto 1000
1001 read(12,*,end=1002) n,(is(n,i),secs(n,i),i=1,14)
goto 1001
1002 continue
close(11)
close(12)
* << Geometrical factor >>
OPEN(15,FILE='jb.table')
CALL GEOM(GFACT,PP0,depth0)
close(15)
nstn=0
print *,' # stn comp Delta Tr-time Start in record'
5 read(5,'(a)') fname
read(5,'(a)') scode
* ta=advance of start-time relative the standard P/S arrival
* du=duration
c
if(fname.eq.'dummy') goto 90
read(5,*) ta,du,dt,f1,f2,iph,nr,iuni
open(1,file=fname)
READ(1,'(g15.7)') dt0
read(1,'(/////5g15.7)') dum, alat, alon, elev
read(1,'(///////5i10)') yr, nday, hr,mnu, nsec
read(1,'(5i10)') nmsec,ndum,ndum,ndum,nd
read(1,'(/////)')
read(1,'(a6,2x,a13)') name,charac
read(1,'(////)')
And so on..
Line 54 is
read(5,*) ta,du,dt,f1,f2,iph,nr,iuni
I found a similar question following this link
Fortran runtime error: Bad real number
However, if I understand correctly, the corrections mentioned were pertaining to reading unformatted data. Despite this, I tried and failed as expected, given that the file I am trying to read is formatted.
here is a little trick if you can't readily find the offending line in the data file:
replace your read that throws the error with this:
read(5,'(a)')line
read(line,*,iostat=ios) ta,du,dt,f1,f2,iph,nr,iuni
if(ios>0)then
write(*,*)'error reading line:',line
stop
endif
with the declarations
integer ios
character*(200) line
Probably just do that for debugging then revert to the original once you fix the problem.