A formatted stream file is equivalent to a C text stream; this acts much like an ordinary sequential file, except that there is no limit on the length of a record. Just as in C, when writing to a formatted stream, an embedded newline character in the data causes a new record to be created. The new intrinsic enquiry function NEW_LINE(A) returns this character for the kind (character set) of A; if the character set is ASCII, this is equal to IACHAR(10). For example,
OPEN(17,FORM='formatted',ACCESS='stream',STATUS='new') WRITE(17,'(A)'), 'This is record 1.'//NEW_LINE('A')//'This is record 2.'
An unformatted stream file is equivalent to a C binary stream, and has no record boundaries. This makes it impossible to BACKSPACE an unformatted stream. Data written to an unformatted stream is transferred to the file with no formatting, and data read from an unformatted stream is transferred directly to the variable as it appears in the file.
When reading or writing a stream file, the POS= specifier may be used to specify where in the file the data is to be written. The first character of the file is at position 1. The POS= specifier may also be used in an INQUIRE statement, in which case it returns the current position in the file. When reading or writing a formatted stream, the POS= in a READ or WRITE shall be equal to 1 (i.e. the beginning of the file) or to a value previously discovered through INQUIRE.
Note that unlike unformatted sequential files, writing to an unformatted stream file at a position earlier than the end of the file does not truncate the file. (However, this truncation does happen for formatted streams.)
Finally, the STREAM= specifier has been added to the INQUIRE statement. This specifier takes a scalar default character variable, and assigns it the value 'YES' if the file may be opened for stream input/output (i.e. with ACCESS='STREAM'), the value 'NO' if the file cannot be opened for stream input/output, and the value 'UNKNOWN' if it is not known whether the file may be opened for stream input/output.
When the mode is DECIMAL='COMMA', all floating-point output will produce a decimal comma instead of a decimal point, and all floating-point input will expect a decimal comma. For example,
PRINT '(1X,"Value cest ",DC,F0.2)',1.25will produce the output
Value cest 1,25
Additionally, in this mode, a comma cannot be used in list-directed input to separate items; instead, a semi-colon may be used.
WRITE(*,*,DELIM='QUOTE') "That's all folks!"will produce the output
'That''s all folks!'
The effect of SIGN='PLUS' is the same as the SP edit descriptor, the effect of SIGN='SUPPRESS' is the same as the SS edit descriptor, and the effect of SIGN='PROCESSOR_DEFINED' is the same as the S edit descriptor.
Input of IEEE infinities and NaNs is now possible; these take the same form as the output described above, except that:
Input/output to an external file while external file input/output is already in progress remains prohibited, except for the case of nested data transfer (see “Defined input/output”).
Except for the INQUIRE statement, the ASYNCHRONOUS= specifier takes a scalar default character expression; this must evaluate either to 'YES' or 'NO', treating lowercase the same as uppercase. In the READ and WRITE statements, this character expression must be a constant expression, and the statement must refer to an external file whose connection allows asynchronous input/output; if ASYNCHRONOUS='YES' is specified, the data transfer may occur asynchronously. In the OPEN statement, this specifier determines whether asynchronous data transfer is allowed for that file: the default setting is 'NO'. For the INQUIRE statement, the ASYNCHRONOUS= specifier takes a scalar default character variable, and sets it to 'YES' if the file is currently connected for asynchronous input/output, 'NO' if the current connection does not allow asynchronous input/output and 'UNKNOWN' if the file is not connected.
For the READ and WRITE statements, the ID= specifier takes a scalar integer variable. This specifier is only permitted if ASYNCHRONOUS='YES' also appears. The integer variable is assigned the “identifier” of the asynchronous data transfer that the READ or WRITE initiates; this value can be used in INQUIRE and WAIT statements to track the progress of the asynchronous data transfer.
For the INQUIRE statement, the ID= specifier takes a scalar integer expression whose value must be that returned from ID= on a READ or WRITE statement for that file, and is only permitted in conjunction with the PENDING= specifier. The PENDING= specifier takes a scalar default logical variable and sets it to .TRUE. if the specified asynchronous data transfer is still underway and to .FALSE. if it has completed. If PENDING= is used without ID=, the enquiry is about all outstanding asynchronous data transfer on that file.
After initiating an asynchronous data transfer, the variables affected must not be referenced or defined until after the transfer is known to have finished. For an asynchronous WRITE of a local variable, this means not returning from the procedure until after ensuring the transfer is complete. An asynchronous data transfer is known to have been finished if there is a subsequent synchronous data transfer, an INQUIRE statement which returns .FALSE. for PENDING=, or a WAIT statement has been executed; in each case, for that file.
REAL :: buf1(n,m),buf2(n,m) LOGICAL more,stillwaiting READ (unit) buf1 DO READ (unit) more ! Note: synchronous IF (more) READ (unit,ASYNCHRONOUS='YES') buf2 CALL process(buf1) IF (.NOT.more) EXIT READ (unit) more ! Note: synchronous IF (more) READ (unit,ASYNCHRONOUS='YES') buf1 CALL process(buf2) IF (.NOT.more) EXIT END DO
Note that the synchronous READ statements automatically “wait” for any outstanding asynchronous data transfer to complete before reading the logical value; this ensures that the dataset will have finished being read into its buffer and is safe to process.
The ASYNCHRONOUS attribute may be explicitly specified in a type declaration statement or in an ASYNCHRONOUS statement. The latter has the syntax
ASYNCHRONOUS [::] variable-name [ , variable-name ]...
If a variable with the ASYNCHRONOUS attribute is a dummy array and is not an assumed-shape array or array pointer, any associated actual argument cannot be an array section, an assumed-shape array or array pointer. Furthermore, if a dummy argument has the ASYNCHRONOUS attribute the procedure must have an explicit interface. Both of these restrictions apply whether the attribute was given explicitly or implicitly.
WAIT ( wait-spec [ , wait-spec ]... )
where wait-spec is one of the following:
UNIT = file-unit-number
END = label
EOR = label
ERR = label
ID = scalar-integer-variable
IOMSG = scalar-default-character-variable
IOSTAT = scalar-integer-variable
The UNIT= specifier must appear, but the ‘UNIT =’ may be omitted if it is the first specifier in the list. The ID= specifier takes a scalar integer expression whose value must be that returned from ID= on a READ or WRITE statement for the file; if the ID= specifier does not appear the WAIT statement refers to all pending asynchronous data transfer for that file.
On completion of execution of the WAIT statement the specified asynchronous data transfers have been completed. If the specified file is not open for asynchronous input/output or is not connected, the WAIT statement has no effect (it is not an error unless the ID= specifier was used with an invalid value).
Here is an example of using the WAIT statement.
REAL array(1000,1000,10),xferid(10) ! Start reading each segment of the array DO i=1,10 READ (unit,id=xfer(i)) array(:,:,i) END DO ... ! Now process each segment of the array DO i=1,10 WAIT (unit,id=xfer(i)) CALL process(array(:,:,i) END DO
FLUSH file-unit-number
FLUSH ( flush-spec [ , flush-spec ]... )
UNIT = file-unit-number
IOSTAT = scalar-integer-variable
IOMSG = scalar-default-character-variable
ERR = label
The UNIT= specifier must appear, but the ‘UNIT =’ may be omitted if it is the first flush-spec in the list.
Here is an example of the use of a FLUSH statement.
WRITE (pipe) my_data FLUSH (pipe)
Here is a type definition with defined input/output procedures.
TYPE tree TYPE(tree_node),POINTER :: first CONTAINS PROCEDURE :: fmtread=>tree_fmtread PROCEDURE :: fmtwrite=>tree_fmtwrite GENERIC,PUBLIC :: READ(formatted)=>fmtread, WRITE(formatted)=>fmtwrite END TYPE
Given the above type definition, whenever a TYPE(tree) object is an effective item in a formatted input/output list, the module procedure tree_fmtread will be called (in a READ statement) or the module procedure tree_fmtwrite will be called (in a WRITE statement) to perform the input or output of that object. Note that a generic interface block may also be used to declare procedures for defined input/output; this is the only option for sequence or BIND(C) types but is not recommended for extensible types.
The procedures associated with each input/output generic identifier must have the same characteristics as the ones listed below, where type-declaration is CLASS(derived-type-spec) for an extensible type and TYPE(derived-type-spec) for a sequence or BIND(C) type. Note that if the derived type has any length type parameters, they must be “assumed” (specified as ‘*’).
SUBROUTINE formatted_read(var,unit,iotype,vlist,iostat,iomsg)
type-declaration,INTENT(INOUT) :: var
INTEGER,INTENT(IN) :: unit
CHARACTER(*),INTENT(IN) :: iotype
INTEGER,INTENT(IN) :: vlist(:)
INTEGER,INTENT(OUT) :: iostat
CHARACTER(*),INTENT(INOUT) :: iomsg
SUBROUTINE unformatted_read(var,unit,iostat,iomsg)
type-declaration,INTENT(INOUT) :: var
INTEGER,INTENT(IN) :: unit
INTEGER,INTENT(OUT) :: iostat
CHARACTER(*),INTENT(INOUT) :: iomsg
SUBROUTINE formatted_write(var,unit,iotype,vlist,iostat,iomsg)
type-declaration,INTENT(IN) :: var
INTEGER,INTENT(IN) :: unit
CHARACTER(*),INTENT(IN) :: iotype
INTEGER,INTENT(IN) :: vlist(:)
INTEGER,INTENT(OUT) :: iostat
CHARACTER(*),INTENT(INOUT) :: iomsg
SUBROUTINE unformatted_write(var,unit,iostat,iomsg)
type-declaration,INTENT(IN) :: var
INTEGER,INTENT(IN) :: unit
INTEGER,INTENT(OUT) :: iostat
CHARACTER(*),INTENT(INOUT) :: iomsg
In each procedure, unit is either a normal unit number if the parent input/output statement used a normal unit number, a negative number if the parent input/output statement is for an internal file, or a processor-dependent number (which might be negative) for the ‘*’ unit. The iostat argument must be assigned a value before returning from the defined input/output procedure: either zero to indicate success, the negative number IOSTAT_EOR (from the intrinsic module ISO_FORTRAN_ENV) to signal an end-of-record condition, the negative number IOSTAT_END to signal an end-of-file condition, or a positive number to indicate an error condition. The iomsg argument must be left alone if no error occurred, and must be assigned an explanatory message if iostat is set to a nonzero value.
For the formatted input/output procedures, the iotype argument will be set to ‘LISTDIRECTED’ if list-directed formatting is being done, ‘NAMELIST’ if namelist formatting is being done, and ‘DT’ concatenated with the character-literal if the DT edit descriptor is being processed. The vlist argument contains the list of values in the DT edit descriptor if present, and is otherwise a zero-sized array. Note that the syntax of the DT edit descriptor is:
DT [ character-literal ] [ ( value [ , value ]... ) ]
where blanks are insignificant, character-literal is a default character literal constant with no kind parameter, and each value is an optionally signed integer literal constant with no kind parameter. For example, ‘DT’, ‘DT"z8,i4,e10.2"’, ‘DT(100,-3,+4,666)’ and ‘DT"silly example"(0)’ are all syntactically correct DT edit descriptors: it is up to the user-defined procedure to interpret what they might mean.During execution of a defined input/output procedure, there must be no input/output for an external unit (other than for the unit argument), but input/output for internal files is permitted. No file positioning commands are permitted. For unformatted input/output, all input/output occurs within the current record, no matter how many separate data transfer statements are executed by the procedure; that is, file positioning both before and after “nested” data transfer is suppressed. For formatted input/output, this effect is approximately equivalent to the nested data transfer statements being considered to be nonadvancing; explicit record termination (using the slash (/) edit descriptor, or transmission of newline characters to a stream file) is effective, and record termination may be performed by a nested list-directed or namelist input/output statement.
If unit is associated with an external file (i.e. non-negative, or equal to one of the constants ERROR_UNIT, INPUT_UNIT or OUTPUT_UNIT from the intrinsic module ISO_FORTRAN_ENV), the current settings for the pad mode, sign mode, etc., can be discovered by using INQUIRE with PAD=, SIGN=, etc. on the unit argument. If unit is negative (associated with an internal file), INQUIRE will raise the error condition IOSTAT_INQUIRE_INTERNAL_UNIT.
Finally, defined input/output is not compatible with asynchronous input/output; all input/output statements involved with defined input/output must be synchronous.