Reading lines
LINEIN is used with text (ie, ascii) files, and it reads the next, entire line of text from a file. It is assumed that each line ends with a newline character, or a combination of a newline and linefeed characters. These last characters are stripped from the end of the line when it is read in.
The first argument you pass to LINEIN is the name of the file. You can include the full path to the file, for example C:\MyDir\MyFileName.txt.
The second (ie, Position) argument is the line number you wish to read. If you pass a 1, then the first line in the file is read.
If you omit the line number argument, then LINEIN will read the next line after any other line you already read with a previous call to LINEIN. In other words, the line you read will be the next line in the file. If you have not yet done any LINEIN (nor CHARIN) to this file, then the assumed position will be at the first line of the file.
The third argument is the actual number of lines you wish to read. This can be only 1 or 0. In other words, a call to LINEIN can read in only one line at most. 0 is a special case that will be discussed later. If you omit this arg, then it is assumed to be 1.
If LINEIN successfully reads the line from the file, then it will return all the characters in that line (without any trailing newline or linefeed characters) as one string. If there is a problem, LINEIN will return an empty string, raise the NOTREADY condition, and put the file in an "error state" such that further calls to LINEIN will return an empty string until you clear that error state. Of course, if the line happens to be a blank line, then an empty string will be returned as well. For this reason, the only way that you can distinguish between a real error, and whether the line you read happens to be a blank line, is to either use STREAM Function's "D" option to retrieve any error message, or trap the NOTREADY condition (discussed later).
You may wonder "What happens if I tell LINEIN to read a line from a file that hasn't yet been created?". In that case, LINEIN will return an empty string. Again, you will have to check for an error as above.
Here is an example where we read the first 3 lines from a text file named mytext.txt and print those lines to the screen.
/* Read the first line from a text file named "mytext.txt". */ line = LINEIN("mytext.txt") /* Check for an error. */ IF STREAM("mytext.txt", "D") \== "" THEN DO Bad: SAY "ERROR:" STREAM("mytext.txt", "D") RETURN END /* Display the line. */ SAY line /* Read the next line from the same text file. */ line = LINEIN("mytext.txt") /* Check for an error. */ IF STREAM("mytext.txt", "D") \== "" THEN SIGNAL Bad /* Display the line. */ SAY line /* Read the next line from the same text file. */ line = LINEIN("mytext.txt") /* Check for an error. */ IF STREAM("mytext.txt", "D") \== "" THEN SIGNAL Bad /* Display the line. */ SAY lineNow rerun the very first script to recreate the 3 line version of "mytext.txt", then run the above script. You should see the 3 lines displayed in the console window.
The line number arg allows you to determine which line is read. For example, let's say that we wish to read the second line in the file. In this case, we specify a line number of 2, as so:
/* Read and display the second line of a text file named * "mytext.txt". */ SAY LINEIN("mytext.txt", 2)Now when you run the above script, it should display only the second line of "mytext.txt".
Reading individual characters
Alternately, the CHARIN Function can read characters (ie, individual bytes) from a file. You specify how many characters to read, and CHARIN reads in that number of characters that appear next in the file. Unlike LINEIN, CHARIN does not trim off any newline or linefeed characters. So, you're reading the "raw data".
The first argument you pass to CHARIN is the name of the file.
The second (ie, Position) argument is the character number you wish to start reading at. If you pass a 1, then CHARIN starts reading from the first character in the file.
If you omit the position arg, then CHARIN will read the next character(s) after any other characters you already read with a previous call to CHARIN (or LINEIN). In other words, the character(s) you read will be the next ones in the file. If you have not yet done any CHARIN (nor LINEIN) to this file, then the assumed position will be at the first character in the file.
The third argument is the actual number of characters you wish to read. If you omit this arg, then it is assumed to be 1 character.
If CHARIN successfully reads the character(s) from the file, then it will return the character(s) you requested as one string. If there is a problem, CHARIN may return a string containing less characters than you requested, and maybe even an empty string. For an error, CHARIN will also raise the NOTREADY condition, and put the file in an "error state" such that further calls to CHARIN (and LINEIN) will return an empty string until you clear that error state. What sort of situation could cause CHARIN to read in less characters than you requested? Well, if there aren't as many characters in the file as you requested, CHARIN will return however many characters there are (but not raise NOTREADY). If you specify a position that is beyond the last character in file, there will be no characters to read, and so CHARIN will return an empty string (and raise NOTREADY). If the file doesn't exist, CHARIN will also return an empty string (and raise NOTREADY). If you've already read all of the characters (ie, you're at the end of the file), CHARIN will return an empty string (and raise NOTREADY). For this reason, you may wish to either use STREAM Function's "D" option to retrieve any error message, or trap the NOTREADY condition (discussed later).
/* Read the first 3 characters from a text file named * "mytext.txt", and display them. */ data = CHARIN("mytext.txt", ,3) /* Check for an error. */ IF STREAM("mytext.txt", "D") \== "" THEN DO Bad: SAY "ERROR:" STREAM("mytext.txt", "D") RETURN END /* Display the characters. */ SAY data /* Read the next 4 characters from the same text file. */ data = CHARIN("mytext.txt", ,4) IF STREAM("mytext.txt", "D") \== "" THEN SIGNAL Bad SAY dataNote: After you have read all of the characters in the file and there are no more to read, if you call CHARIN or LINEIN again, then REXX will raise the NOTREADY condition.
Intermixing LINEIN/CHARIN
If you have a file that contains some raw bytes, as well as some line of text that ends with a newline and/or linefeed, it is even acceptable to use CHARIN to read the raw bytes, and then use LINEIN to read the portion of the file that is the line of text. In other words, you can intermix calls to CHARIN and LINEIN on the same file (although LINEIN makes sense only when dealing with files that have lines of text in them). In this case, LINEIN starts reading a line where CHARIN stopped reading bytes (and vice versa).
Determine how many characters can be read
Sometimes, it is desirable to know how many more characters (ie, bytes) are available to read in an existing file. For example, if you plan to use CHARIN to read them, it may be desireable to know exactly how many characters remain to be read. In this way, you can request no more than that number of characters, and therefore if CHARIN returns less characters than you request, it will be because of some error other than you've reached the end of the file. (ie, It will be easier for you to distinguish a genuine error while reading the file). The CHARS Function tells you how many more characters are available to read. This will be 0 if you've reached the end of the file (or the file contained no data at all).
Note: If you have not yet made any calls to LINEIN nor CHARIN on a given file, then CHARS tells you the total size of the file. STREAM's QUERY SIZE command will also return a file's total size, even if you have already made calls to LINEIN or CHARIN.
Here then is a script that determines how many characters are in a file, and then reads them all with a single call to CHARIN and displays them:
/* Determine how many characters in a text file named * "mytext.txt". */ total = CHARS("mytext.txt") /* Anything to read? */ IF total > 0 THEN DO /* Read those characters. */ data = CHARIN("mytext.txt", , total) /* Check for an error. */ IF STREAM("mytext.txt", "D") \== "" THEN DO Bad: SAY "ERROR:" STREAM("mytext.txt", "D") RETURN END /* Display the characters. */ SAY data END
Determine how many lines can be read
Sometimes, it is desirable to know how many more lines of text are available to read in an existing file. If you plan to use LINEIN to read them, it helps to know exactly how many lines remain to be read, so that you can more easily determine when you've read the last line of the file. The LINES Function tells you if there are any more lines to be read. If there are no more lines to read (ie, you're at the end of the file), then LINES will return 0. Upon some interpreters, if there are more lines to be read, LINES will even tell you exactly how many more lines there are. (ie, LINES will return a count of how many more lines can be read). Upon other interpreters, if there more lines, LINES will simply return a 1 regardless of how many more lines there really are. And some interpreters, such as Reginald, even give you a choice as to whether LINES returns the actual count of lines, or just a 1. (It is usually faster to simply return a 1, so that is referred to as the "fast option", and is useful if you plan to continuously call LINES inside a loop).
The first arg is the name of the file.
If the interpreter supports both the "fast option" as well as an actual line count, then LINES will accept a second arg. This should be 'C' if you want an actual count of lines, or 'N' if you prefer the fast option.
Note: If you're supporting interpreters that do not offer both options, then you should assume that LINES will only return a 1 for more lines. (ie, LINES will never tell you the actual number of lines still waiting to be read).
Here then is a script that loops around all of the lines in a file, reading and displaying each line. It assumes that LINES will never return an actual count of lines, so we need to continuously call LINES inside of the loop, to determine when we finally hit the end of the file. Only when we've already read the last line will LINES return 0.
/* See if there is another line. */ DO WHILE LINES("mytext.txt") > 0 /* Display the next line */ SAY LINEIN("mytext.txt", , 1) ENDHere is another version that uses the LINES option that returns the actual count of lines. This makes the loop faster because we do not need to continuously call LINES inside the loop. But note that if an interpreter does not support the extra option arg for LINES, a SYNTAX condition may be raised.
/* Determine how many lines. */ count = LINES("mytext.txt", "C") /* Do that many lines. */ DO count /* Display the next line */ SAY LINEIN("mytext.txt", , 1) END