I'm working with a fortran program that reads a lot of data from a file and writes it back in a different format.
The code I'm using to read the data is this:
10 read(10,*,err=11,end=20) f,time(i),(a(i,j),j=1,14)
...
goto 10
11 i=i+1
goto 10
It works, but only when the input file is correct.
But some lines are like this:
"2014-04-28 07:51:18.9",2705,-8.42144,6.623851,0.4654102,20.99942,"NAN","NAN",0,0,0,0,-9.0605,5.8855,0.4135,21.39728
When this happens I lose every value in the line after the NAN. Is there a way to read the other values?
It's possible to read every value as a string and then convert them to doubles?
I know very little about fortran and I need to fix it quickly. Rewriting everything in C could take too much time.
Yes, you can read the entire line into a string. Then parse the string and replace the "NAN" with some special numeric value such as a large negative value. The intrinsic functions can help, e.g., index. Then use an "internal read" to read from the string into the numeric variables.
See: Reading comment lines correctly in an input file using Fortran 90, Reading format in Fortran 90 and Prevent FORTRAN from closing when a character is inputed instead of a number
I got it working.
Here is the code:
10 read(10,'(a)',err=16,end=20) linha
linha=trim(adjustl(linha))
pos1=1
n2=0
DO
pos2 = INDEX(linha(pos1:), ",")
IF (pos2 == 0) THEN
n2 = n2 + 1
strings(n2) = linha(pos1:)
EXIT
END IF
n2 = n2 + 1
strings(n2) = linha(pos1:pos1+pos2-2)
pos1 = pos2+pos1
END DO
f=strings(1)
read(strings(2),*) time(i)
j=1
11 read(strings(j+2), *,err=12) a(i,j)
j=j+1
IF (j > 14) THEN
goto 13
END IF
goto 11
12 a(i,j)=9999
j=j+1
goto 11
13 IF (a(i,6)==9999) THEN
goto 14
END IF
pp=1000.
c1=0.622
c2=1.-c1
rv=461.5
e=0.001*a(i,6)*rv*(a(i,4)+273.15)
a(i,6)=1000*e*c1/(100*pp-c2*e)
14 IF (a(i,5)==9999) THEN
goto 15
END IF
mimol=a(i,5)/44
a(i,5)=mimol*83.14*(a(i,4)+273.15)/pp
15 i=i+1
n=i-1
if (i.gt.nmax) goto 20
goto 10
16 i=i+1
goto 10
Thanks for the help.
Related
I am working on a program to calculate minimum nozzle length in supersonic nozzles (Method of Characteristics). I can't seem to figure out why my code won't write to my output file (the lines "write (6,1000)....). My code is below:
program tester
c----------------------------------------------------------------------c
implicit real (a-h,o-z)
integer count
real kminus(0:100),kplus(0:100),theta(0:100),nu(0:100),mach(0:100)
+ ,mu(0:100)
open (5,file='tester.in')
open (6,file='tester.out')
read (5,*) me
read (5,*) maxturn
read (5,*) nchar
read (5,*) theta0
close(5)
c.....set count to 0 and calculate dtheta
count=0
dtheta=(maxturn-theta0)/nchar
c.....first characteristic
do 10 an=1,nchar+1
count=count+1
c........these are already known
theta(count)=theta0+dtheta*(an-1)
nu(count)=theta(count)
c........trivial, but we will "calculate" them anyways
kminus(count)=2*theta(count)
kplus(count)=0
c........we feed it nu(count) and get m out
call pm_hall_approx(nu(count),m)
mach(count)=m
c........does not work with sqrt(...) for some reason
mu(count)=atan((1/(mach(count)**2)-1)**0.5)
write (6,1000) count,kminus(count),kplus(count),theta(count)
+ ,nu(count),mach(count),mu(count)
10 continue
c.....the other characteristics
do 30 bn=1,nchar
do 20 cn=1,(nchar+1-bn)
count=count+1
c...........these are given
kminus(count)=kminus(cn+bn)
kplus(count)=-1*kplus(bn+1)
c...........if this is the last point, copy the previous values
if (cn.eq.(nchar+1-bn)) then
kminus(count)=kminus(count-1)
kplus(count)=kplus(count-1)
endif
c...........calculate theta and nu
theta(count)=0.5*(kminus(count)+kplus(count))
nu(count)=0.5*(kminus(count)-kplus(count))
c...........calculate m
call pm_hall_approx(nu(count),m)
mach(count)=m
mu(count)=atan((1/(mach(count)**2)-1)**0.5)
write (6,1000) count,kminus(count),kplus(count),theta(count)
+ ,nu(count),mach(count),mu(count)
20 continue
30 continue
close(6)
stop
1000 format (11(1pe12.4))
end
c======================================================================c
include 'pm_hall_approx.f'
And my subroutine is given here:
subroutine pm_hall_approx(nu,mach)
c----------------------------------------------------------------------c
c Given a Mach number, use the Hall Approximation to calculate the
c Prandtl-Meyer Function.
c----------------------------------------------------------------------c
implicit real (a-h,o-z)
c.....set constants
parameter (a=1.3604,b=0.0962,c=-0.5127,d=-0.6722,e=-0.3278)
parameter (numax=2.2769)
y=nu/numax
mach=(1+a*y+b*y*y+c*y*y*y)/(1+d*y+e*y*y)
return
end
And here are the contents of tester.in
2.4 = mache
5.0 = maxturn
7 = nchar
0.375 = theta0
In fortran, unit number 6 is reserved for screen. Never use that as the unit number. Try changing it to some other number like write(7,1000) and then it should work.
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
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.
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.
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.