Previous | Contents | Index |
The only condition required for block I/O is the setting of the BIO bit in the File Access field of the FAB, using the normal declarations needed to define the symbols properly. If you wish to perform both block and record I/O on the file without closing it, you need to set the BRO bit as well. For more information on mixing block and record mode I/O, see the OpenVMS Record Management Services Reference Manual. Note that the only difference between BIOCREATE and BIOREAD is the use of SYS$CREATE and SYS$OPEN services, respectively.
! Procedure name: BIOCREATE ! USEROPEN routine to set the Block I/O bit and create the BLOCK I/O file. INTEGER FUNCTION BIOCREATE(FAB, RAB, LUN) INTEGER LUN ! Declare the necessary interface names INCLUDE '($FABDEF)' INCLUDE '($RABDEF)' INCLUDE '($SYSSRVNAM)' ! Declare the FAB and RAB blocks RECORD /FABDEF/ FAB, /RABDEF/ RAB ! Set the Block I/O bit in the FAC (GET and PUT bits set by RTL) FAB.FAB$B_FAC = FAB.FAB$B_FAC .OR. FAB$M_BIO ! Now do the Create and Connect BIOCREATE = SYS$CREATE(FAB) IF (.NOT. BIOCREATE) RETURN BIOCREATE = SYS$CONNECT(RAB) IF (.NOT. BIOCREATE) RETURN ! Nothing more to do at this point, just return RETURN END FUNCTION BIOCREATE ! Procedure name: BIOREAD ! USEROPEN routine to set the Block I/O bit and open the Block I/O demo ! file for reading INTEGER FUNCTION BIOREAD(FAB, RAB, LUN) INTEGER LUN ! Declare the necessary interface names INCLUDE '($FABDEF)' INCLUDE '($RABDEF)' INCLUDE '($SYSSRVNAM)' ! Declare the FAB and RAB blocks RECORD /FABDEF/ FAB, /RABDEF/ RAB ! Set the Block I/O bit in the FAC (GET and PUT bits set by RTL) FAB.FAB$B_FAC = FAB.FAB$B_FAC .OR. FAB$M_BIO ! Now do the Open and Connect BIOREAD = SYS$OPEN(FAB) IF (.NOT. BIOREAD) RETURN BIOREAD = SYS$CONNECT(RAB) IF (.NOT. BIOREAD) RETURN ! Nothing more to do at this point, just return RETURN END FUNCTION BIOREAD |
The following routine initializes the array A and performs the SYS$WRITE operations. Beyond the normal RTL initialization, only the RSZ and RBF fields in the RAB need to be initialized in order to perform the SYS$WRITE operations. The %LOC function is used to create the address value required in the RBF field.
One of the main reasons that block mode I/O is so efficient is that it avoids copy operations by using the data areas of the program directly for the output buffer. When writing to a disk device, the program must specify a value for RSZ that is a multiple of 512 or else the final block would be only partly filled.
! Procedure name: OUTPUT ! Function to output records in block I/O mode LOGICAL FUNCTION OUTPUT(RAB) ! Declare RMS names INCLUDE '($RABDEF)' INCLUDE '($SYSSRVNAM)' ! Declare the RAB RECORD /RABDEF/ RAB ! Declare the Array to output REAL(KIND=8) A(6400) ! Declare the status variable INTEGER(KIND=4) STATUS ! Initialize the array DO I=6400,1,-1 A(I) = I ENDDO ! Now, output the array, two 512-byte (64 elements) blocks at a time OUTPUT = .FALSE. RAB.RAB$W_RSZ = 1024 DO I=0,99,2 ! For each block, set the buffer address to the proper array element RAB.RAB$L_RBF = %LOC(A(I*64+1)) STATUS = SYS$WRITE(RAB) IF (.NOT. STATUS) RETURN ENDDO ! Successful output completion OUTPUT = .TRUE. RETURN END FUNCTION OUTPUT |
The following routine reads the array A from the file and verifies its values. The USZ and UBF fields of the RAB are the only fields that need to be initialized. The I/O transfer size is twice as large as the OUTPUT routine. This can be done because the OUTPUT routine writes an integral number of 512-byte blocks to a disk device. This method cannot be used if the writing routine either specifies an RSZ that is not a multiple of 512 or attempts to write to a magnetic tape device.
! Procedure name: INPUT ! ! Function to input records in block I/O mode LOGICAL FUNCTION INPUT(RAB) ! Declare RMS names INCLUDE '($RABDEF)' INCLUDE '($SYSSRVNAM)' ! Declare the RAB RECORD /RABDEF/ RAB ! Declare the Array to output REAL(KIND=8) A(6400) ! Declare the status variable INTEGER(KIND=4) STATUS ! Now, read the array, four 512-byte (64 elements) blocks at a time INPUT = .FALSE. RAB.RAB$W_USZ = 2048 DO I=0,99,4 ! For each block, set the buffer address to the proper array element RAB.RAB$L_UBF = %LOC(A(I*64+1)) STATUS = SYS$READ(RAB) IF (.NOT. STATUS) RETURN ENDDO ! Successful input completion if data is correct DO I=6400,1,-1 IF (A(I) .NE. I) RETURN ENDDO INPUT = .TRUE. RETURN END FUNCTION INPUT |
This chapter describes:
Sequential and direct access have traditionally been the only file access modes available to Fortran programs. To overcome some of the limitations of these access modes, HP Fortran supports a third access mode, called keyed access, which allows you to retrieve records, at random or in sequence, based on key fields that are established when you create a file with indexed organization. (See Section 6.9.2 for details about keyed access mode.)
You can access files with indexed organization using sequential access or keyed access, or a combination of both.
Once you have read a record by means of an indexed read request, you can then use a sequential read request to retrieve records with ascending key field values, beginning with the key field value in the record retrieved by the initial read request.
Indexed organization is especially suitable for maintaining complex
files in which you want to select records based on one of several
criteria. For example, a mail-order firm could use an indexed
organization file to store its customer list. Key fields could be a
unique customer order number, the customer's zip code, and the item
ordered. Reading sequentially based on the zip-code key field would
enable you to produce a mailing list sorted by zip code. A similar
operation based on customer-order-number key field or item-number key
field would enable you to list the records in sequences of customer
order numbers or item numbers.
12.2 Creating an Indexed File
You can create a file with an indexed organization by using either of these methods:
Any indexed file created with EDIT/FDL can be accessed by HP Fortran I/O statements.
When you create an indexed file, you define certain fields within each record as key fields. The primary key, identified as key number zero, must be present as a field in every record. Alternate keys are numbered from 1 through 254. An indexed file can have as many as 255 key fields (1 primary key and up to 254 alternate keys) defined. In practice, however, few applications require more than 3 or 4 key fields.
The data types used for key fields must be INTEGER (KIND=1), INTEGER (KIND=2), INTEGER (KIND=4), INTEGER (KIND=8), or CHARACTER.
In designing an indexed file, you must decide the byte positions of the key fields. For example, in creating an indexed file for use by a mail-order firm, you might define a file record to consist of the following fields:
STRUCTURE /FILE_REC_STRUCT/ INTEGER(KIND=4) ORDER_NUMBER ! Positions 1:4, key 0 CHARACTER(LEN=20) NAME ! Positions 5:24 CHARACTER(LEN=20) ADDRESS ! Positions 25:44 CHARACTER(LEN=19) CITY ! Positions 45:63 CHARACTER(LEN=2) STATE ! Positions 64:65 CHARACTER(LEN=9) ZIP_CODE ! Positions 66:74, key 1 INTEGER(KIND=2) ITEM_NUMBER ! Positions 75:76, key 2 END STRUCTURE . . . RECORD /FILE_REC_STRUCT/ FILE_REC |
Instead of using a record structure, you can define a the fields of a record using a derived-type definition with the SEQUENCE statement:
TYPE FILE_REC SEQUENCE INTEGER(KIND=4) ORDER_NUMBER ! Positions 1:4, key 0 CHARACTER(LEN=20) NAME ! Positions 5:24 CHARACTER(LEN=20) ADDRESS ! Positions 25:44 CHARACTER(LEN=19) CITY ! Positions 45:63 CHARACTER(LEN=2) STATE ! Positions 64:65 CHARACTER(LEN=9) ZIP_CODE ! Positions 66:74, key 1 INTEGER(KIND=2) ITEM_NUMBER ! Positions 75:76, key 2 END TYPE FILE_REC . . . |
Given this record definition, you can use the following OPEN statement to create an indexed file:
OPEN (UNIT=10, FILE='CUSTOMERS.DAT', STATUS='NEW', & ORGANIZATION='INDEXED', ACCESS='KEYED', RECORDTYPE='VARIABLE', & FORM='UNFORMATTED', RECL=19, & KEY=(1:4:INTEGER, 66:74:CHARACTER, 75:76:INTEGER), & IOSTAT=IOS, ERR=9999) |
This OPEN statement establishes the attributes of the file, including the definition of a primary key and two alternate keys. The definitions of the integer keys do not explicitly state INTEGER (KIND=4) and INTEGER (KIND=2). The data type sizes are determined by the number of character positions allotted to the key fields (4- and 2-digit positions in this case respectively).
If you specify the KEY keyword when opening an existing file, the key specification that you give must match that of the file.
HP Fortran uses RMS default key attributes when creating an indexed file. These defaults are as follows:
You can use the EDIT/FDL Utility or a USEROPEN routine to override these defaults and to specify other values not supported by HP Fortran, such as null key field values, null key names, and key data types other than integer and character.
You can write records to an indexed file with either formatted or unformatted indexed WRITE statements. Each write operation inserts a new record into the file and updates the key indexes so that the new record can be retrieved in a sequential order based on the values in the respective key fields.
For example, you could add a new record to the file for the mail-order firm (see Section 12.2) with the following statement:
WRITE (UNIT=10,IOSTAT=IOS,ERR=9999) FILE_REC |
It is possible to write two or more records with the same value in a single key field. The attributes specified for the file when it was created determine whether this duplication is allowed. By default, HP Fortran creates files that allow duplicate alternate key field values and prohibit duplicate primary key field values. If duplicate key field values are present in a file, the records with equal values are retrieved on a first-in/first-out basis.
For example, assume that five records are written to an indexed file in this order (for clarity, only key fields are shown):
ORDER_NUMBER | ZIP_CODE | ITEM_NUMBER |
---|---|---|
1023 | 70856 | 375 |
942 | 02163 | 2736 |
903 | 14853 | 375 |
1348 | 44901 | 1047 |
1263 | 33032 | 690 |
If the file is later opened and read sequentially by primary key (ORDER_NUMBER), the order in which the records are retrieved is not affected by the duplicated value (375) in the ITEM_NUMBER key field. In this case, the records would be retrieved in the following order:
ORDER_NUMBER | ZIP_CODE | ITEM_NUMBER |
---|---|---|
903 | 14853 | 375 |
942 | 02163 | 2736 |
1023 | 70856 | 375 |
1263 | 33032 | 690 |
1348 | 44901 | 1047 |
However, if the read operation is based on the second alternate key (ITEM_NUMBER), the order in which the records are retrieved is affected by the duplicate key field value. In this case, the records would be retrieved in the following order:
ORDER_NUMBER | ZIP_CODE | ITEM_NUMBER |
---|---|---|
1023 | 70856 | 375 |
903 | 14853 | 375 |
1263 | 33032 | 690 |
1348 | 44901 | 1047 |
942 | 02163 | 2736 |
The records containing the same key field value (375) are retrieved in
the order in which they were written to the file.
12.3.2 Preventing the Indexing of Alternate Key Fields
When writing to an indexed file that contains variable-length records, you can prevent entries from being added to the key indexes for any alternate key fields. This is done by omitting the names of the alternate key fields from the WRITE statement. The omitted alternate key fields must be at the end of the record; another key field cannot be specified after the omitted key field.
For example, the last record (ORDER_NUMBER 1263) in the mail-order example could be written with the following statement:
WRITE (UNIT=10,IOSTAT=IOS,ERR=9999) FILE_REC.ORDER_NUMBER, FILE_REC.NAME, & FILE_REC.ADDRESS, FILE_REC.CITY, FILE_REC.STATE, FILE_REC.ZIP_CODE |
Because the field name FILE_REC.ITEM_NUMBER is omitted from the WRITE statement, an entry for that key field is not created in the index. As a result, an attempt to read the file using the alternate key ITEM_NUMBER would not retrieve the last record and would produce the following listing:
ORDER_NUMBER | ZIP_CODE | ITEM_NUMBER |
---|---|---|
1023 | 70856 | 375 |
903 | 14853 | 375 |
1348 | 44901 | 1047 |
942 | 02163 | 2736 |
You can omit only trailing alternate keys from a record; the primary
key must always be present.
12.4 Reading Records from an Indexed File
You can read records in an indexed file with either sequential or indexed READ statements (formatted or unformatted) under the keyed mode of access. By specifying ACCESS='KEYED' in the OPEN statement, you enable both sequential and keyed access to the indexed file.
Indexed READ statements position the file pointers (see Section 12.7) at a particular record, determined by the key field value, the key-of-reference, and the match criterion. Once you retrieve a particular record by an indexed READ statement, you can then use sequential access READ statements to retrieve records with increasing key field values.
The form of the external record's key field must match the form of the value you specify in the KEY keyword. If the key field contains character data, you should specify the KEY keyword value as a CHARACTER data type. If the key field contains binary data, then the KEY keyword value should be of INTEGER data type.
If you write a record to an indexed file with formatted I/O, the data type is converted from its internal representation to an external representation. As a result, the key value must be specified in the external form when you read the data back with an indexed read. Otherwise, a match will occur when you do not expect it.
The following HP Fortran program segment prints the order number and zip code of each record where the first five characters of the zip code are greater than or equal to '10000' but less than '50000':
! Read first record with ZIP_CODE key greater than or equal to '10000'. READ (UNIT=10,KEYGE='10000',KEYID=1,IOSTAT=IOS,ERR=9999) FILE_REC ! While the zip code previously read is within range, print the ! order number and zip code, then read the next record. DO WHILE (FILE_REC.ZIP_CODE .LT. '50000') PRINT *, 'Order number', FILE_REC.ORDER_NUMBER, 'has zip code', & FILE_REC.ZIP_CODE READ (UNIT=10,IOSTAT=IOS,END=200,ERR=9999) FILE_REC ! END= branch will be taken if there are no more records in the file. END DO 200 CONTINUE |
The error branch on the keyed READ in this example is taken if no record is found with a zip code greater than or equal to '10000'; an attempt to access a nonexistent record is an error. If the sequential READ has accessed all records in the file, an end-of-file status occurs, as with other file organizations.
If you want to detect a failure of the keyed READ, you can examine the
I/O status variable, IOS, for the appropriate error number (see
Table 7-1 for a list of the returned error codes).
12.5 Updating Records in an Indexed File
The REWRITE statement updates existing records in an indexed file. You cannot replace an existing record simply by writing it again; a WRITE statement would attempt to add a new record.
An update operation is accomplished in two steps:
For example, to update the record containing ORDER_NUMBER 903 (see prior examples) so that the NAME field becomes 'Theodore Zinck', you might use the following Fortran code segment:
READ (UNIT=10,KEY=903,KEYID=0,IOSTAT=IOS,ERR=9999) FILE_REC FILE_REC.NAME = 'Theodore Zinck' REWRITE (UNIT=10,IOSTAT=IOS,ERR=9999) FILE_REC |
When you rewrite a record, key fields may change. The attributes specified for the file when it was created determine whether this type of change is permitted. The primary key value can never change on a REWRITE operation. If necessary, delete the old record and write a new record.
Previous | Next | Contents | Index |