Reading a text file is a very common task for REALbasic programmers. But quite often it's implemented in a way that isn't reliable. Here's how to do it.
Given a FolderItem, you open the file using the FolderItem function OpenAsTextFile. This function returns a TextInputStream object, which represents the open file. OpenAsTextFile can fail for many reasons, not all of which are under your control. Possible problems include
Even if you check for these possibilities, it is possible for anything to happen between checking and attempting to open the file.
If OpenAsTextFile fails, then it simply returns a nil object. Thus you must check for this possibility in your code. You can also check the FolderItem.LastErrorCode property upon failure to see if an error code has been set. Contrary to the REALbasic documentation, an error code may be set if OpenAsTextFile fails. For example, if the file represented by the FolderItem does not exist, calling OpenAsTextFile will result in LastErrorCode returning error 101.
Here is a bit of sample code that captures what we've done so far.
Sub Read(f as FolderItem) dim theText as TextInputStream If f Is Nil then //handle error End if theText = f.OpenAsTextFile If theText <> nil then ... Else //handle error End if End Sub
Once you have a non-nil TextInputStream, you retrieve the text using one of two functions. TextInputStream.ReadAll returns the entire contents of the file as a single string. TextInputStream.ReadLine is an iterator function that returns the first line of text the first time you call it; subsequent calls return the next line of text. You cannot reset the iteration.
Remember that text is data plus an encoding that specifies how to interpret the data. Thus you should set the TextInputStream.Encoding property to the correct encoding. This property is set to UTF-8 by default. Beware that this is an arbitrary choice; FolderItem.OpenAsTextFile cannot determine the encoding of a text file. The ReadAll and ReadLine functions return strings with encoding set to the value of TextInputStream.Encoding. Also note that both of these methods have an optional TextEncoding parameter.
Usually, the reason to use a TextinputStream is to use ReadLine, and you call it in a loop. You use the (read-only) property TextInputStream.EOF to determine when to exit the loop. To handle the possibility that the text file contains no text, you should test at the beginning of the loop. You can use either a While-Wend or a Do-Loop block.
Do Until theText.EOF ... Loop
While NOT theText.EOF ... Wend
The first loop is arguably simpler, as it avoids the need for the NOT.
A recent addition to the TextInputStream class is access to file error information. The function ReadError returns true if an error has occurred, and the function LastErrorCode returns an OS error code describing the error. Since we're going for reliability, you should check for an error after reading a line of text.
Once you have finished reading the text, you should close the file; it is generally good practice not to keep files open any longer than necessary. The TextInputStream.Close method closes the file. But I suggest that you not use it. Instead, you should set the TextInputStream object reference to Nil. Once the file is closed, the object is no longer functional; setting the reference to nil will ensure that you don't mistakenly attempt to use the TextinputStream later. When the object is destroyed, the destructor closes the file.
Let's add the loop to our prototype method, with new code in blue.
Sub Read(f as FolderItem) dim theText as TextInputStream dim textLine as String If f Is Nil then //handle error End if theText = f.OpenAsTextFile If theText <> nil then theText.Encoding = TheEncoding Do Until theText.EOF textLine = theText.ReadLine If theText.ReadError then //handle error End if //code that operates on the line of text Loop theText = Nil Else //handle error End if End Sub
And that's it; all that remains is to add your own code.