Accessing text files

Text files can be read by text editors (like TextEdit, NotePad, gedit, and BBEdit) and word processors (like Microsoft Word and Pages). Text files can easily be created, read from, or written to by your apps. Text files are convenient since they can also be read by many other apps.

Whether you are going to read from a text file or write to a text file, you must first have a FolderItem that represents the file you are going to read from or write to.

Text streams

Text streams are used to read or write data to text files. There are two text stream classes: TextInputStream and TextOutputStream. As you might expect, use TextInputStream to read data from text files. Use TextOutputStream to write data to text files.

Reading from a text file

Once you have a FolderItem that represents an existing text file you wish to open, you open the file using the Open shared method of the TextInputStream class. This method is a function that returns a “stream” that carries the text from the text file to your application. The stream is called a TextInputStream. This is a special class of object designed specifically for reading text from text files. You then use ReadAll or ReadLine methods of the TextInputStream to get the text from the text file. The TextInputStream keeps track of the last position in the file you read from.

The TextInputStream.ReadAll method returns all the text from the file (via the TextInputStream) as a String. The ReadLine method returns the next line of text (the text after the last character read but before the next end of line character). As you read text, you can determine if you have reached the end of the file by checking the TextInputStream's EndOfFile property. This property will be True when the end of the file has been reached. When you are finished reading text from the file, call the TextInputStream's Close method to close the stream to the file, making the file available to be opened again.

This code lets the user choose a text file using the Open-file dialog box and displays the text in a TextArea. It assumes that the valid text file types have been defined in a File Type Set called TextTypes:

Var f As FolderItem
f = FolderItem.ShowOpenFileDialog(TextTypes.All)
// All the file types in this set
If f <> Nil Then
  Var input As TextInputStream
  input = TextInputStream.Open(f)
  TextArea1.Text = input.ReadAll
  input.Close
End If

Because ReadAll reads all of the text in the file, the resulting string will be as large as the file. Keep this in mind because reading a large file could require a significant amount of memory and could slow down the app (or cause an error in 32-bit apps with limited memory).

This code reads the lines of text from a file stored in the Preferences folder in the System folder into a ListBox:

Var f As FolderItem
f = SpecialFolder.Preferences.Child("My Apps Prefs")
If f <> Nil And f.Exists Then
  Var input As TextInputStream
  input = TextInputStream.Open(f)
  While Not input.EndOfFile
    ListBox1.AddRow(input.ReadLine)
  Wend
  input.Close
End If

Dealing with Encodings

If you are reading and writing text files with only your app, this code will work. However, if the files are coming from other apps or platforms, in other languages or a mixture of languages, then you need to specify the encoding of the text. This is because the character codes above ACSII 127 may differ from what your app expects. When you read text, you can set the Encoding property of the TextInputStream to the encoding of the text file.

Here is the above code, amended to specify the text encoding of the incoming text stream to UTF8, which is the most common text encoding. The string containing the text will use the specified encoding until it is specifically changed to something else. The code assumes that there is a File Type Set in the project named TextTypes:

Var text As String
Var f As FolderItem
f = FolderItem.ShowOpenFileDialog(TextTypes.All)
If f <> Nil Then
  Var input As TextInputStream
  input = TextInputStream.Open(f)
  input.Encoding = Encodings.UTF8
  text = input.ReadAll
  input.Close
End If

The Encodings module provides access to all encodings. Use it whenever you need to specify an encoding. You can also specify the text encoding by passing the encoding as an optional parameter to Read or ReadAll.

Writing to a text file

Once you have a FolderItem that represents the text file you wish to open and write to, you open the file using the Append shared method of the TextOutputStream class. If you are creating a new text file or overwriting an existing text file, use the Create shared method of the TextOutputStream class. These methods are functions that return a "stream" that carries the text from your app to the text file. You then use the WriteLine method of the TextOutputStream class to write the text to the text file.

The WriteLine method, by default, adds a carriage return to the end of each line. This is controlled by the TextOutputstream's Delimiter property which can be changed to any other character.

When you are finished writing text to the file, call the TextOutputStream's Close method to close the stream to the file making the file available to be opened again. This desktop code prompts the user for a filename and then creates a file with that name and saves text from TextFields into the file:

Var file As FolderItem
file = FolderItem.ShowSaveFileDialog(FileTypes1.Text, "CreateExample.txt")
If file <> Nil Then
  Var output As TextOutputStream
  output = TextOutputStream.Create(file)
  output.WriteLine(NameField.Text)
  output.WriteLine(AddressField.Text)
  output.WriteLine(PhoneField.Text)
  output.Close
End If

This desktop code prompts the user to select an existing text file and then appends the contents of three TextFields to it. It assumes that there is a file type called “Text” in the TextTypes File Type Set, which is one of the common file types that are built into the File Type Sets Editor.

Var file As FolderItem
file = FolderItem.ShowOpenFileDialog(TextTypes.Text)
If file <> Nil Then
  Var output As TextOutputStream
  output = TextOutputStream.Open(file)
  output.WriteLine(NameField.Text)
  output.WriteLine(AddressField.Text)
  output.WriteLine(PhoneField.Text)
  output.Close
End If

Dealing with Encodings

As is the case with reading text files, you need to specify an encoding when you write out a text file. If the app that will read the file is expecting that the text is in a specific encoding, you should convert the text to that encoding before exporting it.

Before writing out a line or the entire block of text (with the Write method) use the ConvertEncoding function to convert the encoding of the text. Here is a revised code that converts the text (from UTF8, the Xojo default for Strings) to the MacRoman encoding:

Var file As FolderItem
Var output As TextOutputStream
file = FolderItem.ShowSaveFileDialog(TextTypes.Text, "My Info")
output = TextOutputStream.Create(file)
output.Write(ConvertEncoding(NameField.Text, Encodings.MacRoman)
output.Close

Random access of text files

Text files are usually accessed sequentially. This means that to read some text that is in the middle of the file, you usually read all of the text that comes before it. However, you can use the PositionB property to position the read position by a specific byte, which is not necessarily a character position, in the text file.

This also means that to write some text to the middle of a text file, you have to write all of the text that comes before the text you wish to insert, then write the text you wish to insert, then the text that follows the text you wish to insert. You can not read text from a text file and write to the same text file at the same time. If these limitations are going to be a problem for your project, consider using a binary file instead.