I have this Fortran code that reads two columns of data from an external file:
PRINT*, ' Q1 Q2 '
DO 2 J = 1, NPTS
READ(20,*) Q1(J),Q2(J)
WRITE(*,98) Q1(J),Q2(J)
So the file 20 is used, but it doesn't seem to like any names I use in the file, so the question is, what should I name the file as? It's a .txt by the way.
You do not read from files, but from units. So if you want to read from unit 20 as in your example, you need open a file using that unit first. This is typically done with an open statement, as suggested in the comments. For a file called yourfile.txt, your program would then read
OPEN( unit=20, file='yourfile.txt', status='old', &
action='read', iostat=stat )
if(stat /= 0) stop 'Could not open file'
PRINT*, ' Q1 Q2 '
DO 2 J = 1, NPTS
READ(20,*) Q1(J),Q2(J)
WRITE(*,98) Q1(J),Q2(J)
! ...
If you don't open a file first, at least gfortran and ifort try to read from a file named fort.<unit>, in your case fort.20. I couldn't find a reference in the Standard for that behavior, though.
Don't forget to close the file after reading, and try to avoid fixed unit numbers. If your compiler supports it, use the newunit= specifier when opening files.
Related
I have a Fortran routine that opens a lot of text files write data from a time loop. This routine uses open with the newunit option, this unit is stored in an object in order to write things in files later. This works fine most of the time but when the program needs to open a large number N of files at the same time I get the following error:
**forrtl: severe (104): incorrect STATUS= specifier value for connected file, unit -1, file CONOUT$**
reffering to the first open function in createFiles subroutine. This error occurs whether the file already exists or not. I don't know if this might help but at this stage the new unit that should be generated would be -32768.
I include a minimal code sample with a "timeSeries" class including a routine that creates two files:
the first file fileName1 is opened and closed directy after writing stuff inside
the second file fileName2 is kept open in order to write things comùputed in a time loop later and closed at the end of the time loop
The example is composed of the two following files. It breaks for i=32639.
main.f90 :
program writeFiles
use TS
logical :: stat
integer :: i, istep, N, NtimeSteps
character(len=16) :: fileName1, fileName2
character(len=300) :: path
type(timeSeries), dimension(:), allocatable :: myTS
call getcwd( path )
path = trim(path) // '\Output_files'
inquire(directory = trim(path), exist = stat )
if (.not. stat) call system("mkdir " // '"' // trim(path) // '"' )
N = 50000
NtimeSteps = 100
allocate(myTS(N))
do i = 1, N
write(fileName1,'(a6,i6.6,a4)') 'file1_', i, '.txt'
write(fileName2,'(a6,i6.6,a4)') 'file2_', i, '.txt'
call myTS(i)%createFiles(trim(path),fileName1,fileName2)
end do
do istep = 1, NtimeSteps
#
#compute stuff
#
do i = 1, N
write(myTS(i)%fileUnit,*) 'stuff'
end do
end do
do i = 1, N
close(myTS(i)%fileUnit)
end do
end program writeFiles
module.f90 :
module TS
type timeSeries
integer :: fileUnit
contains
procedure :: createFiles => timeSeries_createFiles
end type timeSeries
contains
subroutine timeSeries_createFiles(this,dir,fileName1,fileName2)
class(timeSeries) :: this
character(*) :: dir, fileName1, fileName2
open(newunit = this%fileUnit , file = dir // '\' // fileName1, status = 'replace') !error occurs here after multiple function calls
write(this%fileUnit,*) 'Write stuff'
close(this%fileUnit)
open(newunit = this%fileUnit , file = dir // '\' // fileName2, status = 'replace')
end subroutine timeSeries_createFiles
end module
Any idea about the reason for this error? Is there a limitation for the number of files opened at the same time? Could it be related to a memory issue?
I'm using Intel(R) Visual Fortran Compiler 17.0.4.210
Windows has this interesting habit of not releasing all the resources for a closed file for a short time after you do a close. I have seen this sort of problem on and off for decades. My usual recommendation is to put a call to SLEEPQQ with a duration of half a second after a CLOSE when you intend to do another OPEN soon after on the same file. But you're not doing that here.
There's more here that is puzzling. The error message referring to unit -1 and CONOUT$ should not occur when opening an explicit file and using NEWUNIT. In Intel's implementation, NEWUNIT numbers start at -129 and go more negative from there. Unit -1 is used for PRINT or WRITE(*), and CONOUT$ is the console. STATUS='REPLACE' would not be valid for a unit connected to the console. That the newunit number would be -32768 is telling and suggests an internal limit for NEWUNIT in the Intel libraries.
I did a test of my own and see that if you use NEWUNIT and close the unit, the unit numbers go as low as -16384 before cycling back to -129. That's ok if indeed you're closing the units, but you're never closing the second file you open, so you're at least hitting a maximum number of NEWUNIT files open. I would recommend figuring out a different way of approaching the problem that didn't require leaving thousands of files open.
I'm trying to read data from a mesh file in Fortran 2003, but I'm getting an unexpected end of file runtime error. Some lines in the file seem to be skipped by the read command. For example, with this sample.txt file :
1 2 2 0 1 1132 1131 1165
2 2 2 0 2 1099 1061 1060
I want to read the first integer from each line, so my program is :
program read_file
implicit none
integer :: ierr, i, j
open(unit=10,file='sample.txt',status='old',action='read',iostat=ierr)
read(10,*) i
read(10,*) j
write(*,*) i, j
end program read_file
And at runtime, I'm getting
Fortran runtime error: End of file
What is odd is that if I force a carriage return at the end of the file, the program will read the two integers just fine.
If you really need to fix this on the read side (ie. properly terminating the last line of the file is not practical for some reason ) you might try reading each line into a string, then internal reading from the string:
character*80 line
integer i
do ..
read(unit,'(a)')line
read(line,*)i
enddo
Of course this may or may not work depending on the compiler as well..
Obviously fixing the file is the best option ( Whatever program is creating this file should be fixed )
Every record in a sequential file must be properly terminated. The records in text files are the lines. They must be properly terminated. In some editors that means you must add an empty line to the end. Every line containing data must be terminated.
Some compilers are less sensitive to this issue than others and will terminate the last record for you.
My program works with a set of files (several millions). All the files were created earlier with some other code. Some of the files are empty, some have values; all of them have 'OLD' status. My program has to
open one of the files;
add some value to the END of THE FILE if the file contains numbers already or just put a first value if the file is empty;
close the file and go to another file processing.
Right now, if the file is non-empty, the program erase the file's previous content and just write a new value. I think, in order TO ADD a value to the end of existing non-empty file I need to use some clause in OPEN or WRITE statement in addition to the 'OLD' status. Which ones? Thank you.
It would be easier with a MWE, but nonetheless, what you could do is something like that, using the append keyword
open(unit=file_unit, file=filename, status='old', access='append')
You could try it on this simple example adapted from the Fortran Wikibook to see how it works
program write
implicit none
integer :: i, j
integer, parameter :: out_unit=10
print*,"Enter two integers:"
read (*,*) i, j
open (unit=out_unit, file="results.txt", action="write", status="old", access="append")
write (out_unit,*) "The product of",i," and",j
write (out_unit,*) "is",i*j
close (out_unit)
end program write
I have a Fortran program that starts with opening and reading data from a .txt file.
At the end of the program a new file is written, which replaces the old file (that was originally imported).
However it can occur that the file that needs to be opened does not exists, for that case the variables that should be imported from the .txt file should be 0.
I thought by doing this with the code below, however this does not work and the script is aborted when the file history.txt does not exists.
How can I let the script set default values to my variables when the history.txt file does not exists?
OPEN(UNIT=in_his,FILE="C:\temp\history.txt",ACTION="read")
if (stat .ne. 0) then !In case history.txt cannot be opened (iteration 1)
write(*,*) "history.txt cannot be opened"
KAPPAI=0
KAPPASH=0
go to 99
end if
read (in_his, *) a, b
KAPPAI=a
KAPPASH=b
write (*, *) "KAPPAI=", a, "KAPPASH=", b
99 close(in_his)
The file that is imported is pretty simple and looks like:
9.900000000000006E-003 3.960000000000003E-003
I would use IOSTAT as stated by #Fortranner. I would also set defaults before trying to open the file and I tend not to use goto's. As in:
program test
implicit none
integer :: in_his, stat
real :: KAPPAI, KAPPASH
in_his = 7
KAPPAI = 0
KAPPASH = 0
OPEN(UNIT=in_his, FILE="history.txt",ACTION='read',IOSTAT=stat,STATUS='OLD')
if (stat .ne. 0) then
write(*,*) "history.txt cannot be opened"
stop 1
end if
read (in_his, *) KAPPAI, KAPPASH
close(in_his)
write (*, *) "KAPPAI=", KAPPAI, "KAPPASH=", KAPPASH
end program test
Another way is to use an inquire statement and check for the existence of the file before you try to open it. This would set a logical variable that could be used in an IF statement to handle the two cases: 1) open file and read values, or 2) set default values w/o opening the file. Or set the default values first, then have the IF statement only handle the case of opening the file and reading the values.
Set iostat in the open statement and handle the case where it is nonzero.
There are two ways to do this. One is using IOSTAT specifier in the OPEN statement like Fortranner and Timothy Brown suggested. The other is to use the ERR specifier in the OPEN statement which lets you specify a label to which the program will transfer control in the even of an error:
OPEN(UNIT=in_his,FILE="C:\temp\history.txt",ACTION="read",STATUS='OLD',ERR=101)
...
101 CONTINUE
The label must be in the same scoping unit as the OPEN statement.
I have a shell script from which I pass a binary file to a fortran program such that
Mth=$1
loop=1
it=1
while test $it -le 12
do
Mth=`expr $Mth + $loop`
file="DataFile"$Mth".bin"
./fort_exe ${Yr} ${nt} ${it}
# Increment loop
it=`expr $it + 1`
done
This script is used to pass 12 files within a do loop to the fortran program. In the fortran program, I read the binary file passed from the shell script and I am trying to write a 2nd file which would compile in a single file all the data that was read from the consecutive files e.g.
!Open binary file passed from shell script
open(1,file='Datafile'//TRIM{Mth)//.bin',action='read',form='unformatted',access='direct', &
recl=4*x*y, status='old')
! Open write file for t 1. The status is different in t 1 and t > 1 so I open it twice: I guess there is a more elegant way to do this...
open(2,file='Newfile.bin',action='write',form='unformatted', &
access='stream', position='append', status='replace')
irec = 0
do t = 1, nt
! Read input file
irec = irec + 1
read(1,rec=irec) val(:,:)
! write output file
irecW= irec + (imonth-1)*nt
if ( t .eq. 1) write(2,pos=irecW) val(:,:)
! Close file after t = 1, update the status to old and reopen.
if ( t .eq. 2) then
close (2)
open(2,file='Newfile.bin',action='write',form='unformatted', &
access='stream', position='append',status='old')
endif
if ( t .ge. 2) write(2,pos=irecW) val(:,:)
enddo
I can read the binary data from the first file no problem but when I try and read from another program the binary data from the file that I wrote in the first program such that
open(1,file='Newfile.bin',action='read',form='unformatted', &
access='stream', status='old')
irec=0
do t = 1, nt
! Read input file
irec = irec + 1
read(1,pos=irec) val(:,:)
write(*,*) val(:,:)
enddo
val(:,:) is nothing but a list of zeros. This is the first time I use access=stream which I believe is the only way I can use position='append'. I have tried compiling with gfortran and ifort but I do not get any error messages.
Does anyone have any idea why this is happening?
Firstly, I do not think you need to close and reopen your output file as you are doing. The status specifier is only relevant to the open statement in which it appears: replace will delete Newfile.bin if it exists at that time, before opening a new file with the same name. The status is implicitly changed to old, but this does not affect any operations done to the file.
However, since your Fortran code does not know you run it 12 times, you should have a way of making sure the file is only replaced the first time and opened as old afterwards; otherwise, Newfile.bin will only contain the information from the last file processed.
As for reading in the wrong values, this most likely occurs because of the difference between direct access (where you can choose a record length) and stream access (where you cannot). With stream access, data is stored as a sequence of "file storage units". Their size is in general compiler-dependent, but is available through the module iso_fortran_env as file_storage_size; it is usually 8 bits. This means that each entry will usually occupy multiple storage units, so you have to take care that a read or write with the pos = specifier does not access the wrong storage units.
Edit:
Some example code writing and reading with stream access:
program stream
use, intrinsic :: iso_fortran_env
implicit none
integer :: i, offset
real(real32), dimension(4,6) :: val, nval
open(unit=2, file='Newfile.bin', action='readwrite', form='unformatted', &
access='stream', status='replace')
do i = 1,2
call random_number(val)
write(2) val
enddo
! The file now contains two sequences of 24 reals, each element of which
! occupies the following number of storage units:
offset = storage_size(val) / file_storage_size
! Retrieve the second sequence and compare:
read(2, pos = 1 + offset*size(val)) nval
print*, all(nval == val)
close(2)
end program
The value true should be printed to the screen.
Note also that it's not strictly necessary to specify a pos while writing your data to the file, because the file will automatically be positioned beyond the last record read or written.
That said, direct or stream access is most beneficial if you need to access the data in a non-sequential manner. If you only need to combine input files into one, it could be easier to write the output file with sequential access, for which you can also specify recl and position = 'append'.
You can check for the existence of a file in standard Fortran, by using the inquire statement:
logical :: exist
inquire(file="test.dat", exist=exist)
if (exist) then
print *, "File test.dat exists"
else
print *, "File test.dat does not exist"
end if
Alternatively you can have a look at the modFileSys library which provides libc like file manipulation routines.
As for appending and streams: Appending files is also possible when you use "classical" record based fortran files, you do not have to use streams for that.