TextStream class
A TextStream reads text from a file, or writes text to one, a piece at a time. It converts between the encoding of the file and the strings of the program, so the code sees text and not bytes, and the file can be of any size. It has the members of the TextStream of the Scripting Runtime, and adds Flush, Encoding and IsStreaming.
A TextStream is not created with New. These return one:
- OpenTextFile and CreateTextFile, and the same members of FileSystemObject;
- File.OpenAsTextStream and Folder.CreateTextFile;
- GetStandardStream, for the standard input, output and error streams.
Dim work As String, path As String
work = BuildPath(Environ$("TEMP"), GetTempName("fst", ""))
CreateFolder work
path = BuildPath(work, "log.txt")
Dim ts As TextStream
Set ts = CreateTextFile(path, True, encUtf16Bom)
ts.WriteLine "alpha"
ts.WriteLine "beta"
ts.Close
Debug.Print GetFile(path).Size ' 28
Set ts = OpenTextFile(path)
Debug.Print ts.Encoding = encUtf16Bom ' True
Debug.Print ts.ReadLine ' alpha
Debug.Print ts.Line ' 2
Debug.Print ts.ReadLine ' beta
Debug.Print ts.AtEndOfStream ' True
ts.Close
DeleteFolder work
The members, by purpose: AtEndOfLine, AtEndOfStream, Read, ReadAll, ReadLine, Skip and SkipLine read; Write, WriteBlankLines, WriteLine and Flush write; Close ends the stream; Column, Line, Encoding and IsStreaming describe it.
Reading and writing
A stream is open for reading or for writing, as the IOMode of OpenTextFile says; ForAppending is a way to write. A read member on a stream open for writing, and a write member on a stream open for reading, raises error 54 (Stream is not open for reading or for writing).
Encoding. For reading, the encoding is found when the stream is opened: a BOM in the file first, then the encoding that was asked for, then the same guess as GetFileEncoding. The BOM is skipped, and is not part of the text. For writing, the encoding given when the stream was opened is used, and encAutoDetect means UTF-8. A BOM is written once, when a file is created or emptied, by an encoding with Bom in its name; text added to an existing file never gets another one. Encoding returns the result.
Line endings. The reading members accept CR LF, LF and CR as line endings, also mixed in one file. WriteLine and WriteBlankLines write the line ending of the stream: the one given when it was opened, or for ForAppending to a file that has text, the one that the start of the file uses. Write writes its text unchanged.
Large files. ReadLine, Read, Write and appending have no limit on the size of the file. ReadAll is limited by the size of a String: it raises error 7 when more than 2 GB remain. The same limit applies to TextFileToString and to ReadStream of a whole file, which raise error 7 for a file of 2,200,000,000 bytes; File.Size and Folder.Size return such a size exactly.
Dim work As String, big As String, ts As TextStream, b() As Byte
work = BuildPath(Environ$("TEMP"), GetTempName("fst", ""))
CreateFolder work
big = BuildPath(work, "big.dat")
' A file of 2,200,000,000 bytes: it needs that much free space.
CreateObject("WScript.Shell").Run "cmd /c fsutil file createnew """ & big & """ 2200000000 >nul", 0, True
Debug.Print GetFile(big).Size ' 2200000000
Set ts = OpenTextFile(big, ForReading, , encLatin1)
Debug.Print Len(ts.Read(5)) ' 5
ts.Close
Set ts = OpenTextFile(big, ForAppending, , encLatin1)
ts.WriteLine "x"
ts.Close
Debug.Print GetFile(big).Size ' 2200000003
ReadStream big, b, 10
Debug.Print UBound(b) + 1 ' 10
DeleteFolder work
Two modes. Most encodings, UTF-8 among them, are read and written in pieces of 64 KB, and IsStreaming is True. UTF-7 cannot be cut into pieces. A stream in UTF-7 holds the whole file in memory and IsStreaming is False. A stream of this kind that is open for writing does not empty the file when it is opened: the file is replaced when the stream is closed or flushed. Every member works in both modes.
Position. Line and Column follow the characters that are read or written. Reading a line moves past its line ending.
Standard streams. A stream returned by GetStandardStream has the reading members when it is the standard input, and the writing members when it is the standard output or error. The other direction raises error 54. Close ends the object and leaves the handle of the process open. Encoding returns encSystemAnsi and IsStreaming returns False for these streams.
Errors
- 52 – the stream is closed.
- 54 – the member does not fit the direction of the stream.
- 62 – reading at or past the end of the stream.
- 5 – an invalid argument, such as a negative count for Read.
- 7 – ReadAll of more than 2 GB.
The Scripting Runtime raises 54 and 62 for the same mistakes. For a closed stream it raises 91 (Object variable not set), not 52.
Properties
AtEndOfLine
Whether the next character to read is a line ending.
Syntax: object.AtEndOfLine As Boolean
True when the next character is a carriage return or a line feed. False at the end of the stream, and for any other character. Nothing is consumed. Raises error 54 on a stream open for writing.
AtEndOfStream
Whether the stream has no more text to read.
Syntax: object.AtEndOfStream As Boolean
True when everything has been read. The check may read ahead in the file, but it consumes nothing, and it can raise the errors of a read. Raises error 54 on a stream open for writing. On a standard input, it waits until the next input arrives or the input ends.
Column
The column of the next character to read or write.
Syntax: object.Column As Long
The first column is 1. Columns count UTF-16 code units, so a character outside the Basic Multilingual Plane, which takes two, advances the column by two. Reading and writing both advance the column. It is 1 after a line ending.
Encoding
The encoding of the stream.
Syntax: object.Encoding As TextEncoding
A TextEncoding member. For a stream open for reading it is the encoding that was found; for one open for writing, the encoding that was asked for, with encAutoDetect read as encUtf8. A negative value means that the file has a BOM. An empty file that is opened for reading reports encUtf8 when no encoding was given. For a standard stream, also one made with unicode True, the property returns encSystemAnsi.
IsStreaming
Whether the stream works in pieces or holds the whole file.
Syntax: object.IsStreaming As Boolean
True for the encodings that can be read and written in pieces, which is nearly all. False when the stream holds the whole file in memory, as described under Two modes above. A standard stream returns False.
Line
The line of the next character to read or write.
Syntax: object.Line As Long
The first line is 1. Each line ending that is read or written adds 1; CR LF counts once. Writing counts as well: after Write "abc" & vbCrLf & "de", Line is 2 and Column is 3. A CR and an LF written by two separate calls count as two line endings.
Dim work As String, ts As TextStream
work = BuildPath(Environ$("TEMP"), GetTempName("fst", ""))
CreateFolder work
Set ts = CreateTextFile(BuildPath(work, "pos.txt"))
ts.Write "abc" & vbCrLf & "de"
Debug.Print ts.Line & "," & ts.Column ' 2,3
ts.Close
DeleteFolder work
Methods
Close
Ends the stream.
Syntax: object.Close
Writes the text that is still waiting to be written, and closes the file. Calling it again does nothing. A reading or writing member called afterwards raises error 52; Line, Column, Encoding and IsStreaming still answer. A stream that is released without Close is closed when the object is destroyed, with its waiting text written: after ts.Write "x" and Set ts = Nothing the file is one byte long. A program should still call Close.
Dim work As String, path As String, ts As TextStream
work = BuildPath(Environ$("TEMP"), GetTempName("fst", ""))
CreateFolder work
path = BuildPath(work, "x.txt")
Set ts = CreateTextFile(path)
ts.Write "x"
Set ts = Nothing
Debug.Print GetFile(path).Size ' 1
DeleteFolder work
Flush
Writes the waiting text to the file.
Syntax: object.Flush
For a stream that works in pieces, the text that is held back is encoded and handed to Windows, and the file stays open. This does not wait for the disk to confirm it. For a stream that holds the whole file, the whole file is written again. For a stream open for reading, nothing happens. Raises error 52 when the stream is closed.
Read
Reads a number of characters.
Syntax: object.Read ( characters ) As String
- characters
- required A Long, the number of characters to read. Characters count UTF-16 code units. A value of 0 returns an empty string. A negative value is error 5.
Returns that many characters, or all that remain when fewer are left. Line endings are returned as they are in the file. Raises error 62 when the stream is already at its end.
ReadAll
Reads everything that is left.
Syntax: object.ReadAll As String
Returns the rest of the stream, which is an empty string at the end of the stream, and for an empty file. The Scripting Runtime raises error 62 there. The text is decoded in one step and kept in memory, so a very large file is better read with ReadLine. Raises error 7, without reading anything, when the rest is more than 2 GB.
Dim work As String, path As String, ts As TextStream
work = BuildPath(Environ$("TEMP"), GetTempName("fst", ""))
CreateFolder work
path = BuildPath(work, "empty.txt")
StringToTextFile "", path
Set ts = OpenTextFile(path)
Debug.Print "[" & ts.ReadAll & "]" ' []
ts.Skip 3 ' nothing happens at the end
On Error Resume Next
ts.SkipLine
Debug.Print Err.Number ' 62
Err.Clear
On Error GoTo 0
ts.Close
DeleteFolder work
ReadLine
Reads one line.
Syntax: object.ReadLine As String
Returns the text up to the next line ending, without the line ending. CR LF, LF and CR each end a line. The last line is returned also when no line ending follows it. Raises error 62 when the stream is already at its end.
Skip
Skips a number of characters.
Syntax: object.Skip ( characters )
- characters
- required A Long, the number of characters to skip. A value of 0 or less does nothing.
Does nothing at the end of the stream, where SkipLine raises error 62. The Scripting Runtime raises error 62 for Skip at the end too.
SkipLine
Skips one line.
Syntax: object.SkipLine
Skips the text and the line ending of the next line. Raises error 62 when the stream is already at its end.
Write
Writes text.
Syntax: object.Write ( text )
- text
- required A String. It is written as it is, and no line ending is added.
A stream that works in pieces holds the text back until 65,536 characters have collected, or until Flush or Close; a stream that holds the whole file writes it at Flush or Close. A surrogate pair is never cut between two pieces. For a code page that cannot hold a character, the character is replaced by the substitute of the code page, without an error; UTF-8 and GB18030 raise error 5 for an unpaired surrogate.
WriteBlankLines
Writes empty lines.
Syntax: object.WriteBlankLines ( lines )
- lines
- required A Long, the number of line endings to write.
WriteLine
Writes text and a line ending.
Syntax: object.WriteLine ( [ text ] )
- text
- optional A String to write before the line ending. Default: an empty string, which writes a line ending alone.
The line ending is the one of the stream: see Line endings above.
See Also
- FileSystemTools module – OpenTextFile, CreateTextFile and GetStandardStream create streams
- File class – OpenAsTextStream opens a file as a stream
- Folder class – CreateTextFile creates a file in a folder and returns a stream
- Open statement – the VBA statement that opens a file for input and output
- Line Input # statement – the VBA statement that reads a line from a file
- Print # statement – the VBA statement that writes text to a file