Fortran does not have an intrinsic that returns the size of a file, but with a modern compiler the answer to this question has gotten much simpler than it used to be. For most external files you can query the size with an INQUIRE(3f):
use :: iso_fortran_env, only : FILE_STORAGE_SIZE
implicit none
character(len=:),allocatable :: filename
integer :: file_size
filename='test.txt'
INQUIRE(FILE=filename, SIZE=file_size) ! return -1 if cannot determine file size
write(*,*)'size of file '//filename//' is ',file_size * FILE_STORAGE_SIZE /8,' bytes'
end
There are some dusty corners where this might not return what you expect on some systems, especially if the file is currently open as a direct access or stream file or is a soft link; but essentially every method has problems with special file types. I have not had the INQUIRE(SIZE=...) statement fail on regular external files.
If the INQUIRE(3f) statement does not yet work with SIZE= in your programming environment, there are several alternative methods for obtaining system file information (some work for far more than just file size), each with advantages and disadvantages:
* using non-standard extensions
* opening a file at EOF and reading position
* call C routines via ISO_C_BINDING module
* calling system command and reading command output
* reading the entire file and counting line lengths and lines
If you are not concerned about portability many compilers support at least a subset of the POSIX system interface routines. Look for routines like STAT(3f) or PXFSTAT(3f).
Depending on what vintage of fortran you have available, if you OPEN(3f) the file with POSITION='APPEND' and then use INQUIRE(3f) to query the position of a file you get the size of the file assuming it is a basic external file. You cannot use this to query the size of some types of files such as files being piped to your process or other files where positioning the files to their end position really does not apply.
So far (f2008) it is not standard to open a file that is already open, so the example FILESIZE(3f) procedure in the following example has to be used on files that are not open. The routine could be extended to use INQUIRE(3f) to detect this (by checking if the file is already open).
program file_size
implicit none
character(len=:),allocatable :: filename
integer :: filename_length, ios, nchars, count, ierr
do count = 1,command_argument_count()
! get filename from command line
call get_command_argument(number=count,length=filename_length,status=ios) ! get command line length
if(ios.ne.0)then
stop '*file_size* ERROR: filenames must be specified on command line'
endif
allocate(character(len=filename_length) :: filename) ! allocate string big enough to hold command line
call get_command_argument(number=count,value=filename) ! get command line as a string
filename=trim(adjustl(filename)) ! trim leading spaces just in case
if(filename.eq.'')then
write(*,'(a)')'*file_size* ERROR: blank filename '
cycle
endif
! call routine that should get size of file in bytes
call filesize(filename,nchars,ierr)
if(ierr.ne.0)then
write(*,'("*file_size* ERROR: ierr=",i0," for file ",a)')ierr,filename
elseif(nchars.le.0)then
write(*,'(a)')'empty file '//trim(filename)
else
write(*,'(a," is ",i0," bytes")')trim(filename),nchars
endif
deallocate(filename)
enddo
end program file_size
subroutine filesize(filename,nchars,ierr)
implicit none
character(len=*),intent(in) :: filename
integer,intent(out) :: nchars
integer,intent(out) :: ierr
character(len=256) :: message
integer :: lun, ios
nchars=0
ierr=0
! open named file in stream mode positioned to append
open (newunit=lun, &
& file=trim(filename), &
& access='stream', &
& status='old', &
& position='append', &
& iomsg=message, &
iostat=ios)
if(ios.eq.0)then ! if file was successfully opened
! get file size in bytes and position file to beginning of file
inquire(unit=lun,pos=nchars) ! get number of bytes in file plus one
nchars=nchars-1 ! opened for append, so subtract one to get current length
else
write(*,'("*error*:",a)')message
endif
ierr=ios
end subroutine filesize
file_size *
empty file asdf
block_comments.md is 31712 bytes
character_array_initialization.html is 7239 bytes
comments.html is 5713 bytes
compare_arrays.html is 6337 bytes
contained.html is 4272 bytes
faq.html is 2782 bytes
file_size.ff is 3464 bytes
file_size.ff~ is 3483 bytes
nan.md is 34 bytes
row-column.html is 7982 bytes
scratch.html is 28136 bytes
zero_elements.html is 7485 bytes
With modern Fortran it is relatively standard and portable to call C routines. There is an extensive interface in module M_sytem(3f) in the GPF(General Purpose Fortran) collection that includes the procedure SYSTEM_STAT(3f) which, among other things, calls stat(3c) and returns system file information including file size.
Although what system commands are available varies between programming environments, you can generally call a system command that prints the file size (such as stat(1), ls(1), dir(1), find(1), wc(1), ...) and read the command input.
The stat(1) command on Unix and GNU/Linux systems can be used to return many external file attributes. This is just a simple example. Note that a more robust method for getting a scratch file than just using the name "_scratch" would be needed in any production version.
program read_command
implicit none
character(len=:),allocatable :: filename,cmd
character(len=256) :: message=''
integer :: lun, ios=0, nchars=0, icmd, iexit
! assume a file called "test.txt" exists
filename='test.txt'
! system command to execute
cmd="stat --dereference --format='%s' "//filename//'>_scratch'
! if you do not have execute_command_line(3f) look for a system(3f) procedure
call execute_command_line(command=cmd,exitstat=iexit,cmdstat=icmd,cmdmsg=message)
if(iexit.ne.0.or.icmd.ne.0)then
write(*,*)'*read_command* error '//trim(message)
else
open(newunit=lun,file='_scratch',iostat=ios) ! you would want to trap errors here
if(ios.eq.0)then
read(lun,*)nchars ! you would want to trap errors here
endif
!!close(unit=lun,status='delete',iostat=ios)
endif
write(*,'(a,i0,a)')' file '//filename//' is ',nchars,' bytes'
end program read_command
file test.txt is 938 bytes
One reason you might do this even with a modern Fortran version is to get the number of lines in a sequential file. For example:
program count_lines
implicit none
integer :: line_count, ios
line_count=0
open(unit=10,file='test.txt')
do
read(10,*,iostat=ios) ! note there is no list of variables
if(ios.ne.0)exit
line_count=line_count+1
enddo
write(*,*)'file has ',count,' lines'
end program count_lines
In modern Fortran in addition to INQUIRE(3f) with SIZE= you can open a file as a stream and read one character at a time and (assuming you know what the line terminator is for the file) count lines and words and characters; but in older FORTRAN there were no standard ADVANCE='NO' options on READ(3f), no stream I/O, and no _INQUIRE(3f) parameters to easily give you file size.
The trick in older Fortran versions was generally to open the file as a direct-access file with RECL=1 on the OPEN(3f). One problem was that the units for RECL were not always one byte; they were often 4 bytes or more, but there was usually a compiler option to make the unit 1 byte. Then you just read the file from beginning to end. I would replace any such code with the INQUIRE(3f) statement using SIZE=.
CHARACTER C , FILENAME*256
ISIZE=1
FILENAME='test.txt'
OPEN(10,FILE=FILENAME,IOSTAT=IOS,
$ACCESS='DIRECT',FORM='UNFORMATTED',STATUS='OLD',RECL=1)
IF(IOS.NE.0)THEN
WRITE(*,*)'I/O ERROR: ',IOS, ' for ',FILENAME
STOP
ENDIF
1 CONTINUE
READ(10,IOSTAT=IOS,REC=ISIZE,ERR=999)C
IF (IOS.NE.0) THEN
WRITE(*,*)'ERROR ',IOS
STOP ! Some sort of error.
ELSE
ISIZE=ISIZE+1
END IF
GOTO 1
999 CONTINUE
ISIZE=ISIZE-1
WRITE(*,*)'File ',FILENAME(:JULEN(FILENAME)),' is ',ISIZE,' bytes'
END
INTEGER FUNCTION JULEN(STRING)
C @(#) return position of last non-blank character in "string".
C if the string is blank, a length of 0 is returned.
C
CHARACTER STRING*(*)
CHARACTER NULL*1
INTRINSIC LEN
NULL=CHAR(0)
ILEN=LEN(STRING)
IF(ILEN.GE.1)THEN ! CHECK FOR NULL STRINGS
DO 10 I10=ILEN,1,-1
IF(STRING(I10:I10).NE.' '.AND.STRING(I10:I10).NE.NULL)THEN
JULEN=I10
RETURN
ENDIF
10 CONTINUE
ENDIF
JULEN=0
RETURN
END