Chapter 2. Standard Fortran I/O

The Fortran standard describes program statements that you can use to transfer data between external media (external files) or between internal files and internal storage. It describes auxiliary input/output (I/O) statements that can be used to change the position in the external file or to write an endfile record. It also describes auxiliary I/O statements that describe properties of the connection to a file or that inquire about the properties of that connection.

Files

The Fortran standard specifies the form of the input data that a Fortran program processes and the form of output data resulting from a Fortran program. It does not specifically describe the physical properties of I/O records, files, and units. This section provides a general overview of files, records, and units.

Standard Fortran has two types of files: external and internal. An external file is any file that is associated with a unit number. An internal file is a character variable that is used as the unit specifier in a READ or WRITE statement. A unit is a means of referring to an external file. A unit is connected or linked to a file through the OPEN statement in standard Fortran. An external unit identifier refers to an external file and an internal file identifier refers to an internal file. See “Fortran Unit Identifiers”, for more information about unit identifiers.

A file can have a name that can be specified through the FILE= specifier in a Fortran OPEN statement. If no explicit OPEN statement exists to connect a file to a unit, and if assign(1) was not used, the I/O library uses a form of the unit number as the file name.

Internal Files

Internal files provide a means of transferring and converting text stored in character variables. An internal file must be a character variable or character array. If the file is a variable, the file can contain only one record. If the file is a character array, each element within the array is a record. On output, the record is filled with blanks if the number of characters written to a record is less than the length of the record. An internal file is always positioned at the beginning of the first record prior to data transfer. Internal files can contain only formatted records.

When reading and writing to an internal file, only sequential formatted data transfer statements that do not specify list-directed formatting may be used. Only sequential formatted READ and WRITE statements may specify an internal file.

External Files

In standard Fortran, one external unit may be connected to a file. SGI allows more than one external unit to be connected to the standard input, standard output, or standard error files if the files were assigned with the assign -D command. More than one external unit can be connected to a terminal.

External files have properties of form, access, and position as described in the following text. You can specify these properties explicitly by using an OPEN statement on the file. The Fortran standard provides specific default values for these properties.

  • Form (formatted or unformatted): external files can contain formatted or unformatted records. Formatted records are read or written by formatted I/O data transfer statements. Unformatted records are accessed through unformatted I/O data transfer statements. If the default does not match the form needed, you can specify the form by using an OPEN statement.

  • File access (sequential or direct access): external files can be accessed through sequential or direct access methods. The file access method is determined when the file is connected to a unit.

    • Sequential access does not require an explicit open of a file by using an OPEN statement.

      When connected for sequential access, the external file has the following properties:

      • The records of the file are either all formatted or unformatted, except that the last record of the file may be an endfile record.

      • The records of the file must not be read or written by direct-access I/O statements when the file is opened for sequential access.

      • If the file is created with sequential access, the records are stored in the order in which they are written (that is, sequentially).

      To use sequential access on a file that was created as a formatted direct-access file, open the file as sequential. To use sequential access on a file that was created as an unformatted direct-access file, open the file as sequential, and use the assign command on the file as follows:

      assign -s unblocked ...

      The assign command is required to specify the type of file structure. The I/O libraries need this information to access the file correctly.

      Buffer I/O files are unformatted sequential access files.

    • Direct access does require an explicit open of a file by using an OPEN statement. If a file is accessed through a sequential access READ or WRITE statement, the I/O library implicitly opens the file. During an explicit or implicit open of a file, the I/O library tries to access information generated by the assign(1) command for the file.

      Direct access can be faster than sequential access when a program must access a set of records in a nonsequential manner.

      When connected for direct access, an external file has the following properties:

      • The records of the file are either all formatted or all unformatted. If the file can be accessed as a sequential file, the endfile record is not considered part of the file when it is connected for direct access. Some sequential files do not contain a physical endfile record.

      • The records of the file must not be read or written by sequential-access I/O statements while the file is opened for direct access.

      • All records of the file have the same length, which is specified in the RECL specifier of the OPEN statement.

      • Records do not have to be read or written in the order of their record numbers.

      • The records of the file must not be read or written using list-directed or namelist formatting.

      • The record number (a positive integer) uniquely identifies each record.

      If all of the records in the file are the same length and if the file is opened as direct access, a formatted sequential-access file can be accessed as a formatted direct-access file if the direct access file is assigned a text structure (with assign -s text).

      Unformatted sequential-access files can be accessed as unformatted direct-access files if all of the records are the same length and if the file is opened as direct access, but only if the sequential-access file was created with an unblocked file structure. The following assign commands create these file structures:

      assign -s unblocked ...
      assign -s u ...
      assign -F system ...

      For more information about the assign environment and about default file structures, see Chapter 6, “The assign Environment”.

  • File position: a file connected to a unit has a position property, which can be either an initial point or a terminal point. The initial point of a file is the position just before the first record, and the terminal point is the position just after the last record. If a file is positioned within a record, that record is considered to be the current record; otherwise, there is no current record.

    During an I/O data transfer statement, the file can be positioned within a record as each individual input/output list (iolist) item is processed. The use of a dollar sign ($) or a backslash (\) as a carriage control edit descriptor in a format may cause a file to be positioned within a record.

    In standard Fortran, the end-of-file (EOF) record is a special record in a sequential access file; it denotes the last record of a file. A file can be positioned after an EOF, but only CLOSE, BACKSPACE, or REWIND statements are then allowed on the file in standard Fortran. Other I/O operations are allowed after an EOF to provide multiple-file I/O if a file is assigned to certain devices or is assigned with a certain file structure.

Fortran Unit Identifiers

A Fortran unit identifier is required for Fortran READ or WRITE statements to uniquely identify the file. A unit identifier can be one of the following:

  • An integer variable or expression whose value is greater than or equal to 0. Each integer unit identifier i is associated with the fort.i file, which may exist (except as noted in the following text). For example, unit 10 is associated with the fort.10 file in the current directory.

  • An asterisk (*) is allowed only on READ and WRITE statements. It identifies a particular file that is connected for formatted, sequential access. On READ statements, an asterisk refers to unit 100 (standard input). On WRITE statements, an asterisk refers to unit 101 (standard output).

Certain Fortran I/O statements have an implied unit number. The PRINT statement always refers to unit 101 (standard output), and the outmoded PUNCH statement always refers to unit 102 (standard error).

Fortran INQUIRE and CLOSE statements may refer to any valid or invalid unit number (if referring to an invalid unit number, no error is returned). All other Fortran I/O statements may refer only to valid unit numbers. For the purposes of an executing Fortran program, all unit numbers in use or available for use by that program are valid; that is, they exist. All unit numbers not available for use are not valid; that is, they do not exist.

Valid unit numbers are all nonnegative numbers except 100 through 102. Unit numbers 0, 5, and 6 are associated with the standard error, standard input, and standard output files; any unit can also refer to a pipe. All other valid unit numbers are associated with the fort. i file, or with the file name implied in a Hollerith unit number. Use the INQUIRE statement to check the validity (existence) of any unit number prior to using it, as in the following example:

logical UNITOK, UNITOP...
inquire (unit=I,exist=UNITOK,opened=UNITOP)
if (UNITOK .and. .not. UNITOP) then
  open (unit = I, ...)
endif

All valid units are initially closed. A unit is connected to a file as the result of one of three methods of opening a file or a unit:

  • An implicit open occurs when the first reference to a unit number is an I/O statement other than OPEN, CLOSE, INQUIRE , BACKSPACE, ENDFILE, or REWIND. The following example shows an implicit open:

    WRITE (4) I,J,K

    If unit number 4 is not open, the WRITE statement causes it to be connected to the associated file fort.4, unless overridden by an assign command that references unit 4.

    The BACKSPACE, ENDFILE, and REWIND statements do not perform an implicit OPEN. If the unit is not connected to a file, the requested operation is ignored.

  • An explicit unnamed open occurs when the first reference to a unit number is an OPEN statement without a FILE specifier. The following example shows an explicit unnamed open:

    OPEN (7, FORM='UNFORMATTED')

    If unit number 7 is not open, the OPEN statement causes it to be connected to the associated file fort.7 , unless an assign(1) command that references unit 7 overrides the default file name.

  • An explicit named open occurs when the first reference to a unit number is an OPEN statement with a FILE specifier. The following is an example:

    OPEN (9, FILE='blue')

    If unit number 9 is not open, the OPEN statement causes it to be connected to file blue, unless overridden by an assign command that references the file named blue.

Unit numbers 100, 101, and 102 are permanently associated with the standard input, standard output, and standard error files, respectively. These files can be referenced on READ and WRITE statements. A CLOSE statement on these unit numbers has no effect. An INQUIRE statement on these unit numbers indicates they are nonexistent (not valid).

These unit numbers exist to allow guaranteed access to the standard input, standard output, and standard error files without regard to any unit actions taken by an executing program. Thus, a READ or WRITE I/O statement with an asterisk unit identifier (which is equivalent to unit 101) or a PRINT statement always works. Nonstandard I/O operations such as BUFFER IN and BUFFER OUT , READMS, and WRITMS on these units are not supported.

Fortran applications or library subroutines that must access the standard input, standard output, and standard error files can be certain of access by using unit numbers 100 through 102, even if the user program closes or reuses unit numbers 0, 5, and 6.

For all unit numbers associated with the standard input, standard output, and standard error files, the access mode and form must be sequential and formatted. The standard input file is read only, and the standard output and standard error files are write only. REWIND and BACKSPACE statements are permitted on these files but have no effect. ENDFILE statements are permitted on terminal files unless they are read only. The ENDFILE statement writes a logical endfile record.

The REWIND statement is not valid for any unit numbers associated with pipes. The BACKSPACE statement is not valid if the device on which the file exists does not support repositioning. BACKSPACE after a logical endfile record does not require repositioning because the endfile record is only a logical representation of an endfile record.

Data Transfer Statements

The READ statement is the data transfer input statement. The WRITE and PRINT statements are the data transfer output statements. If the data transfer statement contains a format specifier, the data transfer statement is a formatted I/O statement. If the data transfer statement does not contain a format specifier, the data transfer statement is an unformatted I/O statement. The time required to convert input or output data to the proper form adds to the execution time for formatted I/O statements. Unformatted I/O maintains binary representations of the data. Very little CPU time is required for unformatted I/O compared to formatted I/O.

Formatted I/O

In formatted I/O, data is transferred with editing. Formatted I/O can be edit-directed, list-directed, and namelist I/O. If the format identifier is an asterisk, the I/O statement is a list-directed I/O statement. All other format identifiers indicate edit-directed I/O.

Formatted I/O should be avoided when I/O performance is important. Unformatted I/O is faster and it avoids potential inaccuracies due to conversion. However, there are occasions when formatted I/O is necessary. The advantages for formatted I/O are as follows:

  • Formatted data can be interpreted by humans.

  • Formatted data can be readily used by programs and utilities not written in Fortran, or otherwise unable to process Fortran unformatted files.

  • Formatted data can be readily exchanged with other computer systems where the structure of Fortran unformatted files may be different.

See the Fortran Language Reference manuals for your compiler system for more information about formatted I/O statements.

Edit-directed I/O

The format used in an edit-directed I/O statement provides information that directs the editing between internal representation and the character strings of a record (or sequence of records) in the file.

An example of a sequential access, edit-directed WRITE statement follows:

C  Sequential edit-directed WRITE statement
C
   WRITE (10,10,ERR=101,IOSTAT=IOS) 100,200
10 FORMAT (TR2,I10,1X,I10)

An example of a sequential access, edit-directed READ statement follows:

C  Sequential edit-directed READ statement
C
   READ (10,11,END=99,ERR=102,IOSTAT=IOS) IVAR
11 FORMAT (BN,TR2,I10:1X,I10)

An example of a direct access edit-directed I/O statement follows:

   OPEN (11,ACCESS='DIRECT',FORM='FORMATTED',
+  RECL=24)
C
C  Direct edit-directed READ and WRITE statements
C
   WRITE (11,10,REC=3,ERR=103,IOSTAT=IOS) 300,400
   READ (11,11,REC=3,ERR=104,IOSTAT=IOS) IVAR   

There are four general optimization techniques that you can use to improve the efficiency of edit-directed formatted I/O.

Procedure 2-1. Optimization technique: using single statements

Read or write as much data with a single READ/WRITE/PRINT statement as possible. The following is an example of an inefficient way to code a WRITE statement:

    DO J=1,M
       DO I=1,N
         WRITE (42, 100) X(I,J)
100      FORMAT (E25.15)
       ENDDO
    ENDDO

It is better to write the entire array with a single WRITE statement, as is done in the following two examples:

    WRITE (42, 100) ((X(I,J),I=1,N),J=1,M)
100 FORMAT (E25.15)

or

    WRITE (42, 100) X
100 FORMAT (E25.15)

Each of these three code fragments produce exactly the same output; although the latter two are about twice as fast as the first. Note that the format can be used to control how much data is written per record. Also, the last two cases are equivalent if the implied DO loops write out the entire array, in order and without omitting any items.

    Procedure 2-2. Optimization technique: using longer records

    Use longer records if possible. Because a certain amount of processing is necessary to read or write each record, it is better to write a few longer records instead of more shorter records. For example, changing the statement from Example 1 to Example 2 causes the resulting file to have one fifth as many records and, more importantly, causes the program to execute faster:

    Example 1: (Not recommended)

        WRITE (42, 100) X
    100 FORMAT (E25.15)

    Example 2: (Recommended)

        WRITE (42,101) X
    101 FORMAT (5E25.15)

    You must make sure that the resultant file does not contain records that are too long for the intended application. Certain text editors and utilities, for example, cannot process lines that are longer than a predetermined limit. Generally lines that are 128 characters or less are safe to use in most applications.

      Procedure 2-3. Optimization technique: using repeated edit descriptors

      Use repeated edit descriptors whenever possible. Instead of using the format in Example 1, use the format in Example 2 for integers which fit in four digits (that is, less than 10000 and greater than -1000).

      Example 1: (Not recommended)

      200 FORMAT (16(X,I4))

      Example 2: (Recommended)

      201 FORMAT (16(I5))

        Procedure 2-4. Optimization technique: using data edit descriptors

        Character data should be read and written using data edit descriptors that are the same width as the character data. For CHARACTER* n variables, the optimal data edit descriptor is A (or A n). For Hollerith data in INTEGER variables, the optimal data edit descriptor is A8 (or R8).

          List-directed I/O

          If the format specifier is an asterisk, list-directed formatting is specified. The REC= specifier must not be present in the I/O statement.

          In list-directed I/O, the I/O records consist of a sequence of values separated by value separators such as commas or spaces. A tab is treated as a space in list-directed input, except when it occurs in a character constant that is delimited by apostrophes or quotation marks.

          List-directed and namelist output of real values uses either an F or an E format with a number of decimal digits of precision that assures full-precision printing of the real values. This allows formatted, list-directed, or namelist input of real values to result later in the generation of bit-identical binary floating point representation. Thus, a value may be written and then reread without changing the stored value.

          The LISTIO_PRECISION and LISTIO_OUTPUT_STYLE environment variables can be used to control list-directed output, as discussed in the following paragraphs.

          You can set the LISTIO_PRECISION environment variable to control the number of digits of precision printed by list-directed or namelist output. The following values can be assigned to LISTIO_PRECISION :

          FULL

          Prints full precision (this is the default value).

          PRECISION

          Prints x or x +1 decimal digits, where x is a value of the Fortran 95 PRECISION() intrinsic function for a given real value. This is a smaller number of digits that usually ensures that the last decimal digit is accurate to within 1 unit.

          An example of a list-directed WRITE statement follows:

          C  Sequential list-directed WRITE statement
             WRITE (10,*,ERR=101,IOSTAT=IOS) 100,200

          An example of a list-directed READ statement follows:

          C  Sequential list-directed READ statement
             READ (10,*,END=99,ERR=102,IOSTAT=IOS) IVAR

          Namelist I/O

          Namelist I/O is similar to list-directed I/O, but it allows you to group variables by specifying a namelist group name. On input, any namelist item within that list may appear in the input record with a value to be assigned. On output, the entire namelist is written.

          The namelist item name is used in the namelist input record to indicate the namelist item to be initialized or updated. During list-directed input, the input records must contain a value or placeholder for all items in the input list. Namelist does not require that a value be present for each namelist item in the namelist group.

          You can specify a namelist group name in READ, WRITE, and PRINT statements.

          The following is an example of namelist I/O:

          NAMELIST/GRP/T,I
          READ(5,GRP)
          WRITE(6,GRP)

          Unformatted I/O

          During unformatted I/O, binary data is transferred without editing between the current record and the entities specified by the I/O list. Exactly one record is read or written. The unit must be an external unit.

          The following is an example of a sequential access unformatted I/O WRITE statement:

          C  Sequential unformatted WRITE statement
             WRITE (10,ERR=101,IOSTAT=IOS) 100,200

          The following is an example of a sequential access unformatted I/O READ statement:

          C  Sequential unformatted READ statement
             READ (10,END=99,ERR=102,IOSTAT=IOS) IVAR

          The following is an example of a direct access unformatted I/O statement:

             OPEN (11,ACCESS='DIRECT',FORM='UNFORMATTED', RECL=24)
          C  Direct unformatted READ and WRITE statements
             WRITE (11,REC=3,ERR=103,IOSTAT=IOS) 300,400
             READ (11,REC=3,ERR=103,IOSTAT=IOS) IVAR

          Auxiliary I/O

          The auxiliary I/O statements consist of the OPEN, CLOSE, INQUIRE, BACKSPACE, REWIND, and ENDFILE statements. These types of statements specify file connections, describe files, or position files. See the Fortran Language Reference manual for your compiler system for more details about auxiliary I/O statements.

          File Connection Statements

          The OPEN and CLOSE statements specify an external file and how to access the file.

          An OPEN statement connects an existing file to a unit, creates a file that is preconnected, creates a file and connects it to a unit, or changes certain specifiers of a connection between a file and a unit. The following are examples of the OPEN statement:

          OPEN (11,ACCESS='DIRECT',FORM='FORMATTED',RECL=24)
          OPEN (10,ACCESS='SEQUENTIAL', FORM='UNFORMATTED')
          OPEN (9,BLANK='NULL')

          The CLOSE statement terminates the connection of a particular file to a unit. A unit that does not exist or has no file connected to it may appear within a CLOSE statement; this would not affect any files.

          The INQUIRE Statement

          The INQUIRE statement describes the connection to an external file. This statement can be executed before, during, or after a file is connected to a unit. All values that the INQUIRE statement assigns are current at the time that the statement is executed.

          You can use the INQUIRE statement to check the properties of a specific file or check the connection to a particular unit. The two forms of the INQUIRE statement are INQUIRE by file and INQUIRE by unit.

          The INQUIRE by file statement retrieves information about the properties of a particular file.

          The INQUIRE by unit statement retrieves the name of a file connected to a specified unit if the file is a named file. The standard input, standard output, and standard error files are unnamed files. An INQUIRE on a unit connected to any of these files indicates that the file is unnamed.

          An INQUIRE by unit on any unit connected by using an explicit named OPEN statement indicates that the file is named, and returns the name that was present in the FILE= specifier in the OPEN statement.

          An INQUIRE by unit on any unit connected by using an explicit unnamed OPEN statement, or an implicit open may indicate that the file is named. A name is returned only if the I/O library can ensure that a subsequent OPEN statement with a FILE= name will connect to the same file.

          File Positioning Statements

          The BACKSPACE and REWIND statements change the position of the external file. The ENDFILE statement writes the last record of the external file.

          You cannot use file positioning statements on a file that is connected as a direct access file. The REC= record specifier is used for positioning in a READ or WRITE statement on a direct access file.

          The BACKSPACE statement causes the file connected to the specified unit to be positioned to the preceding record. The following are examples of the BACKSPACE statement:

          BACKSPACE 10
          BACKSPACE (11, IOSTAT=ios, ERR=100)
          BACKSPACE (12, ERR=100)
          BACKSPACE (13, IOSTAT=ios)

          The ENDFILE statement writes an endfile record as the next record of the file. The following are examples of the ENDFILE statement:

          ENDFILE 10
          ENDFILE (11, IOSTAT=ios, ERR=100)
          ENDFILE (12, ERR=100)
          ENDFILE (13, IOSTAT=ios)

          The REWIND statement positions the file at its initial point. The following are examples of the REWIND statement:

          REWIND 10
          REWIND (11, IOSTAT=ios, ERR=100)
          REWIND (12, ERR=100)
          REWIND (13, IOSTAT=ios)
          REWIND (14)

          Multithreading and Standard Fortran I/O

          Multithreading is the concurrent use of multiple threads of control which operate within the same address space. Multithreading is available through DOACROSS compiler directives and through the Pthreads interface.

          Standard Fortran I/O is thread-safe. The runtime I/O library performs all the needed locking to permit multiple threads to concurrently execute Fortran I/O statements. The result is proper execution of all Fortran I/O statements and the sequential execution of I/O statements issued across multiple threads to files opened for sequential access.