[construct-name:] SELECT CASE ( case-expr )
CASE ( case selector ) [construct identifier]
...
... < statements >
...
CASE ( case selector ) [construct-name]
...
CASE ( case selector ) [construct-name]
...
CASE DEFAULT
...
END SELECT [construct-name]
Where
SELECT CASE (Month) !!Find number of days in a Month
CASE (:0, 13:)
Days_in_month = 0
PRINT *, "Invalid month!!"
CASE (1, 3, 5, 7:8, 10, 12)
Days_in_month = 31
CASE (2) !!February
Days_in_month = 28
IF (MOD(Year,4) == 0) Days_in_month = 29 !!Leap year
CASE DEFAULT !!September, April, June & November
Days_in_month = 30 !! Thirty days hath ...^
END SELECT
A file is either "Formatted" or "Unformatted", and access to a file is either "Sequential" or "Direct". This gives rise to 4 types of files:
The following two OPEN statements are equivalent, since all of the
optional keyword arguments in the second are the defaults.
OPEN (5, FILE="MYPROG.F90", POSITION="REWIND")
OPEN (UNIT=5, FILE="MYPROG.F90", FORM="FORMATTED",
ACCESS="SEQUENTIAL", &
STATUS="UNKNOWN", POSITION="REWIND")
The arguments of OPEN are as follows:
WRITE (5,"(10I6)")
output-list
READ (6)
input-list Once a file has been written with a particular RECL, it must always be opened with the same RECL.
WRITE (UNIT=7,
FMT=78,
REC=MyRec,
ERR=20, END=30,
ADVANCE="YES") output-list
"(I3,A)"
In this example, we will write a file named A record is the data transfered by a single
READ or WRITE statement.
Lengths of records can vary. It is important that the type, kind, and shape
of variables in a READ statement match those of the WRITE statement that
produced the same record.
In this example, a series of records each store a person's name, and a list
of their numbered Swiss bank accounts and balances. Except that the first
record stores the total number of records. Note that it is necessary to
store the length of a variable-length record before the variable data, so
that it can be read in time to control the implied DO loop.
CLOSE statement
CLOSE(5) closes previously opened file 5. It is kept by default,
unless the file was opened as STATUS="SCRATCH", which cannot be kept.
Formatted sequential files
This is a text file, consisting of
characters. It is portable between computer systems and software.
Data is converted between characters in the file and binary
representation in memory, either by explicit format, or
Fortran's choice list-directed formatting, using * as the format
specifier. This works exactly the same way as reading from standard
input and writing to standard output. In fact, these special files
are always open, and specified by UNIT=*.
MY.TXT, which
will overwrite any previous version.
INTEGER :: Number, Count, Quantity
REAL :: Amount, Total
CHARACTER(20) :: Description
OPEN (5, FILE="MY.TXT", STATUS="REPLACE", POSITION="REWIND")
WRITE (5,*) "This is the file written by me" !!list directed
WRITE (5,*) !!blank line
WRITE (5,500) "Quantity","Description","Price","Sub-Total"
!!column headings
WRITE (5,500) "========","===========","=====","========="
!!underline them
......
!! Variables get some values, we accumulate a Total
......
WRITE (5, 501) Quantity, Description, Amount, Amount*Quantity
......
WRITE (UNIT=5, FMT=502) Total
CLOSE (5)
......
!! -------- Formats -------------
500 FORMAT (A10, A20, 2A12)
501 FORMAT (I10, A20, 2F12.2) !!dollar amounts with 2 places after .
502 FORMAT ("Total cost for this list of items is",T42, F12.2)
Unformatted sequential files
This is a binary file, data is stored exactly as in memory.
Thus, it is shorter than an equivalent formatted file, and access is faster.
However, such a file can only be read by a
Fortran program on the same (type of) system as it was written.
An important aspect of Unformatted files is that no accuracy is lost when
storing REAL data.
PROGRAM Top_Secret
!! Example of Unformatted Sequential file
IMPLICIT NONE
INTEGER :: N, Account(20), A
REAL :: Balance(20) !!Holdings expressed in Swiss Francs
CHARACTER(30) :: Name
OPEN (6, FILE="swiss.bin", STATUS="NEW", FORM="UNFORMATTED", &
POSITION="REWIND", ACTION="WRITE")
DO
CALL Enter_a_Person (Name, N, Account, Balance)
IF (Name ==" ") EXIT
WRITE (6) Name, N, (Account(A), Balance(A), A=1,N)
END DO
CLOSE (6, STATUS="KEEP")
!! ----------- now read it again -----
OPEN (6, FILE="swiss.bin", STATUS="OLD", FORM="UNFORMATTED", &
POSITION="REWIND", ACTION="READ")
PRINT *, "Holders of Swiss bank accounts, and their holdings"
DO
READ (6, END=30) Name, N, (Account(A), Balance(A), A=1,N)
!! - - - - - Note: All values must be read, even if we don't use Account
PRINT "(' ',A, F12.2)", Name, SUM (Balance(:N))
END DO
30 CLOSE (6)
STOP
END PROGRAM Top_secret
Formatted direct files
Such files have fixed-length lines of text as records. They can be printed,
and also individual lines can be accessed directly by line number
(REC=).
An explicit format specifier (not * ) must be present in
READ or WRITE statements.
In this example, a Medical lab that does blood tests registers patients with a patient number starting with 2. They want to be able to print the current patient list, and also access patient name, etc directly by number. Note that we are using the
We first set up a file with only the header record:
PROGRAM Start_Patient
IMPLICIT NONE
CHARACTER(8) :: Date
INTEGER :: Next_Patient = 2 !! Next number to assign
!! Patient numbers 2:NextPatient-1 exist
CALL DATE_AND_TIME (Date) !! 8 character system date
OPEN (UNIT=7, FILE="PATIENT.TXT", STATUS="NEW", ACCESS="DIRECT", &
FORM="FORMATTED", ACTION="WRITE", RECL=82)
WRITE (7, 77, REC=1) "Patient registrations, next patient is", &
Next_Patient, "Last updated on ", Date
77 FORMAT (A, T40, I6, A20, A8)
CLOSE(7)
STOP
END PROGRAM Start_Patient
Next we present a subroutine to access this file to add a new patient. Of course, it might also be a good idea to check that the health insurance number does not already occur in the file. We will skip around, reading record 1, writing a new record, presumably at the end, and finally writing an updated record 1. The intervening records do not have to be processed. Note that the same RECL=82 must be specified.
SUBROUTINE New_Patient (Name, Phone, RAMQ_ID, Dr_ID, Remark, Next_Patient)
IMPLICIT NONE
!! -- Fields of patient records
CHARACTER(25), INTENT(IN) :: Name !!Patient name
CHARACTER(12), INTENT(IN) :: Phone !!Patient's phone number
CHARACTER(4), INTENT(IN) :: RAMQ_ID (3) !!Health insurance no
!!LLLF YYMM DDSS This encodes date of birth and sex
INTEGER , INTENT(IN) :: Dr_ID !!Doctor code
CHARACTER(20), INTENT(IN) :: Remark !!other remarks
INTEGER, INTENT(OUT) :: Next_Patient !! Next number to assign
!! Patient numbers 2:NextPatient-1 exist
CHARACTER(8) :: Date, Check
77 FORMAT (A, T40, I6, A20, A8) !!for 1st line, <80 char.
78 FORMAT (A25, A12, TR2, 3A5, I4, A21) !!all other lines, 80 ch.
CALL DATE_AND_TIME (Date) !! 8 character system date
OPEN (UNIT=7, FILE="PATIENT.TXT", STATUS="OLD", ACCESS="DIRECT", &
FORM="FORMATTED", ACTION="READWRITE", RECL=82)
READ (7, 77, REC=1) Check, Next_Patient
WRITE (7, 78, REC=Next_Patient) Name, Phone, RAMQ_ID, Dr_ID, Remark
WRITE (7, 77, REC=1) "Patient registrations, next patient is", &
Next_Patient + 1, "Last updated on ", Date
CLOSE(7)
RETURN
END SUBROUTINE New_Patient
Finally, this function returns Patient Name, based on patient number. It could check to see that the number is valid, by also reading record 1. We have chosen to use the ERR= option to return an error name instead, should there be any problem with I/O. In this OPEN statement, the default FORM="FORMATTED" is assumed. The FORMAT statements used for READ or WRITE must appear in this subprogram unit as well as any others that read/write the file. They need not be identical, as long as they interpret the same columns of data consistantly. In this example, we only need to read Patient_Name, because it is the first data item interpreted by the Format.
FUNCTION Patient_Name (Patient_Number)
IMPLICIT NONE
CHARACTER(25) :: Patient_Name
INTEGER, INTENT(IN) :: Patient_Number
OPEN (7, FILE="PATIENT.TXT", ACCESS="DIRECT", STATUS="OLD", &
ACTION="READ", RECL=82)
READ (7, 78, REC=Patient_Number, ERR=33) Patient_name
78 FORMAT (A25, A12, TR2, 3A5, I4, A21) !!all other lines, 80 ch.
RETURN
!! ----- OR, in case of error in reading
33 Patient_name = "Patient number not found"
RETURN
END FUNCTION Patient_Name
When this function is used, it looks very much like an array reference.
READ (8,REC=42) Name, Age, Height, WeightWhen opening the file, record length must be specified with RECL =. This can be calculated from the number, kind, and length of data items read/written (by the longest statement). A direct file that is STATUS="OLD" must be opened with the same RECL as when it was created.
For example, the same medical lab of our formatted-direct example receives blood samples, which are numbered sequentially in the "Incomming Blood Book". We wish to determine this number, and then record the Patient_Number, the date, the amount of blood (in cc's, a real number), and up to 6 standard tests (5 tiny integers). Later, when the test results come in, we will consult this file and the patient file (and a Doctor file) to prepare a report for the Doctor.
First, here is the subroutine to record incoming blood. (The file needs to be created with Next_Blood in REC=1, similar to the previous example.) By adding up the space required for the WRITE statement, we find that 16 bytes are needed for each record (fewer for record 1). Actually it is desirable for record length to be a power of 2.
SUBROUTINE Incoming_Blood (Patient_Number, Volume, Tests, Blood_Number)
IMPLICIT NONE
INTEGER(kind=2), INTENT(IN) :: Patient_Number
REAL(kind=4), INTENT(IN) :: Volume !!cc's of blood rec'd
INTEGER(kind=1), INTENT(IN) :: Tests(6)
INTEGER, INTENT(OUT):: Blood_Number
!! --- local variables
INTEGER :: DateTime(8) !!see DATE_TIME intrinsic function
INTEGER(kind=2) :: Year
INTEGER(kind=1) :: Month, Day
!! --- get date from system
CALL DATE_TIME (values=DateTime)
Year = DateTime(1) !!example, 1996
Month= DateTime(2)
Day = DateTime(3)
!! --- open file and read next blood number
OPEN (8, FILE="blood", ACCESS="DIRECT", FORM="UNFORMATTED",&
STATUS="OLD", ACTION="READWRITE", RECL=16)
READ (8, REC=1) Blood_Number
WRITE (8, REC=Blood_Number) Patient_Number, Year, Month, Day, &
Volume, Tests
WRITE (8, REC=1) Blood_Number + 1, Year, Month, Day
RETURN
END SUBROUTINE Incoming_Blood
Now when test results come back, we start with blood number and look up patient
number, etc., which can be used for writing a report.
We can use the previously given Function Patient_Name to print the actual name.
SUBROUTINE Get_Blood END SUBROUTINE Get_Blood
Example, convert an integer to characters:
CHARACTER(8) :: CHARINT
INTEGER :: Example = -12345
WRITE (CHARINT, "(I8)") Example
WRITE *, "The number is" // ADJUSTL(CharInt)//"<---"
will write
The number is-12345 <---with 2 spaces after the 5 due to the action of ADJUSTL
Up to top of this document
Back to Lin Jensen's home page.
Up to Bishop's University
www.cs.ubishops.ca