I have a program in Fortran that saves the results to a file. At the moment I open the file using
OPEN (1, FILE = 'Output.TXT')
However, I now want to run a loop, and save the results of each iteration to the files 'Output1.TXT', 'Output2.TXT', 'Output3.TXT', and so on.
Is there an easy way in Fortran to constuct filenames from the loop counter i?
you can write to a unit, but you can also write to a string
program foo
character(len=1024) :: filename
write (filename, "(A5,I2)") "hello", 10
print *, trim(filename)
end program
Please note (this is the second trick I was talking about) that you can also build a format string programmatically.
program foo
character(len=1024) :: filename
character(len=1024) :: format_string
integer :: i
do i=1, 10
if (i < 10) then
format_string = "(A5,I1)"
else
format_string = "(A5,I2)"
endif
write (filename,format_string) "hello", i
print *, trim(filename)
enddo
end program
A much easier solution IMHO ...................
character(len=8) :: fmt ! format descriptor
fmt = '(I5.5)' ! an integer of width 5 with zeros at the left
i1= 59
write (x1,fmt) i1 ! converting integer to string using a 'internal file'
filename='output'//trim(x1)//'.dat'
! ====> filename: output00059.dat
Well here is a simple function which will return the left justified string version of an integer:
character(len=20) function str(k)
! "Convert an integer to string."
integer, intent(in) :: k
write (str, *) k
str = adjustl(str)
end function str
And here is a test code:
program x
integer :: i
do i=1, 100
open(11, file='Output'//trim(str(i))//'.txt')
write (11, *) i
close (11)
end do
end program x
I already showed this elsewhere on SO (How to use a variable in the format specifier statement? , not an exact duplicate IMHO), but I think it is worthwhile to place it here. It is possible to use the techniques from other answers for this question to make a simple function
function itoa(i) result(res)
character(:),allocatable :: res
integer,intent(in) :: i
character(range(i)+2) :: tmp
write(tmp,'(i0)') i
res = trim(tmp)
end function
which you can use after without worrying about trimming and left-adjusting and without writing to a temporary variable:
OPEN(1, FILE = 'Output'//itoa(i)//'.TXT')
It requires Fortran 2003 because of the allocatable string.
For a shorten version.
If all the indices are smaller than 10, then use the following:
do i=0,9
fid=100+i
fname='OUTPUT'//NCHAR(i+48) //'.txt'
open(fid, file=fname)
!....
end do
For a general version:
character(len=5) :: charI
do i = 0,100
fid = 100 + i
write(charI,"(A)"), i
fname ='OUTPUT' // trim(charI) // '.txt'
open(fid, file=fname)
end do
That's all.
I've tried #Alejandro and #user2361779 already but it gives me an unsatisfied result such as file 1.txt or file1 .txt instead of file1.txt. However i find the better solution:
...
integer :: i
character(len=5) :: char_i ! use your maximum expected len
character(len=32) :: filename
write(char_i, '(I5)') i ! convert integer to char
write(filename, '("path/to/file/", A, ".dat")') trim(adjustl(char_i))
...
Explanation:
e.g. set i = 10 and write(char_i, '(I5)') i
char_i gives " 10" ! this is original value of char_i
adjustl(char_i) gives "10 " ! adjust char_i to the left
trim(adjustl(char_i)) gives "10" ! adjust char_i to the left then remove blank space on the right
I think this is a simplest solution that give you a dynamical length filename without any legacy blank spaces from integer to string conversion process.
Try the following:
....
character(len=30) :: filename ! length depends on expected names
integer :: inuit
....
do i=1,n
write(filename,'("output",i0,".txt")') i
open(newunit=iunit,file=filename,...)
....
close(iunit)
enddo
....
Where "..." means other appropriate code for your purpose.
To convert an integer to a string:
integer :: i
character* :: s
if (i.LE.9) then
s=char(48+i)
else if (i.GE.10) then
s=char(48+(i/10))// char(48-10*(i/10)+i)
endif
Here is my subroutine approach to this problem. it transforms an integer in the range 0 : 9999 as a character. For example, the INTEGER 123 is transformed into the character 0123. hope it helps.
P.S. - sorry for the comments; they make sense in Romanian :P
subroutine nume_fisier (i,filename_tot)
implicit none
integer :: i
integer :: integer_zeci,rest_zeci,integer_sute,rest_sute,integer_mii,rest_mii
character(1) :: filename1,filename2,filename3,filename4
character(4) :: filename_tot
! Subrutina ce transforma un INTEGER de la 0 la 9999 in o serie de CARACTERE cu acelasi numar
! pentru a fi folosite in numerotarea si denumirea fisierelor de rezultate.
if(i<=9) then
filename1=char(48+0)
filename2=char(48+0)
filename3=char(48+0)
filename4=char(48+i)
elseif(i>=10.and.i<=99) then
integer_zeci=int(i/10)
rest_zeci=mod(i,10)
filename1=char(48+0)
filename2=char(48+0)
filename3=char(48+integer_zeci)
filename4=char(48+rest_zeci)
elseif(i>=100.and.i<=999) then
integer_sute=int(i/100)
rest_sute=mod(i,100)
integer_zeci=int(rest_sute/10)
rest_zeci=mod(rest_sute,10)
filename1=char(48+0)
filename2=char(48+integer_sute)
filename3=char(48+integer_zeci)
filename4=char(48+rest_zeci)
elseif(i>=1000.and.i<=9999) then
integer_mii=int(i/1000)
rest_mii=mod(i,1000)
integer_sute=int(rest_mii/100)
rest_sute=mod(rest_mii,100)
integer_zeci=int(rest_sute/10)
rest_zeci=mod(rest_sute,10)
filename1=char(48+integer_mii)
filename2=char(48+integer_sute)
filename3=char(48+integer_zeci)
filename4=char(48+rest_zeci)
endif
filename_tot=''//filename1//''//filename2//''//filename3//''//filename4//''
return
end subroutine nume_fisier
Related
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
I am creating a linked list from the input file. Each node in the linked list includes the information in each block of the input.
When I try to get value from input file, I assigned multiple lines (4 lines each time) into a 1D array "tmp". I think the DO-loop I used in the "input:DO" loop is wrong. But I do not know how to solve.
I use gfortran to compile and no mistake come out for compiling.
I have tried WRITE for testing in the "input:DO" loop for testing. The result shows that I can open the input file successfully.
PROGRAM read
IMPLICIT NONE
INTEGER,PARAMETER :: nat=4
character(len=20) :: filename
!Derived types to store atom data
TYPE :: atom
CHARACTER(LEN=2) :: atom_name
REAL, DIMENSION(3) :: coord
END TYPE atom
!The array info stores info of all atom in one time step
type :: atom_seq
type(atom),dimension(nat):: info
type(atom_seq),pointer :: p
end type atom_seq
TYPE (atom_seq), POINTER :: head
TYPE (atom_seq), POINTER :: tail
type(atom), dimension(nat) :: temp
! Declare variable
INTEGER :: istat
INTEGER :: i=0, n=0
! Open input data file
WRITE(*,*) 'ENTER the file name with the data to be read: '
READ(*,'(A20)') filename
NULLIFY(head)
OPEN( UNIT=9, FILE=TRIM(filename), STATUS="OLD", ACTION="READ", IOSTAT=istat)
! Was the open successful
fileopen: IF (istat == 0) THEN
input: DO
!WRITE(*,*) "OPEN done " ! for testing
READ(9,*) ! <--when run, error is in this line
READ(9,*)
DO i = 1, nat
READ(9,*,IOSTAT=istat) temp(i)%atom_name, temp(i)%coord(1), temp(i)%coord(2), temp(i)%coord(3)
ENDDO
IF (istat /= 0) EXIT
n = n + 1 ! Bump count
IF (.NOT. ASSOCIATED(head) ) THEN ! No values in list
ALLOCATE(head, STAT=istat) ! Allocate new value
tail => head ! Tail points to new value
NULLIFY(tail%p) ! Nullify p in new value
DO i = 1, nat ! Store number
tail%info(i)%atom_name = temp(i)%atom_name
tail%info(i)%coord(1) = temp(i)%coord(1)
tail%info(i)%coord(2) = temp(i)%coord(2)
tail%info(i)%coord(3) = temp(i)%coord(3)
ENDDO
ELSE ! Values already in list
ALLOCATE(tail%p, STAT=istat) ! Allocate new value
tail => tail%p
NULLIFY(tail%p)
DO i = 1, nat ! Store number
tail%info(i)%atom_name = temp(i)%atom_name
tail%info(i)%coord(1) = temp(i)%coord(1)
tail%info(i)%coord(2) = temp(i)%coord(2)
tail%info(i)%coord(3) = temp(i)%coord(3)
ENDDO
END IF
END DO input
ELSE fileopen
WRITE(*,1030) istat
1030 FORMAT ('File open failed --status = ', I6)
END IF fileopen
END PROGRAM read
The input file: inp
4
Particles:1_0
O 0.8050005000 0.7000000000 3.2350000000
H 1.4750005000 1.2800000000 2.8650000000
H 0.8550005000 -0.0900000000 2.7150000000
O 0.4050005000 0.7500000000 -4.1350000000
4
Particles:1_5
O 0.8799478358 0.6383317306 3.1569568025
H 1.4046592860 1.2232485826 2.4978364665
H 1.1472783731 -0.2687458123 3.0229092093
O 0.5392992531 0.6047144782 -4.0811918365
4
Particles:1_10
O -3.8021765454 3.1600783692 -4.5455655916
H -4.5320715486 3.0937504111 4.9514896261
H -3.5088238380 4.0613340230 -4.5394597924
O -3.3469012765 -0.7064128847 1.2465212113
and the error is
hg#xi /home/hg/pole $ ./read
ENTER the file name with the data to be read:
inp
At line XXX of file read.f95 (unit = 9, file = 'inp')
Fortran runtime error: End of file
Error termination. Backtrace:
#0 0x7f1c1fdbb31a
#1 0x7f1c1fdbbec5
#2 0x7f1c1fdbc68d
#3 0x7f1c1ff32a33
#4 0x7f1c1ff364b7
#5 0x7f1c1ff365b8
#6 0x5566d3dc9daf
#7 0x5566d3dca9ed
#8 0x7f1c1f9d0b96
#9 0x5566d3dc9a79
#10 0xffffffffffffffff
I hope to fix the problem. If my idea is wrong, please give some suggestions to design a better data structure to save data in the input (The input file may have thousands of blocks, instead of 3. It is big and the number of blocks is unknown before running the code. )
There is a test on the IOSTAT missing with the first executed READ in the loop. When the result is not OK the loop can be terminated e.g. change:
fileopen: IF (istat == 0) THEN
input: DO
!WRITE(*,*) "OPEN done " ! for testing
READ(9,*) ! <--when run, error is in this line
READ(9,*)
in
fileopen: IF (istat == 0) THEN
input: DO
!WRITE(*,*) "OPEN done " ! for testing
READ(9,*,IOSTAT=istat)
IF (istat /=0) EXIT
READ(9,*)
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).
I'm working on a project where I need to write some existing data to disk as ascii. I have something that works, but the IO itself is quite expensive and I'd like to optimise it further.
The data is basically an array of reals, however some of the columns store encoded strings which need to be recast as character strings (don't ask!). The input and output of this problem are beyond my control, I am receiving this real array and need to write it out as ascii.
I know that writing the array in one go as an unformatted write is faster, but this doesn't deal with the string columns correctly. Any ideas?
Here is some example code:
program test
implicit none
integer(kind=4), parameter :: nrows = 5000
integer(kind=4), parameter :: ncols = 400
integer, parameter :: real_kind = 8
integer(kind=4) :: i,j, handle
character(len=256) :: value_str
character(len=1) :: delimiter
real(kind=real_kind) :: data(nrows,ncols)
delimiter = " "
data(:,:) = 999.999
! Some examples of the "string columns"
data(:,10) = transfer(' foo ',data(1,1))
data(:,20) = transfer(' bar ',data(1,1))
handle=10
open(handle,file="out.txt",status="replace", access="stream")
do i=1,nrows
do j=1,ncols
! If this column contains encoded strings then recast
if((j==10).or.(j==20))then
write(handle) delimiter
value_str = transfer(data(i,j),value_str(1:real_kind))
write(handle) trim(value_str)
else
write(value_str,*) data(i,j)
write(handle) trim(value_str)
endif
enddo
write(handle) new_line('x')
enddo
close(handle)
end program test
gfortran test.F90 -o test.x
time test.x
real 0m2.65s
user 0m2.24s
sys 0m0.04s
Edit: removed "if(j/=1)" from original test.F90 code sample in response to comment.
Use the free formatting and have the system handle more for you. In this proposition, I handle the transfer beforehand and use a single loop to write the data to file. This is handy if you have only few columns of character data like the 2 in your example.
Your code will look like this
program test
implicit none
integer(kind=4), parameter :: nrows = 5000
integer(kind=4), parameter :: ncols = 400
integer, parameter :: real_kind = 8
integer, parameter :: pos1 = 10 ! I like named constants
integer, parameter :: pos2 = 20 ! I like named constants
integer(kind=4) :: i,j, handle
character(len=256) :: value_str
character(len=1) :: delimiter
real(kind=real_kind) :: data(nrows,ncols)
character(real_kind), dimension(nrows,2) :: cdata ! two columns array for
delimiter = " "
data(:,:) = 999.999
! Some examples of the "string columns"
data(:,pos1) = transfer(' foo ',data(1,1))
data(:,pos2) = transfer(' bar ',data(1,1))
handle=10
open(handle,file="out.txt",status="replace", form="formatted")
! Transfer beforehand
cdata(:,1) = transfer( data(:,pos1), cdata(1,1) )
cdata(:,2) = transfer( data(:,pos2), cdata(1,1) )
do i=1,nrows
write(handle,*) data(i,1:pos1-1), cdata(i,1)&
, data(i,pos1+1:pos2-1), cdata(i,2)&
, data(i,pos2+1:)
enddo
close(handle)
end program test
and give this timing
time ./test.x
real 0m1.696s
user 0m1.661s
sys 0m0.029s
instead of
time ./test.x
real 0m2.654s
user 0m2.616s
sys 0m0.032s
On my computer
Dear All, I am writing a code that writes the out put in multiple files named as 1.dat, 2.dat, ..... Here is my code but it gives some unusual output. May you tell me what is wrong in my code please? Basically I could not get the correct syntax to open multiple files, write on them and close before the next file is opened. Thank you. My Code:
implicit double precision (a-h,o-z),integer(i-n)
dimension b(3300,78805),bb(78805)
character*70,fn
character*80,fnw
nf = 3600 ! NUMBER OF FILES
nj = 360 ! Number of rows in file.
do j = 1, nj
bb(j) = 0.0
end do
c-------!Body program-----------------------------------------------
iout = 0 ! Output Files upto "ns" no.
DO i= 1,nf ! LOOP FOR THE NUMBER OF FILES
if(mod(i,180).eq.0.0) then
open(unit = iout, file = 'formatted')
x = 0.0
do j = 1, nj
bb(j) = sin(x)
write(iout,11) int(x),bb(j)
x = x + 1.0
end do
close(iout)
iout = iout + 1
end if
END DO
11 format(i0,'.dat')
END
So there are a few things not immediately clear about your code, but I think here the most relevant bits are that you want to specify the filename with file = in the open statement, not the formatting, and looping over units with iout is problematic because you'll eventually hit system-defined units for stdin and stdout. Also, with that format line it looks like you're getting ready to create the filename, but you never actually use it.
I'm not sure where you're; going with the mod test, etc, but below is a stripped down version of above which just creates the files ina loop:
program manyfiles
implicit none
character(len=70) :: fn
integer, parameter :: numfiles=40
integer, parameter :: outunit=44
integer :: filenum, j
do filenum=1,numfiles
! build filename -- i.dat
write(fn,fmt='(i0,a)') filenum, '.dat'
! open it with a fixed unit number
open(unit=outunit,file=fn, form='formatted')
! write something
write(outunit, *) filenum
! close it
close(outunit)
enddo
end program manyfiles
In my case, I want the file name have an prefix likedyn_
program manyfiles
implicit none
character(len=70) :: filename
integer, parameter :: numfiles=40
integer, parameter :: outunit=44
integer :: filenum, j
do filenum=1,numfiles
write(filename,'("dyn_",i0,".dat")') filenum
open(unit=outunit,file=filename, form='formatted')
write(outunit, *) filenum
close(outunit)
enddo
end program manyfiles