Writing specific parts of the file to another file - fortran

I am writing a code to read huge file (File XYZ) which has a couple of objects (say A,B,C,D,E... there might be more and each object has hundreds of data files). Now I need to read the file into Fortran and select an object for example 'object B' and write all the information of 'object B' into a new file which I create using the fortran code.
Each object (from A-....) had information consisting of header files and an array of data (a matrix(i,j) where i varies for each file and j=4 ). I have to read in both the header and array data into the new file and these objects (A-....) are not ordered sequentially (suppose after A may be C or D).
I have written a small code but it doesn't seem to work well as I am either able to output only the header but not the array or nothing at all attaching a data file for an object and code.
program trail
implicit none
CHARACTER (LEN=5) :: thumb='3C286', SOURCE
INTEGER :: pair(4), n,io, allocstat
real, dimension(:,:),allocatable:: a
CHARACTER (LEN=20) :: POLCODE, DEC, RA
CHARACTER (LEN=40) :: TELESCOPE, NAME_MY, DATA_FILENAME, object , date, TEXT, HEADER, restfreq,BANDWIDT, TSYS, OBSTIME, ANGLE
character(len=180) :: Outdat
character(len=75) :: ra_1, residu, flux, vlafit
open(2, FILE='trail9.txt',POSITION='APPEND')
do
open(1, File="column_3C286_iteration1.txt",status='old' )
read(1,*), SOURCE
10 if(Source.eq.'OJ287') then
! READING THE DATA
read(1,'(A40)') DATA_FILENAME
read(1,'(A40)') TELESCOPE
read(1,'(A40)') NAME_MY
read(1,'(A40)') data_filename
read(1,'(A40)') OBJECT
read(1,'(A40)') restfreq
read(1,'(A20)') polcode
read(1,'(A40)') angle
read(1,'(A40)') date
read(1,'(A40)') bandwidt
read(1,'(A40)') tsys
read(1,'(A40)') obstime
read(1,'(A20)') ra
read(1,'(A20)') dec
read(1,'(A40)') text
read(1,'(A40)') header
read(1,'(A180)') OUTDAT
read(1,"(T2, A3,T30,A4, T50,A6,T67,A6)"),ra_1, residu, flux, vlafit
n=0
do
read(1,*,iostat=io) pair
if (io/=0) exit
n = n + 1
end do
allocate(a(n,4),stat=allocstat)
do i=1,n
read(1,*) (a(i,j),j=1,4 )
enddo
if (index(data_filename,'#')>0 ) then
!WRITING THE DATA
write(2,'(A40)') DATA_FILENAME
write(2,'(A40)') TELESCOPE
write(2,'(A40)') NAME_MY
write(2,'(A40)') data_filename
write(2,'(A40)') OBJECT
write(2,'(A40)') restfreq
write(2,'(A20)') polcode
write(2,'(A40)') angle
write(2,'(A40)') date
write(2,'(A40)') bandwidt
write(2,'(A40)') tsys
write(2,'(A40)') obstime
write(2,'(A20)') ra
write(2,'(A20)') dec
write(2,'(A40)') text
write(2,'(A40)') header
WRITE(2,'(A180)') OUTDAT
Write(2,"(T2, A3,T30,A4, T50,A6,T67,A6)"),ra_1, residu, flux, vlafit
do i=1,n
write(2,"(T1,F10.4: T27,F10.4,T47, F10.4, T63,F10.4, T75,F10.4 )"), (a(i,j), j=1,4)
end do
end do
end if
end do
end program trail

Please try changing the type of pair from integer to real and modify the code as
real :: pair(4)
integer :: i
n = 0
do
read(1,*,iostat=io) pair
if (io/=0) exit
n = n + 1
end do
do i = 1, n + 1
backspace( 1 )
enddo
In this way, the read statement counts the lines including four floating-point numbers and stops reading when non-numeric characters (such as #) are found in the line. Please note also that backspace() is performed n+1 times so that the file pointer goes back to the top of the current data section.
Although not very sure, I also think that the following open statement
open(1, File="column_3C286_iteration1.txt",status='old' )
should be written before the outermost DO loop if this corresponds to the data file.
To summarize, I suggest the following modification:
real :: pair(4)
integer :: i
open(1, File="column_3C286_iteration1.txt",status='old' )
do
read(1,*,end=5000), SOURCE
if( Source .eq.'OJ287' ) then
! read the remaining header and data lines in one block.
read(1,'(A40)') DATA_FILENAME
read(1,'(A40)') TELESCOPE
...
read(1,"(T2, A3,T30,A4, T50,A6,T67,A6)"),ra_1, residu, flux, vlafit
n = 0
do
read(1,*,iostat=io) pair
if (io/=0) exit
n = n + 1
end do
do i = 1, n + 1
backspace( 1 )
enddo
if ( allocated( a ) ) deallocate( a )
allocate( a( n, 4 ) )
do i = 1, n
read( 1, * ) a( i, 1:4 )
enddo
!! write out the obtained data to file 2.
endif
enddo
5000 continue
Edit
If we simply want to copy one block of "header + data" to a different file, it is more straight-forward to echo all the lines in the block without formatting. For example, the program may look like this.
character(200) :: str, field
integer :: ios
real :: dum
open( 10, file="input.dat", status="old" )
do
read( 10, "(a)", end=5000 ) str
if ( trim(str) == "OJ287" ) then !! search for the target block
print "(a)", trim(str) !! print the tag
!! read one block.
do
read( 10, "(a)", end=5000 ) str
read( str, * ) field !! get the first field of a line
if ( field(1:1) == "#" .or. field(1:2) == "ra" ) then
print "(a)", trim(str) !! header part
else
read( str, *, iostat=ios ) dum !! test if this line starts with a number
if ( ios == 0 ) then
print "(a)", trim(str) !! data part
else
backspace( 10 )
exit
endif
endif
enddo
endif
enddo
5000 continue

Related

Reading three specific numbers in a text file and writing them out

I have a problem how to print only specific three numbers, which are in a file with no format. I have no idea how to read it and print because if I use read from higher i, it does not start reading e.g. for i = 4, the line 4. I need only numbers 88.98, 65.50, and 30.
text
678 people
450 girls
22 old people
0 cats
0 dogs
4 girls blond
1 boy blond
1 old man
0 88.9814 xo xi
0 65.508 yo yi
0 30 zo zi
I tried this, but this is not working at all.
program souradnice
implicit none
integer :: i, k
character*100 :: yo, zo, line, name, text
real :: xo
open(10,file="text.dat", status='old')
do i=20,20
read(10,fmt='(a)') line
read(unit=line, fmt='(a100)') text
if(name=="xo") then
print *, trim(text)
endif
enddo
close(10)
end program souradnice
You need to read the whole file line by line, and check each line to see if it's the one you want, e.g. by using the index intrinsic. For example,
program souradnice
implicit none
character(100) :: line
character(5) :: matches(3)
real :: numbers(3)
character(10) :: dummy
integer :: i, ierr
! Substrings to match to find the relevant lines
matches = ["xo xi", "yo yi", "zo zi"]
open(10,file="text.dat", status='old')
do
! Read a line from the file, and exit the loop if the file end is reached.
read(10,fmt='(a)',iostat=ierr) line
if (ierr<0) then
exit
endif
do i=1,3
! Check if `line` matches any of the i'th line we want.
if (index(line, matches(i))>0) then
! If it matches, read the relevant number into `numbers`.
read(line,*) dummy, numbers(i)
endif
enddo
enddo
write(*,*) numbers
end program

FORTRAN parsing file with varying line formate

I have only limited experience with FORTRAN and I need to parse files with a structure similar to this:
H s 13.010000 0.019685
1.962000 0.137977
0.444600 0.478148
s 0.122000 1.000000
p 0.727000 1.000000
***
He s 38.360000 0.023809
5.770000 0.154891
1.240000 0.469987
s 0.297600 1.000000
p 1.275000 1.000000
***
I need to search for the label (e.g. He) and then read the corresponding blocks into an array.
I know I can parse file by specifying the format each line is supposed to have, but here there are different formats possible.
In Python I would just split each line by the white spaces and deal with it depending on the number of columns. But how to approach this in FORTRAN?
You can read each line as a character string and then process it. If, as it seems, the format is fixed (element symbol in first two characters, orbital letter in sixth character, etc.), the following program could serve you as inspiration:
program elms
implicit none
integer, parameter :: MAX_LEN = 40
character(len=MAX_LEN) :: line_el, line
integer :: u
integer :: is
integer :: nlin
character(len=2) :: element = 'He'
integer, parameter :: MAX_LINES = 20
real, dimension(MAX_LINES) :: e, f
open(newunit=u, file='elms.dat', status='old', action='read')
main_loop: do
! Read line
read(u, '(a)', iostat=is) line_el
if (eof_iostat(is)) exit main_loop
! Check first two characters of the line vs. chemical element.
if (line_el(1:2) .eq. element) then
! This is the beginning of an element block
nlin = 0
line = line_el
do
if (line .ne. '') then
! Line is not empty or only spaces.
nlin = nlin + 1
if (line(6:6) .ne. ' ') then
! Line contains an orbital letter - process it.
end if
! Read the real values in the rest of the line
read(line(7:),*) e(nlin), f(nlin)
end if
! Read next line
read(u, '(a)', iostat=is) line
if (eof_iostat(is)) exit main_loop
if (line(1:2) .ne. ' ') then
! Finished processing element block.
exit main_loop
end if
end do
end if
end do main_loop
! Close file
close(u)
contains
logical function eof_iostat(istat)
! Returns true if the end of file has been reached
use, intrinsic :: iso_fortran_env, only: IOSTAT_END
implicit none
integer, intent(in) :: istat
select case (istat)
case (0) ! No error
eof_iostat = .false.
case (IOSTAT_END) ! End of file reached
eof_iostat = .true.
case default ! Error
STOP
end select
end function eof_iostat
end program
You will probably need to make the program a subroutine, make element an intent(in) dummy argument, process the orbital symbols, etc.
Note that, if possible, it would be easier to just read all the data from the file in one go, and then search for the relevant data in the memory (e.g., having an array with the chemical symbols).

Trouble reading reals from unknown length character string in Fortran

This is a small portion of the data I am trying to read:
01/06/2009,Tom Sanders,,264,220,73,260
01/08/2009,Adam Apple,158,,260,,208
01/13/2009,Lori Freeman,230,288,218,282,234
01/15/2009,Diane Greenberg,170,,250,321,197
01/20/2009,Adam Apple,257,,263,256,190
01/21/2009,Diane Greenberg,201,,160,195,142
01/27/2009,Tom Sanders,267,,143,140,206
01/29/2009,Tina Workman,153,,124,155,140
02/03/2009,Tina Workman,233,,115,,163
02/03/2009,Adam Apple,266,130,310,,310
the numbers between each comma are from a different location
Where two commas would represent missing data and a trailing comma would mean the fifth data point is missing
My goal is to organize the data into a table after calculating the average of each site and person, hence my two dim arrays
I want my output to look something like the following:
(obviously neater formatting but a table nonetheless)
Average Observed TDS (mg/l)
Name Site 1 Site 2 Site 3 Site 4 Site 5
------------------------------------------------------
Tom Sanders 251.0 172.5 251.7 160.0 229.0
Adam Apple 227.0 130.0 277.7 256.0 236.0
Lori Freeman 194.0 288.0 216.7 279.0 202.7
Diane Greenberg 185.5 190.0 205.0 258.0 169.5
Tina Workman 193.0 140.0 119.5 155.0 163.0
This is my program so far:
program name_finder
implicit none
integer, parameter :: wp = selected_real_kind(15)
real(wp) :: m, tds
real(wp), dimension(20,5) :: avg_site, site_sum
integer, dimension(20) :: nobs
integer, dimension(5) :: x
integer :: ierror, i, nemp, cp, non, ni, n
character(len=40), dimension(20) :: names
character(len=200) :: line, aname
character(len=20) :: output, filename
character(len=3), parameter :: a = "(A)"
do
write(*,*) "Enter file to open."
read(*,*) filename
open(unit=10,file = filename, status = "old", iostat = ierror)
if (ierror==0) exit
end do
write(*,*) "File, ",trim(filename)," has been opened."
non = 0
outer: do
read(10,a, iostat = ierror) line
if (ierror/=0) exit
cp = index(line(12:),",") + 11
aname = line(12:cp-1)
n=0
middle: do
read(line,'(Tcp,f4.2)') tds
write(*,*) "tds=", tds
n=n+1
if (n>10) exit
i = 1
inner: do
if (i > non) then
non = non +1
names(non) = trim(aname)
!ni = non
exit
end if
if (aname == names(i)) then
!ni = i
!cycle outer
exit inner
end if
i = i + 1
end do inner
end do middle
end do outer
write(*,*)
write(*,*) "Names:"
do i = 1,non
write(*,*) i, names(i)
end do
close(10)
close(20)
STOP
end program name_finder
TLDR; I am having trouble reading the data from the file shown at the top of each site after the names.
Suggestions? Thanks!
I hope the following is helpful. I have omitted any easily assumed declarations or any further data manipulation or writing to another file. The code is used just to read the data line by line.
character(150) :: word
read(fileunit, '(A)') word ! read the entire line
comma_ind = index(word,',') ! find the position of first comma
! Find the position of next comma
data_begin = index(word(comma_ind+1:),',')
! Save the name
thename = word(comma_ind+1:comma_ind+data_begin-1)
! Define next starting point
data_begin = comma_ind+data_begin
! Read the rest of the data
outer: do
if (word(data_begin+1:data_begin+1) == ',') then
! decide what to do when missing an entry
data_begin = data_begin + 1
cycle outer
else if (word(data_begin+1:data_begin+1) == ' ') then
! Missing last entry
exit outer
else
! Use it to find the length of current entry
st_ind = index(word(data_begin+1:),',')
if (st_ind == 0) then
! You reached the last entry, read it and exit
read(word(data_begin+1:), *) realData
exit outer
else
! Read current entry
read(word(data_begin+1: data_begin+st_ind-1),*) realData
end if
! Update starting point
data_begin = data_begin + st_ind
end if
end do outer
There could be a more elegant way to do it but I cannot think of any at the moment.

data entrance error Fortran

I'm learning how to programming with fortran90 and i need receive data from a txt file by the command prompt (something like that:
program.exe"<"data.txt).
at the Input txt file I'll always have a single line with at least 6 numbers till infinity.
if the data was wrote line by line it runs fine but as single line I'm receiving the error: "traceback:not available,compile with - ftrace=frame or - ftrace=full fortran runtime error:end file"
*note: i'm using Force fortran 2.0
here is example of data:
0 1 0.001 5 3 1 0 -9 3
edit: just clarifying: the code is working fine itself except for the read statement, which is a simple "read*,". I want know how To read a entire line from a txt once the entrance will be made by the promt command with stream direction.
( you can see more about that here: https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx?mfr=true).
there is no need to read the code, i've posted it just for knowledge.
I'm sorry about the whole inconvenience.
here is the code so far:
program bissecao
implicit none
integer::cont,int,e,k,intc,t1,t2,t3
doubleprecision::ii,is,pre,prec,erro,somaa,somab,xn
doubleprecision,dimension(:),allocatable::co
t1=0
t2=0
t3=0
! print*,"insira um limite inf da funcao"
read*,ii
!print*,"insira o limite superior da func"
read*,is
! print*,"insira a precisÆo admissivel"
read*,pre
if (erro<=0) then !elimina criterio de parada negativo ou zero
Print*,"erro"
go to 100
end if
!print*,"insira a qtd iteracoes admissiveis"
read*,int
!print*,"insira o grau da f(x)"
read*,e
if (e<=0) then ! elimina expoente negativo
e=(e**2)**(0.5)
end if
allocate(co(e+1))
!print*, "insira os coeficientes na ordem:&
! &c1x^n+...+(cn-1)x^1+cnx^0"
read(*,*)(co(k),k=e+1,1,-1)
somab=2*pre
intc=0
do while (intc<int.and.(somab**2)**0.5>pre.and.((is-ii)**2)**0.5>pre)
somab=0
somaa=0
xn =(ii+is)/2
do k=1,e+1,1
if (ii /=0) then
somaa=ii**(k-1)*co(k)+somaa
else
somaa=co(1)
end if
! print*,"somaa",k,"=",somaa
end do
do k=1,(e+1),1
if (xn/=0) then
somab=xn**(k-1)*co(k)+somab
else
somab=co(1)
end if
!print*,"somab",k,"=",somab
end do
if ((somaa*somab)<0) then
is=xn
else if((somaa*somab)>0)then
ii=xn
else if ((somaa*somab)==0) then
xn=(ii+is)/2
go to 100
end if
intc =intc+1
prec=is-ii
if ((((is-ii)**2)**.5)< pre) then
t3=1
end if
if (((somab**2)**.5)< pre) then
t2=1.
end if
if (intc>=int) then
t1=1
end if
end do
somab=0
xn=(ii+is)/2
do k=1,(e+1),1
if (xn/=0) then
somab=xn**(k-1)*co(k)+somab
else
somab=co(1)
end if
end do
100 write(*,'(A,F20.15,A,F20.15,A,A,F20.15,A,F20.15,A,I2)'),"I:[",ii,",",is,"]","raiz:",xn,"Fraiz:",somab,"Iteracoes:",intc
end program !----------------------------------------------------------------------------
In your program, you are using the "list-directed input" (i.e., read *, or read(*,*))
read *, ii
read *, is
read *, pre
read *, int
read *, e
read *, ( co( k ), k = e+1, 1, -1 )
which means that the program goes to the next line of the data file after each read statement (by neglecting any remaining data in the same line). So, the program works if the data file (say "multi.dat") consists of separate lines (as suggested by OP):
0
1
0.001
5
3
1 0 -9 3
But now you are trying to read an input file containing only a single line (say "single.dat")
0 1 0.001 5 3 1 0 -9 3
In this case, we need to read all the values with a single read statement (if list-directed input is to be used).
A subtle point here is that the range of array co depends on e, which also needs to be read by the same read statement. A workaround might be to just pre-allocate co with a sufficiently large number of elements (say 100) and read the data in a single line, e.g.,
integer :: k
allocate( co( 100 ) )
read *, ii, is, pre, int, e, ( co( k ), k = e+1, 1, -1 )
For completeness, here is a test program where you can choose method = 1 or 2 to read "multi.dat" or "single.dat".
program main
implicit none
integer :: int, e, k, method
double precision :: ii, is, pre
double precision, allocatable :: co(:)
allocate( co( 1000 ) )
method = 1 !! 1:multi-line-data, 2:single-line-data
if ( method == 1 ) then
call system( "cat multi.dat" )
read*, ii
read*, is
read*, pre
read*, int
read*, e
read*, ( co( k ), k = e+1, 1, -1 )
else
call system( "cat single.dat" )
read*, ii, is, pre, int, e, ( co( k ), k = e+1, 1, -1 )
endif
print *, "Input data obtained:"
print *, "ii = ", ii
print *, "is = ", is
print *, "pre = ", pre
print *, "int = ", int
print *, "e = ", e
do k = 1, e+1
print *, "co(", k, ") = ", co( k )
enddo
end program
You can pass the input file from standard input as
./a.out < multi.dat (for method=1)
./a.out < single.dat (for method=2)
Please note that "multi.dat" can also be read directly by using "<".

How to get FORTRAN 77 to read input?

I am using some old fortran code for a biology project I am doing. I am posting the relevant snippets here. Here is a subroutine called "READCN". Earllier in the program MAXN was set to 108.
OPEN ( UNIT = CNUNIT, FILE = CNFILE,
: STATUS = 'OLD', FORM = 'UNFORMATTED' )
READ ( CNUNIT ) N, BOX
IF ( N .GT. MAXN ) STOP ' N TOO LARGE '
READ ( CNUNIT ) ( RX(I), I = 1, N ), ( RY(I), I = 1, N )
CLOSE ( UNIT = CNUNIT )
RETURN
END
I am inputting a file called "data.dat" to the program. Here is the file:
10, 4
0.8147, 0.1576
0.9058, 0.9706
0.1270, 0.9572
0.9134, 0.4854
0.6324, 0.8003
0.0975, 0.1419
0.2785, 0.4218
0.5469, 0.9157
0.9575, 0.7922
0.9649, 0.9595
Nevertheless, I always get the message "N TOO LARGE". Any advice? Thanks!
Don't open as unformatted, it will read your file as if it were binary data. Open as formatted instead, and use "*" format. Also, don't read in one line, as you would not read your data in the expected order.
program bob
implicit none
integer cnunit, n, maxn, box, i
parameter(maxn=108, cnunit=10)
real rx(maxn), ry(maxn)
open(unit=cnunit, file='bob.txt', status='old', form='formatted')
read(cnunit, *) n, box
print *, 'n=', n, 'box=', box
if(n .gt. maxn) stop 'n too large'
do i=1, n
read(cnunit, *) rx(i), ry(i)
print *, rx(i), ry(i)
end do
close(unit=cnunit)
end
Alternately, if you can't change the code, then change your input file to fit the needs of your program. The input file you give simply won't work: you need binary data, in the format expected by your compiler (there is the usual, non portable "record size"), and data must be given column-wise, not row-wise.