I'm trying to open 6 different files (at least) and then read the number of lines in each file, which should be about 20,000 lines each. I've read some posts on this forum about how to do that as I'm a newbie, and I've tried to implement it for my purposes.
I can do this individually without any problem, but when I try to read in all the files, I get an error message. I get either the "Killed: 9" error message or a malloc error:
malloc: *** mach_vm_map(size=63032829050880) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
What does this error mean regarding memory allocation? What am I doing wrong? How do I go about correcting this?
PROGRAM X
IMPLICIT NONE
INTEGER :: J,IO,NFILES,NLINES
CHARACTER (LEN=128) :: FILENAME
NFILES = 6
NLINES = 0
DO J = 0,NFILES-1
WRITE(FILENAME,'(A,I7.7,A)') 'data_',J*200,'.txt'
OPEN(1,FILE='FILENAME',FORM='FORMATTED')
DO
READ(1,*,IOSTAT=IO)
IF (IO/=0) EXIT
NLINES = NLINES + 1
END DO
WRITE(*,*) NLINES
CLOSE(1)
END DO
END PROGRAM X
I am using gfortran to compile.
UPDATE
I created 6 test files, data_0000000.txt, data_0000200.txt, ..., data_0001000.txt, each with less than 10 lines where there are less than 100 characters in each line. Unfortunately, I get the same error.
Obligatory disclaimer: If you just want to know the number of lines in a file, use wc -l <filename>. Don't reinvent the wheel if you don't have to.
I write this not necessarily because I think you didn't know that, but because someone else might come along, and think they need to write their own program to get the number of lines of files.
As for your question: I don't know why you get a malloc error. Maybe tell us which compiler and system you're using (including versions)? That said, there are three things that I noticed when reading your code:
You create a variable FILENAME, but then you don't use it. You're quoting it: FILE='FILENAME' which means that the open command looks for a file literally called FILENAME, not for a file with the name stored in the variable FILENAME. Remove the quotes:
OPEN(1, FILENAME=FILENAME, FORM='FORMATTED')
You use the unit number 1 -- that is dangerous. Different version of Fortran use specific unit numbers for specific uses. Use a handle far larger (at least 10, or more), or, even better, use the newunit descriptor in the open statement:
INTEGER :: u
OPEN(NEWUNIT=u, FILE=FILENAME, ACTION='READ', FORM='FORMATTED')
READ(u, *, IOSTAT=IO)
CLOSE(u)
You're not resetting the NLINES variable to 0 between files. The program will print a cumulative sum, not the number of lines for each file directly.
Adding to #chw21's response, if your sole purpose is to count the number of records (lines) in a file, here is a modular solution and a test program along with it (the counting is done inside subroutine getNumRecordInFile()):
module NumRecord_mod
implicit none
type :: Err_type
logical :: occurred = .false.
integer :: stat = -huge(0)
character(:), allocatable :: msg
end type Err_type
contains
! returns the number of lines in a file.
subroutine getNumRecordInFile(filePath,numRecord,Err)
implicit none
character(len=*), intent(in) :: filePath
integer, intent(out) :: numRecord
type(Err_type), intent(out) :: Err
character(len=8) :: record
integer :: fileUnit
logical :: fileExists, isOpen
integer :: iostat
character(*), parameter :: PROCEDURE_NAME = "#getNumRecordInFile()"
Err%occurred = .false.
Err%msg = ""
! Check if file exists
inquire( file=filePath, exist=fileExists, opened=isOpen, number=fileUnit, iostat=Err%stat )
if (Err%stat/=0) then
Err%occurred = .true.
Err%msg = PROCEDURE_NAME // ": Error occurred while inquiring the status of file='" // filePath // "'."
return
end if
if (.not.fileExists) then
Err%occurred = .true.
Err%msg = PROCEDURE_NAME // ": The input file='" // filePath // "' does not exist."
return
end if
if (isOpen) close(unit=fileUnit,iostat=Err%stat)
if (Err%stat>0) then
Err%occurred = .true.
Err%msg = PROCEDURE_NAME // ": Error occurred while attempting to close the open input file='" // filePath // "'."
return
end if
open(newunit=fileUnit,file=filePath,status="old",iostat=Err%stat)
if (Err%stat>0) then
Err%occurred = .true.
Err%msg = PROCEDURE_NAME // ": Error occurred while opening input file='" // filePath // "'."
return
end if
numRecord = 0
do
read(fileUnit,'(A)',iostat=iostat) record
if(iostat==0) then
numRecord = numRecord + 1
cycle
elseif(is_iostat_end(iostat)) then
exit
else
Err%occurred = .true.
Err%stat = iostat
Err%msg = PROCEDURE_NAME // ": Error occurred while reading input file='" // filePath // "'."
return
end if
end do
close(fileUnit,iostat=Err%stat)
if (Err%stat>0) then
Err%occurred = .true.
Err%msg = PROCEDURE_NAME // ": Error occurred while attempting to close the open input file='" // &
filePath // "' after counting the number of records in file."
return
end if
end subroutine getNumRecordInFile
end module NumRecord_mod
program test_numRecord
use NumRecord_mod
implicit none
type(Err_type) :: Err
integer :: numRecord
character(:), allocatable :: filePath
filePath = "main.f95"
call getNumRecordInFile(filePath=filePath,numRecord=numRecord,Err=Err)
if (Err%occurred) then
write(*,*) Err%msg
write(*,*) Err%stat
error stop
else
write(*,*) "Total number of records in file='" // filePath // "': ", numRecord
end if
end program test_numRecord
Now if you put this code in a file named "main.f95" and compile it under Fortran 2008 standard, then it should output the number of lines in your "main.f95" file, which should be something like the following:
$gfortran -std=f2008 *.f95 -o main
$main
Total number of records in file='main.f95': 98
For testing, you can simply copy paste the entire code in the online Fortran compiler here: https://www.tutorialspoint.com/compile_fortran_online.php
But keep in mind to change the compile option -std=f95 to -std=f2008 by going to Project -> Compile Options before executing the code.
Try this change (declaration and read line), there was no variable specified where the line contents shall go, here insert now dummy...
character(len=1000) :: dummy
...
READ(u, '(a)' , IOSTAT=IO) dummy
....
Related
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
I'm new to fortran and I'm trying to write and compile an easy example that I found in this address to read a simple 2 column text file:
Date Water-flow
717976 7.140
717977 6.570
717978 6.040
717979 5.780
717980 5.530
The program is pasted below, but when trying to compile and run I don't really understand the resulting error, could you please advice me?
PROGRAM READHPOP
IMPLICIT NONE
INTEGER, PARAMETER :: lun = 10
INTEGER :: res,i
CHARACTER(len=80) :: cbuffer
INTEGER :: flength
INTEGER,ALLOCATABLE,DIMENSION(:) :: dates
REAL,ALLOCATABLE,DIMENSION(:) :: water_flow
INTEGER :: c_position,string_length
OPEN(UNIT=lun,FILE="st.dat",FORM="FORMATTED",IOSTAT=res)
IF(res/=0) THEN
PRINT*,'error in opening file, status: ',res
STOP
END IF
READ(UNIT=lun,FMT='(A)',IOSTAT=res) cbuffer
IF(res /=0) THEN
PRINT *,'ERROR IN READING file, status: ',res
CLOSE(UNIT=lun)
STOP
END IF
string_length=LEN_TRIM(cbuffer)
c_position=INDEX(cbuffer,':')
READ(cbuffer(c_position+1:string_length),FMT='(A10)') flength
ALLOCATE(dates(flength),STAT=res)
IF (res/=0) THEN
PRINT*,'ERROR IN ALLOCATING MEMORY, status:',res
CLOSE(UNIT=lun)
STOP
END IF
READ(UNIT=lun,FMT='(A)',IOSTAT=res) cbuffer
DO i=1,cbuffer
READ(UNIT=lun,FMT='(I6,1X,F6.3)') dates(i),water_flow(i)
PRINT*,'DIAS ',dates(i)
END DO
end PROGRAM readhpop
EDIT: thanks to all for the inputs, just to close the issue, I paste below the working code, regards!
PROGRAM READHPOP
IMPLICIT NONE
INTEGER, PARAMETER :: lun = 10
INTEGER :: res,i
CHARACTER(len=80) :: cbuffer
INTEGER :: flength
INTEGER,ALLOCATABLE,DIMENSION(:) :: dates
REAL,ALLOCATABLE,DIMENSION(:) :: water_flow
INTEGER :: c_position,string_length
OPEN(UNIT=lun,FILE="st.dat",FORM="FORMATTED",IOSTAT=res)
IF(res/=0) THEN
PRINT*,'error in opening file, status: ',res
STOP
END IF
READ(UNIT=lun,FMT='(A)',IOSTAT=res) cbuffer
IF(res /=0) THEN
PRINT *,'ERROR IN READING file, status: ',res
CLOSE(UNIT=lun)
STOP
END IF
string_length=LEN_TRIM(cbuffer)
c_position=INDEX(cbuffer,':')
READ(cbuffer(c_position+1:string_length),FMT='(I10)') flength
ALLOCATE(dates(flength),water_flow(flength),STAT=res)
IF (res/=0) THEN
PRINT*,'ERROR IN ALLOCATING MEMORY, status:',res
CLOSE(UNIT=lun)
STOP
END IF
DO i=1,flength
READ(UNIT=lun,FMT='(I6,F6.3)') dates(i),water_flow(i)
PRINT*,'LINE OF FILE',i,' DAYS ',dates(i),' WATERFLOW ',water_flow(i)
END DO
PAUSE
END PROGRAM READHPOP
Okay, here is a working version:
PROGRAM READHPOP
IMPLICIT NONE
INTEGER, PARAMETER :: lun = 10
INTEGER :: res,i
CHARACTER(len=80) :: cbuffer
INTEGER :: flength
INTEGER,ALLOCATABLE,DIMENSION(:) :: dates
REAL,ALLOCATABLE,DIMENSION(:) :: water_flow
INTEGER :: c_position,string_length
OPEN(UNIT=lun,FILE="st.dat",FORM="FORMATTED",IOSTAT=res)
IF(res/=0) THEN
PRINT*,'error in opening file, status: ',res
STOP
END IF
READ(UNIT=lun,FMT='(A)',IOSTAT=res) cbuffer
IF(res /=0) THEN
PRINT *,'ERROR IN READING file, status: ',res
CLOSE(UNIT=lun)
STOP
END IF
string_length=LEN_TRIM(cbuffer)
c_position=INDEX(cbuffer,':')
READ(cbuffer(c_position+1:string_length),FMT='(I10)') flength
ALLOCATE(dates(flength),water_flow(flength),STAT=res)
IF (res/=0) THEN
PRINT*,'ERROR IN ALLOCATING MEMORY, status:',res
CLOSE(UNIT=lun)
STOP
END IF
DO i=1,flength
READ(UNIT=lun,FMT='(I6,F6.3)') dates(i),water_flow(i)
PRINT*,'DIAS ',dates(i)
END DO
end PROGRAM readhpop
Issues:
water_flow needs to be allocated
When determining flength: flength is an integer, so read it as an integer (here: '(I10)'). The number of dates is expected after a colon, so change st.dat to:
Date Water-flow: 5
717976 7.140
717977 6.570
717978 6.040
717979 5.780
717980 5.530
Loop over flength instead of cbuffer
One read statement too much - you are trying to read beyond the end of the file...
In this line
READ(cbuffer(c_position+1:string_length),FMT='(A10)') flength
you read an integer value under a character edit descriptor. This may well return a value in flength which exceeds any reasonable value for your allocatable array. For example, in a quick test I got a value of 538981169. Change that line to
READ(cbuffer(c_position+1:string_length),'(i)') flength
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