REXX provides several ways for you to read records of a file directly (that is, in random order). The following example, DIRECT.CMD, shows seven different cases that illustrate some of your options.
DIRECT opens a file for both reading and writing, which is indicated by the BOTH argument on the OPEN method. The REPLACE argument on the OPEN method causes any existing DIRECT.DAT file to be replaced.
The OPEN method also has a couple of arguments we haven't yet seen: BINARY and RECLENGTH. Both of these arguments are useful for direct file access.
The BINARY argument opens the stream in binary mode, which means that line-end characters are ignored. Binary mode is useful when you want to process binary data using line methods. From studying the example you'll see that it is easier to use line methods for direct access. With line methods, you can seek to a position in a file using line numbers. With character methods, you must calculate a character displacement into the file.
The RECLENGTH argument defines a record length of 50 for the file. The RECLENGTH argument makes it possible for you to use line methods on a binary-mode stream. Since REXX now knows how long each record is, it can calculate a displacement into a file for a given record number and read the record directly.
/* DIRECT -- demonstration of direct file access */ db=.stream~new('direct.dat') db~open('both replace binary reclength 50') /* Write three records of 50 bytes each using LINEOUT */ db~lineout('Cole, Gary: Blue') db~lineout('McGuire, Rick: Red') db~lineout('Pritko, Steve: Red. Oops.. I mean blue!') /* Case 1: Read the records in order using LINEIN. */ say 'Case 1: Sequential reads with LINEIN...' do i=1 to 3 say db~linein end say 'Press Enter to continue'; parse pull resp /* Case 2: Read records in random order using LINEIN */ say 'Case 2: Random reads with LINEIN...' do i=1 to 5 lineno=random(1,3) say 'Record' lineno '=' db~linein(lineno) end say 'Press Enter to continue'; parse pull resp /* Case 3: Read entire file with CHARIN */ say 'Case 3: Read entire file with a single CHARIN...' say db~charin(1,150) say 'Press Enter to continue'; parse pull resp /* Case 4: Read file sequentially with CHARIN */ say 'Case 4: Sequential reads with CHARIN...' db~seek(1 read) /* Reposition read pointer */ do i=1 to 3 say db~charin(,50) end say 'Press Enter to continue'; parse pull resp /* Case 5: Read records in random order with CHARIN */ say 'Case 5: Random reads with CHARIN...' do i=1 to 5 lineno=random(1,3) charno=((lineno-1)*50)+1 say 'Record' lineno 'Character' charno '=' db~charin(charno,50) end say 'Press Enter to continue'; parse pull resp /* Case 6: Write records in random order with LINEOUT */ say 'Case 6: Replace record 2 with LINEOUT' db~lineout('This should replace line 2',2) do i=1 to 3 say db~linein(i) end say 'Press Enter to continue'; parse pull resp /* Case 7: Write records in random order with CHAROUT */ say 'Case 7: Replace record 2 with CHARIN...' db~charout('New record 2 from CHAROUT'~left(50,'.'),51) db~seek(1 read) /* Reposition read pointer */ do i=1 to 3 say db~charin(,50) end say 'Press Enter to continue'; parse pull resp db~close
After opening the file, DIRECT writes three records using LINEOUT. Notice that we didn't pad the records to 50 characters. REXX handles that for us. Because the file is opened in binary mode, REXX does not write line-end characters at the end of each line. It just writes the strings one after another to the stream.
In Case 1, the LINEIN method is used to read the file. Because the file is open in binary mode, LINEIN doesn't look for line-end characters to mark the end of a line. Instead, it relies on the record length that you specify on open. In fact, if there happened to be a carriage-return/line-feed sequence of the line, REXX would return those characters to your program.
Case 2 demonstrates how to read the file in random order. In this case, we use the RANDOM function to choose a record to retrieve. Then we specify the desired record number as an argument on LINEIN. Note that records are numbered starting from 1, not from 0. Because the file is opened in binary mode, REXX doesn't look for line-end characters. It uses the RECLENGTH to determine where to read. When used in this way, the LINEIN method can retrieve a line directly, without having to scan through the file counting line-end characters.
Case 3 proves that no line-end characters exist in the file. The CHARIN method reads the entire file. SAY displays the returned string as one long string. If REXX inserted line-end characters, each record would be displayed on a separate line.
Case 4 shows how to read the binary mode file sequentially using CHARIN. But before reading the file, we need to reset the read pointer to the beginning of the file. (Case 3 leaves the read pointer at the end of the file.) We used a SEEK method to reset the read pointer to character 1, which is the beginning of the file. As with lines, REXX numbers characters starting with 1, not 0. Position 1 is the first character of the file.
By default, the number specified on SEEK refers to a character positioning. You can also seek by line number or by offsets. SEEK allows offsets from the current read or write position, or from the beginning or ending of the file. If you prefer typing longer method names, you can use POSITION as a synonym for SEEK.
In the loop in Case 4, the first argument on CHARIN is omitted. The first argument tells where to position the read pointer. If it is omitted, REXX automatically advances the read pointer based on the number of characters you are reading.
Case 5 demonstrates how to read records in random order with CHARIN. In the loop, a random record number is selected and assigned to variable lineno. This record number is then converted to a character number, which can be used to specify the read position on CHARIN. Compare Case 5 with Case 2. In Case 2, which uses line methods, it isn't necessary to perform a calculation, you just request the record you want.
Cases 6 and 7 write records in random order. Case 6 uses LINEOUT, while Case 7 uses CHAROUT. Because the file is opened in binary mode, LINEOUT doesn't write line-end characters. You can write over a line by specifying a line number. With CHAROUT, you need to calculate the character position of the line to be replaced. Also unlike LINEOUT, you need to ensure that the string being written with CHAROUT is padded to the appropriate record length. Otherwise, part of the record being replaced will remain in the file.
Consequently, for random reading of files with fixed length records, line methods are often the better choice. However, one limitation of the line methods is that you cannot use them to write sparse records. That is, if a file already has 200 records, you can use LINEOUT to write record 201, but you cannot use LINEOUT to write record 300. With CHAROUT, you can open a new file and start writing at character position 5000 if you choose.