C H A P T E R  2

Fortran Input/Output

This chapter discusses the input/output features provided by the Sun Studio Fortran 95 compiler.


2.1 Accessing Files From Within Fortran Programs

Data is transferred between the program and devices or files through a Fortran logical unit. Logical units are identified in an I/O statement by a logical unit number, a nonnegative integer from 0 to the maximum 4-byte integer value (2,147,483,647).

The character * can appear as a logical unit identifier. The asterisk stands for standard input file when it appears in a READ statement; it stands for standard output file when it appears in a WRITE or PRINT statement.

A Fortran logical unit can be associated with a specific, named file through the OPEN statement. Also, certain preconnected units are automatically associated with specific files at the start of program execution.

2.1.1 Accessing Named Files

The OPEN statement's FILE= specifier establishes the association of a logical unit to a named, physical file at runtime. This file can be pre-existing or created by the program.

The FILE= specifier on an OPEN statement may specify a simple file name (FILE='myfile.out') or a file name preceded by an absolute or relative directory path (FILE='../Amber/Qproj/myfile.out'). Also, the specifier may be a character constant, variable, or character expression.

Library routines can be used to bring command-line arguments and environment variables into the program as character variables for use as file names in OPEN statements.

The following example (GetFilNam.f) shows one way to construct an absolute path file name from a typed-in name. The program uses the library routines GETENV, LNBLNK, and GETCWD to return the value of the $HOME environment variable, find the last non-blank in the string, and determine the current working directory:


      CHARACTER F*128, FN*128, FULLNAME*128
      PRINT*, 'ENTER FILE NAME:'
      READ *, F 
      FN = FULLNAME( F ) 
      PRINT *, 'PATH IS: ',FN
      END 
 
      CHARACTER*128 FUNCTION FULLNAME( NAME ) 
      CHARACTER NAME*(*), PREFIX*128
C          This assumes C shell.
C           Leave absolute path names unchanged. 
C           If name starts with '~/', replace tilde with home 
C           directory; otherwise prefix relative path name with 
C           path to current directory. 
      IF ( NAME(1:1) .EQ. '/' ) THEN 
            FULLNAME = NAME 
      ELSE IF ( NAME(1:2) .EQ. '~/' ) THEN 
            CALL GETENV( 'HOME', PREFIX ) 
            FULLNAME = PREFIX(:LNBLNK(PREFIX)) // 
     1                     NAME(2:LNBLNK(NAME)) 
      ELSE 
            CALL GETCWD( PREFIX ) 
            FULLNAME = PREFIX(:LNBLNK(PREFIX)) // 
     1                     '/' // NAME(:LNBLNK(NAME)) 
      ENDIF 
      RETURN 
      END

Compiling and running GetFilNam.f results in:


demo% pwd
/home/users/auser/subdir
demo% f95 -o getfil GetFilNam.f
demo% getfil
 ENTER FILE NAME:
getfil
 PATH IS: /home/users/auser/subdir/atest.f 
 
demo%

These routines are further described in Section 2.1.4, Passing File Names to Programs. See man page entries for getarg(3F), getcwd(3F), and getenv(3F) for details; these and other useful library routines are also described in the Fortran Library Reference.

2.1.2 Opening Files Without a Name

The OPEN statement need not specify a name; the runtime system supplies a file name according to several conventions.

2.1.2.1 Opened as Scratch

Specifying STATUS='SCRATCH' in the OPEN statement opens a file with a name of the form tmp.FAAAxnnnnn, where nnnnn is replaced by the current process ID, AAA is a string of three characters, and x is a letter; the AAA and x make the file name unique. This file is deleted upon termination of the program or execution of a CLOSE statement. When compiling in FORTRAN 77 compatibility mode (-f77), you can specify STATUS='KEEP' in the CLOSE statement to preserve the scratch file. (This is a non-standard extension.)

2.1.2.2 Already Open

If the file has already been opened by the program, you can use a subsequent OPEN statement to change some of the file's characteristics; for example, BLANK and FORM. In this case, you would specify only the file's logical unit number and the parameters to change.

2.1.2.3 Preconnected or Implicitly Named Units

Three unit numbers are automatically associated with specific standard I/O files at the start of program execution. These preconnected units are standard input, standard output, and standard error:

Typically, standard input receives input from the workstation keyboard; standard output and standard error display output on the workstation screen.

In all other cases where a logical unit number but no FILE= name is specified on an OPEN statement, a file is opened with a name of the form fort.n, where n is the logical unit number.

2.1.3 Opening Files Without an OPEN Statement

Use of the OPEN statement is optional in those cases where default conventions can be assumed. If the first operation on a logical unit is an I/O statement other than OPEN or INQUIRE, the file fort.n is referenced, where n is the logical unit number (except for 0, 5, and 6, which have special meaning).

These files need not exist before program execution. If the first operation on the file is not an OPEN or INQUIRE statement, they are created.

Example: The WRITE in the following code creates the file fort.25 if it is the first input/output operation on that unit:


demo% cat TestUnit.f
      IU=25
      WRITE( IU, '(I4)' ) IU 
      END 
demo% 

The preceding program opens the file fort.25 and writes a single formatted record onto that file:


demo% f95 -o testunit TestUnit.f
demo% testunit
demo% cat fort.25
  25
demo%

2.1.4 Passing File Names to Programs

The file system does not have any automatic facility to associate a logical unit number in a Fortran program with a physical file.

However, there are several satisfactory ways to communicate file names to a Fortran program.

2.1.4.1 Via Runtime Arguments and GETARG

The library routine getarg(3F) can be used to read the command-line arguments at runtime into a character variable. The argument is interpreted as a file name and used in the OPEN statement FILE= specifier:


demo% cat testarg.f
         CHARACTER outfile*40
C  Get first arg as output file name for unit 51
         CALL getarg(1,outfile)
         OPEN(51,FILE=outfile)
         WRITE(51,*) 'Writing to file: ', outfile
         END
demo% f95 -o tstarg testarg.f
demo% tstarg AnyFileName
demo% cat AnyFileName
 Writing to file: AnyFileName
demo%

2.1.4.2 Via Environment Variables and GETENV

Similarly, the library routine getenv(3F) can be used to read the value of any environment variable at runtime into a character variable that in turn is interpreted as a file name:


demo% cat testenv.f
         CHARACTER outfile*40
C  Get $OUTFILE as output file name for unit 51
         CALL getenv('OUTFILE',outfile)
         OPEN(51,FILE=outfile)
         WRITE(51,*) 'Writing to file: ', outfile
         END
demo% f95 -o tstenv testenv.f
demo% setenv OUTFILE EnvFileName
demo% tstenv
demo% cat EnvFileName
 Writing to file: EnvFileName
demo%

When using getarg or getenv, care should be taken regarding leading or trailing blanks. (Fortran 95 programs can use the intrinsic function TRIM, or the older FORTRAN 77 library routine LNBLNK()) Additional flexibility to accept relative path names can be programmed along the lines of the FULLNAME function in the example at the beginning of this chapter.

2.1.4.3 Command-Line I/O Redirection and Piping

Another way to associate a physical file with a program's logical unit number is by redirecting or piping the preconnected standard I/O files. Redirection or piping occurs on the runtime execution command.

In this way, a program that reads standard input (unit 5) and writes to standard output (unit 6) or standard error (unit 0) can, by redirection (using <, >, >>, >&, |, |&, 2>, 2>&1 on the command line), read or write to any other named file.

This is shown in the following table:


TABLE 2-1 csh/sh/ksh Redirection and Piping on the Command Line

Action

Using C Shell

Using Bourne or Korn Shell

Standard input --
read from mydata

myprog < mydata

myprog < mydata

Standard output --
write (overwrite) myoutput

myprog > myoutput

myprog > myoutput

Standard output -- write/append to myoutput

myprog >> myoutput

myprog >> myoutput

Redirect standard error to a file

myprog >& errorfile

myprog 2> errorfile

Pipe standard output to input of another program

myprog1 | myprog2

myprog1 | myprog2

Pipe standard error and output to another program

myprog1 |& myprog2

myprog1 2>&1 | myprog2


See the csh, ksh, and sh man pages for details on redirection and piping on the command line.


2.2 Direct I/O

Direct or random I/O allows you to access a file directly by record number. Record numbers are assigned when a record is written. Unlike sequential I/O, direct I/O records can be read and written in any order. However, in a direct access file, all records must be the same fixed length. Direct access files are declared with the ACCESS='DIRECT' specifier on the OPEN statement for the file.

A logical record in a direct access file is a string of bytes of a length specified by the OPEN statement's RECL= specifier. READ and WRITE statements must not specify logical records larger than the defined record size. (Record sizes are specified in bytes.) Shorter records are allowed. Unformatted, direct writes leave the unfilled part of the record undefined. Formatted, direct writes cause the unfilled record to be padded with blanks.

Direct access READ and WRITE statements have an extra argument, REC=n, to specify the record number to be read or written.

Example: Direct access, unformatted:


      OPEN( 2, FILE='data.db', ACCESS='DIRECT', RECL=200,
&             FORM='UNFORMATTED', ERR=90 ) 
      READ( 2, REC=13, ERR=30 ) X, Y 

This program opens a file for direct access, unformatted I/O, with a fixed record length of 200 bytes, then reads the thirteenth record into X and Y.

Example: Direct access, formatted:


      OPEN( 2, FILE='inven.db', ACCESS='DIRECT', RECL=200, 
&             FORM='FORMATTED', ERR=90 )
      READ( 2, FMT='(I10,F10.3)', REC=13, ERR=30 ) X, Y

This program opens a file for direct access, formatted I/O, with a fixed record length of 200 bytes. It then reads the thirteenth record and converts it with the format (I10,F10.3).

For formatted files, the size of the record written is determined by the FORMAT statement. In the preceding example, the FORMAT statement defines a record of 20 characters or bytes. More than one record can be written by a single formatted write if the amount of data on the list is larger than the record size specified in the FORMAT statement. In such a case, each subsequent record is given successive record numbers.

Example: Direct access, formatted, multiple record write:


      OPEN( 21, ACCESS='DIRECT', RECL=200, FORM='FORMATTED')
      WRITE(21,'(10F10.3)',REC=11) (X(J),J=1,100) 

The write to direct access unit 21 creates 10 records of 10 elements each (since the format specifies 10 elements per record) these records are numbered 11 through 20.


2.3 Binary I/O

Sun Studio Fortran 95 extends the OPEN statement to allow declaration of a "binary" I/O file.

Opening a file with FORM='BINARY' has roughly the same effect as FORM='UNFORMATTED', except that no record lengths are embedded in the file. Without this data, there is no way to tell where one record begins, or ends. Thus, it is impossible to BACKSPACE a FORM='BINARY' file, because there is no way of telling where to backspace to. A READ on a 'BINARY' file will read as much data as needed to fill the variables on the input list.


2.4 Stream I/O

A new "stream" I/O scheme of the Fortran 2003 standard is implemented in f95. Stream I/O access treats a data file as a continuous sequence of bytes, addressable by a positive integer starting from 1. Declare a stream I/O file with the ACCESS='STREAM' specifier on the OPEN statement. File positioning to a byte address requires a POS=scalar_integer_expression specifier on a READ or WRITE statement. The INQUIRE statement accepts ACCESS='STREAM', a specifier STREAM=scalar_character_variable, and POS=scalar_integer_variable.

Stream I/O is very useful when interoperating with files created or read by C programs, as is shown in the following example:


Fortran 95 program reads files created by C fwrite()
 
program reader
 integer:: a(1024), i, result
 open(file="test", unit=8, access="stream",form="unformatted")
! read all of a
 read(8) a
 do i = 1,1024
   if (a(i) .ne. i-1) print *,'error at ', i
 enddo
! read the file backward
 do i = 1024,1,-1
   read(8, pos=(i-1)*4+1) result
   if (result .ne. i-1) print *,'error at ', i
 enddo
 close(8)
end
 
C program writes to a file
 
#include <stdio.h>
int binary_data[1024];
 
/* Create a file with 1024 32-bit integers */
int
main(void)
{
    int i;
    FILE *fp;
 
    for (i = 0; i < 1024; ++i)
        binary_data[i] = i;
    fp = fopen("test", "w");
    fwrite(binary_data, sizeof(binary_data), 1, fp);
    fclose(fp);
}

The C program writes 1024 32-bit integers to a file using C fwrite(). The Fortran 95 reader reads them once as an array, and then reads them individually going backwards through the file. The pos= specifier in the second read statement illustrates that positions are in bytes, starting from byte 1 (as opposed to C, where they start from byte 0).


2.5 Internal Files

An internal file is an object of type CHARACTER such as a variable, substring, array, element of an array, or field of a structured record. Internal file READ can be from a constant character string. I/O on internal files simulates formatted READ and WRITE statements by transferring and converting data from one character object to another data object. No file I/O is performed.

When using internal files:

Example: Sequential formatted read from an internal file (one record only):


demo% cat intern1.f
      CHARACTER X*80 
      READ( *, '(A)' ) X 
      READ( X, '(I3,I4)' ) N1, N2 ! This codeline reads the internal file X
      WRITE( *, * )  N1, N2
      END
demo% f95 -o tstintern intern1.f
demo% tstintern
 12 99
 12 99
demo%

Example: Sequential formatted read from an internal file (three records):


demo% cat intern2.f 
      CHARACTER  LINE(4)*16   
      DATA  LINE(1) / ' 81  81 ' /
      DATA  LINE(2) / ' 82  82 ' /
      DATA  LINE(3) / ' 83  83 ' /
      DATA  LINE(4) / ' 84  84 ' /
      READ( LINE,'(2I4)') I,J,K,L,M,N 
      PRINT *, I, J, K, L, M, N
      END
demo% f95 intern2.f 
demo% a.out 
   81  81  82  82  83  83 
demo%

Example: Direct access read from an internal file (one record), in -f77 compatibility mode:


demo% cat intern3.f 
      CHARACTER LINE(4)*16   
      DATA  LINE(1) / ' 81  81 ' /
      DATA  LINE(2) / ' 82  82 ' /
      DATA  LINE(3) / ' 83  83 ' /
      DATA  LINE(4) / ' 84  84 ' /
      READ ( LINE, FMT=20, REC=3 ) M, N   
20       FORMAT( I4, I4 ) 
      PRINT *, M, N 
      END 
demo% f95 -f77 intern3.f 
demo% a.out 
   83  83 
demo%


2.6 Binary I/O Between Big-Endian and Little-Endian Platforms

A new compiler flag -xfilebyteorder provides support for binary I/O files when moving between SPARC and x86 platforms. The flag identifies the byte-order and byte-alignment of unformatted I/O files.

For example,

-xfilebyteorder=little4:%all,big16:20

would specify that all files (except those opened as "SCRATCH") contain "little-endian" data aligned on 4-byte boundaries (for example 32-bit x86), except for Fortran unit 20, which is a 64-bit "big-endian" file (for example 64-bit SPARC V9).

For details, see the f95(1) man page or the Fortran User's Guide.


2.7 Legacy I/O Considerations

Fortran 95 and legacy Fortran 77 programs are I/O compatible. Executables containing intermixed f77 and f95 compilations can do I/O to the same unit from both the f77 and f95 parts of the program.

However, Fortran 95 provides some additional features:

See the Fortran User's Guide for additional information about Fortran 95 I/O extensions and compatibility between f95 and f77.