The implication is that, if you are creating data files in your own, proprietary file format, you should devise some sort of text format. For example, let's say that you need to save 4 numbers in some data file. Let's say those numbers are 10, -45, 6.5, and 1. One approach you can take is to write out each number with a call to LINEOUT, so that each number appears upon its own line in the text file. Then, when you later read this data file, you'll use a call to LINEIN to retrieve each number. (Note that error checking has been omitted in the examples for simplicity).
/* Write the first number to a file named "mytext.txt". Note * that we specify a position of 1, so if the file already exists * and has some text in it, that original text is erased. */ err = LINEOUT("mytext.txt", 10, 1) /* Write the second number to the same text file. Note that * we omit the position, so this line gets written immediately * after the line we just wrote above. */ err = LINEOUT("mytext.txt", -45) /* Write the third number. */ err = LINEOUT("mytext.txt", 6.5) /* Write the fourth (last) number. Get the value from a variable, * just to demonstrate using a variable with LINEOUT. */ last = 1 err = LINEOUT("mytext.txt", last) /* Close the file. */ err = LINEOUT("mytext.txt")If you viewed mytext.txt in a text editor, you would see the 4 numbers, each on its own line, as so:
10 -45 6.5 1You can read those numbers back into variables as so:
/* Read the first line from "mytext.txt". */ num1 = LINEIN("mytext.txt", 1) /* Read the next line from the same text file. */ num2 = LINEIN("mytext.txt") /* Read the next line from the same text file. */ num3 = LINEIN("mytext.txt") /* Read the next line from the same text file. */ num4 = LINEIN("mytext.txt") /* Close the file. */ err = LINEOUT("mytext.txt")As another example, let's say you need to save record-based data. That is to say that you need to save a bunch of items, where there are several pieces of data for each item. Let's take a phone book as an example. You need to save the names, phone numbers , and ages of several people. In this case, you can put all the information about each person upon one line. And you can pick a "special character" to separate each piece of information, for example, a comma. Then when you later read the file, you can use LINEIN to load each person's information, and PARSE VAR to separate the pieces of information.
/* Write the first person's name, phone number, and * age to a file named "mytext.txt". */ err = LINEOUT("mytext.txt", "Paul Nobody, 555-2167, 22", 1) /* Write the second person's information. */ name = 'John Peabody' phone = '555-6660' age = 17 err = LINEOUT("mytext.txt", name || ', ' || phone || ', ' || age) /* Close the file. */ err = LINEOUT("mytext.txt")If you viewed mytext.txt in a text editor, you would see the 2 records, each on its own line, as so:
Paul Nobody, 555-2167, 22 John Peabody, 555-6660, 17You can read the information back into variables as so:
/* Initially, no people in the phone book. */ people = 0 /* See if there is another line. */ DO WHILE LINES("mytext.txt") > 0 /* Read the next line. */ info = LINEIN("mytext.txt", , 1) /* Count another person. */ people = people + 1 /* Parse the information into separate variables. */ PARSE VAR info name.people ', ' phone.people ', ' age.people END /* Close the file. */ err = LINEOUT("mytext.txt") /* Now, 'people' is a count of how many people are in the phone book. * The first person's name, phone, and age is in name.1, phone.1, and * age.1. The second person's name, phone, and age is in name.2, * phone.2, and age.2. Etc. */
Reading a binary file
Sometimes, you'll need to deal with file formats that were created by others. These formats may not contain purely text. They may contain binary data. Now, it's true that you can use CHARIN to read these binary bytes. But in order to manipulate the data with REXX functions and instructions, you may need to convert the data to a string representation. In particular, if the file contains numeric values in binary format, and you want to actually use them as numeric strings, you'll need to use the C2D Function to convert each binary byte to a decimal string when you read from the file. You'll use the D2C Function to convert a numeric value to a series of binary bytes when write a binary number to the file.
For example, assume that you have a file with 3 binary bytes in it. If you look at the file in a binary file editor, let's assume you see 3 bytes as so (in hexadecimal):
10 03 E1
If you wish to read these bytes as 3 numeric strings, you'll need to convert each one with C2D() as so:
/* Read the first byte from "test.bin". */ byte = CHARIN("test.bin", , 1) /* Convert the binary byte to a numeric (decimal) string. */ number = C2D(byte) /* Display the numeric string. */ SAY number /* Read the next byte. */ byte = CHARIN("test.bin", , 1) /* Convert the binary byte to a numeric string. */ number = C2D(byte) /* Display the numeric string. */ SAY number /* Read the next byte. */ byte = CHARIN("test.bin", , 1) /* Convert the binary byte to a numeric string. */ number = C2D(byte) /* Display the numeric string. */ SAY number /* Close the file. */ err = LINEOUT("mytext.txt")You should see displayed the three numbers of 16 (10 hex), 3, and 225 (E1 hex).
To write out the 3 numeric values of 16, 3, and 225 to a binary file, you'd use D2C to convert to binary, and then use CHAROUT to write to the file.
/* Write the number 10 as first binary byte to "test.bin". */ err = CHAROUT("test.bin", D2C(10), 1) /* Write the number 3 as the next binary byte. */ num = 3 err = CHAROUT("test.bin", D2C(num)) /* Write the number 225 as the next binary byte. */ err = CHAROUT("test.bin", D2C(225)) /* Close the file. */ err = LINEOUT("mytext.txt")This is not too bad so far. The real hassle comes when you need to read in words or doublewords. A word is 2 binary bytes combined into one number. A doubleword is 4 binary bytes combined into one number. So not only do you need to convert each byte; you also need to perform some math to combine 2 or 4 bytes. And if that isn't bad enough, you have to know whether the bytes are stored in big endian (sometimes called "Motorola") order, or little endian (sometimes called "Intel") order. Depending upon which order is used, you'll combine the bytes differently.
For example, let's say that our binary file contains the doubleword (4 bytes) of 03 16 02 00 in little endian order. That would be the numeric value of decimal 136707. Here's how you would read and convert it to a numeric string:
/* Read the first byte from "test.bin". */ byte = CHARIN("test.bin", , 1) /* Convert the binary byte to a numeric (decimal) string. */ number = C2D(byte) /* We need to create one sum that is the combination * of all 4 bytes. Let's start the sum now. */ sum = number /* Read the next byte. */ byte = CHARIN("test.bin", , 1) /* Convert the binary byte to a numeric string. */ number = C2D(byte) /* Add to our sum. We multiply the second byte by * 256 before adding to the sum. */ sum = sum + (number * 256) /* Read the next byte. */ byte = CHARIN("test.bin", , 1) /* Convert the binary byte to a numeric string. */ number = C2D(byte) /* Add to our sum. We multiply the third byte by * 65536 before adding to the sum. */ sum = sum + (number * 65536) /* Read the next byte. */ byte = CHARIN("test.bin", , 1) /* Convert the binary byte to a numeric string. */ number = C2D(byte) /* Add to our sum. We multiply the fourth byte by * 16777216 before adding to the sum. */ sum = sum + (number * 16777216) /* Display the final numeric string. */ SAY sum /* Close the file. */ err = LINEOUT("mytext.txt")Because it can be a hassle to read/write binary files (especially with word or doubleword values), Reginald offers two functions that do all of the conversions as well as read/write to the file. VALUEIN can read a binary byte, word, or doubleword value from a file, in either big or little endian order, and convert it to a REXX numeric string. So here is how we would do the same as the above script to read that doubleword:
/* Read a binary doubleword in little endian order from "test.bin". */ sum = VALUEIN("test.bin", , 4) /* Check for an error. */ IF sum \== "" THEN /* Display the numeric string. */ SAY sum /* Close the file. */ err = LINEOUT("mytext.txt")That's a bit less hassle, isn't it? But this is a Reginald-only feature.
So too, VALUEOUT can write a numeric string as a binary byte, word, or doubleword value to a file, in either big or little endian order.
/* Write the number 136707 as a binary doubleword in little * endian order to "test.bin". */ err = VALUEOUT("test.bin", 136707, , 4) /* Check for an error. */ IF err \== 0 THEN SAY 'Error' /* Close the file. */ err = LINEOUT("mytext.txt")So with VALUEIN, you never need to use C2D, and with VALUEOUT, you never need to use D2C. And you can intermix calls to VALUEIN, CHARIN, and LINEOUT, as well as calls to VALUEOUT, CHAROUT, and LINEOUT.
Note: VALUEIN updates the read character position, and VALUEOUT updates the write character position).