[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_NameWhen 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_BloodNow 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