How can I extract data from dat file? - fortran

I want to extract values in 1,3,5,9,11,.... lines of my .dat file using a fortran code.This is my code
program one
integer x, y, z
open (unit=10, file='try.dat', status='old')
open (unit=20, file='run_energy.dat', status='unknown')
dimension time(40), energy(40)
do y=1,13
z=2*(y-1)
do x=(z+1),(z+1)
read (10,*) time(x), energy(x)
write(20,*) time(x), energy(x)
end do
end do
stop
end
~
But, I don't get the values correspond to the lines I mentioned above. Can you please anyone help me.
Thank you.

You need to skip line (dummy read). Something like this:
x = 1
do i=1,13
! Read only if at correct line of 'try.dat'
if (i == 2*(x-1)+1) then
read (10,*) time(x), energy(x)
write(20,*) time(x), energy(x)
! Increment x
x=x+1
else
! Skip line
read (10,*)
end if
end do
Kind regards.

As already mentionned, the trick is to skip one line of the input file try.dat in order that only odd lines are written to the output file run_energy.dat.
Therefore the FORTRAN solution might also look like this, if the odd values of the input file shall be saved in the vectors time and energy:
program one
implicit none
integer, parameter :: siz = 40
integer :: x, st
real :: time(siz), energy(siz)
character :: line*256
open (unit=10, file='try.dat', status='old')
open (unit=20, file='run_energy.dat', status='unknown')
x = 1
read(10,'(A)',iostat=st) line
do while ((st == 0).and.(x < siz))
write(*,*) 'x = ',x, ', line = ', trim(line)
read(line,*,iostat=st) time(x), energy(x)
if (st == 0) write(20,*) time(x), energy(x)
read(10,'(A)',iostat=st) line ! Skip one line
if (st == 0) then ! Read next relevant line
x = x + 1
read(10,'(A)',iostat=st) line
endif
end do
close(10)
close(20)
end
If no saving of data into these vectors is required then the solution is quite shorter:
program one
implicit none
integer :: st
character :: line*256
open (unit=10, file='try.dat', status='old')
open (unit=20, file='run_energy.dat', status='unknown')
read(10,'(A)',iostat=st) line
do while (st == 0)
write(20,'(A)') trim(line)
read(10,'(A)',iostat=st) line ! Skip one line
if (st == 0) then ! Read next relevant line
read(10,'(A)',iostat=st) line
endif
end do
close(10)
close(20)
end
The trick is to use iostat in the read statement to detect end of file or improper matches of the line data to the vectors time and energy.
Hope it helps.

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 read text file

I have a file that writes like this
N
1000
NNODES
3
TURB_INT
0.20000
U_MEAN
30
TYPE_GP
dav
L
120
WIND_TURB
NODE_1 NODE_2 NODE_3
0.90139 -1.02858 0.03962
-2.56887 -1.59726 -0.82062
-0.58745 0.72129 -1.90712
-4.46302 -2.49995 -5.45345
-4.10550 -5.50565 -7.77285
-6.18588 -6.34998 -5.95054
I am really struggling to understand how to read it.
Finally what I need the most is reading the NODES records into variable WT(i,:), hence I could use a DO loop.
Here is what I have:
! open inputfile, ID=100
open(unit=100, file=inputfile, recl=fileln, status='old', iostat=iost)
print *, iost
print *, fileln
if (iost .ne. 0) then
print *, 'Error opening file'
print *, erromsg
else
print *, 'File opened correctly'
print *, erromsg
end if
! Trying to read what's written in file.
read(unit=100, *) N
print *, N
read(unit=100, *) Nval
print *, Nval
I was trying to see how reading is done line by line. I can read variable N, but right at second reading (line 2) I have "severe(24): END OF FILE during read".
Please could you advise me?
Thanks
I think you should read up on the proper flags for your open statement.
The keyword recl is used for direct access files. It means that every record has the same length, so if you need the 64th record, the program knows immediately where that record is.
If your file has a record length, I can't see it. As I said in my comment, I have the strong suspicion that fileln is the length of the file in bytes (you didn't say). So in your first read, the first record would be read, which is the entirety of the file, and then parsed into the variable N -- which can only take a single integer, everything else is then discarded.
And then it tries to read the next fileln bytes after the end of the file, which results in an 'end of file' error.
In your case, I would add the action="READ" and form="FORMATTED" keywords in the file open statement. And I always find it useful to not only look for the error code, but also the error message iomsg.
For what it's worth, here's my suggestion on how to read the file:
program readfile
implicit none
integer :: u ! File handle
integer :: ios
character(len=100) :: iom, line
integer :: N, NNodes, U_Mean, L
real :: Turb_Int
character(len=20) :: Type_GP
real, allocatable :: node_values(:)
character(len=10), allocatable :: node_names(:)
character(len=*), parameter :: inputfile = 'data.txt'
! newunit, instead of unit, creates a new unique
! file unit number. You could also set u to 100
! and then use unit=u
open(newunit=u, file=inputfile, action='READ', &
status='OLD', form='FORMATTED', iostat=ios, &
iomsg=iom)
if (ios /= 0) then
print *, "Error opening file:"
print *, iom
stop 1
end if
! Read the header. I'm always reading 'line' when
! I expect there to be just the next keyword
! in the line. You might want to check for that
read(u, *) line ! Hopefully 'N'
read(u, *) N
read(u, *) line ! Hopefully "NNODES"
read(u, *) NNodes
! I assume that NNODES stands for the number
! of columns later in the file.
! So here I'm also allocating the arrays.
allocate(node_values(NNodes))
allocate(node_names(NNodes))
read(u, *) line ! Hopefully TURB_INT
read(u, *) Turb_Int
read(u, *) line ! Hopefully U_MEN
read(u, *) U_Mean
read(u, *) line ! Hopefully TYPE_GP
read(u, *) Type_GP
read(u, *) line ! Hopefully L
read(u, *) L
read(u, *) line ! Hopefully WIND_TURB
read(u, *) node_names
! Just print out what we got from the header so far to see
! everyting's right
write(*, '(A, I0)') "N = ", N
write(*, '(A, I0)') "NNodes = ", NNodes
write(*, '(A, F5.1)') "Turb_Int = ", Turb_Int
write(*, '(A, I0)') "U_Mean = ", U_Mean
write(*, '(A, A)') "Type_GP = ", trim(Type_GP)
write(*, '(A, I0)') "L = ", L
write(*, '(A, *(A))') node_names
! Now read the data itself. In this case I'm just reading it
! line by line, print it out, and forget it again
! until the end of the file.
data_loop : do
read(u, *, iostat=ios, iomsg=iom) node_values
if (ios /= 0) exit data_loop
print *, node_values
end do data_loop
end program readfile

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.

How to open all files with some specific extension(prefix in name) in fortran ?

I want to read data from some files in Fortran, I can do that when the file names have a regular order. but now it's not regular, although all have same prefix for example : Fix001, Fix002, Fix023, Fix432, ...
I want the program get the prefix from the user and open all the files in a loop, read the data and write them in a single file.
any idea ?
Thanks.
PROGRAM Output
Implicit none
Integer ::n=5 !number of files
Integer ::nn=50 !number of rows in each file
Integer ::i,j
Real,Dimension(:),Allocatable::t,x,y,z
Character(len=12)::TD
Open(11,file='outputX.txt')
Allocate (t(1000),x(1000),y(1000),z(1000))
j=0
Do i=1,n
Write(TD,10)i
Write(*,*)TD
Open(1,file=TD)
Read(1,*)(t(j),x(j),j=1,nn)
Write(11,20)(x(j),j=1,nn)
j=j+1
Enddo
10 Format('100',i3.3,'')
20 Format(<nn>E25.8E3)
Deallocate(x,y,z,t)
END PROGRAM Output
If you do have an upper limit, you can try to open the file and test with the iostat parameter whether that was successful. If it wasn't, you skip the file.
This is an example that reads only the first integer variable from a file and appends it to the output file:
program read_files
implicit none
integer :: i, d
integer :: ioerr
character(len=len("FixXXX.txt")) :: fname
open(unit=30, file="Output.txt", action="write", iostat=ioerr)
if (ioerr /= 0) stop 1
do i = 0, 999
write(fname, '(A, I3.3, A)') "Fix", i, ".txt"
open(unit = 40, file=fname, status="old", action="read", iostat=ioerr)
if (ioerr /= 0) cycle
read(40, *) d
write(30, *) d
close(40)
end do
end program read_files