7.5 Texte mit TextReader und TextWriter 

Zeichenketten werden in .NET in Unicode codiert. Jeder Buchstabe besteht aus 1 bis 2 Byte (bzw. 4 bei Surrogaten). Beim Lesen und Schreiben einzelner Bytes ist es sehr lästig, einzelne Bytes zusammenzusetzen oder auseinanderzupflücken. Daher gibt es in .NET die beiden ab-strakten Klassen TextReader und TextWriter, die sich um das korrekte Hantieren mit Texten kümmern. Aus der folgenden Klassenhierarchie werden wir uns mit den Klassen StreamReader/StreamWriter und StringReader/StringWriter beschäftigen. Die Stream-Variante operiert auf Dateien, die String-Variante auf Zeichenketten.
System.Object
+System.MarshalByRefObject
+3-+TextReader
| +1--DocDataTextReader
| +3-+StreamReader
| +StringReader
+TextWriter
+1--DocDataTextWriter
+2--IndentedTextWriter
+3-+StreamWriter
| +StringWriter
+4--HttpWriter
+5--HtmlTextWriter
+5-+Html32TextWriter
| | +5--ChtmlTextWriter
| +XhtmlTextWriter
+6--MultiPartWriter
+6--MobileTextWriter
+6-+HtmlMobileTextWriter
| | +6--ChtmlMobileTextWriter
| +WmlMobileTextWriter
| +6--UpWmlMobileTextWriter
+7--XhtmlMobileTextWriter
1: Microsoft.VisualStudio.Shell.Design.Serialization
2: System.CodeDom.Compiler
3: System.IO
4: System.Web
5: System.Web.UI
6: System.Web.UI.MobileControls.Adapters
7: System.Web.UI.MobileControls.Adapters.XhtmlAdapters
7.5.1 Texte mit StreamWriter schreiben 

Konstruktoren
Wir wenden uns zunächst einigen Konstruktoren der Klasse StreamWriter zu. In der folgenden Syntax sind optionale Parameter kursiv gesetzt.
Public Sub New(path As String, _ |
Der Endpunkt des Writers wird im ersten Parameter übergeben. Es ist entweder ein Dateipfad oder ein beliebiger von Stream abgeleiteter Strom, zum Beispiel FileStream. In der Einleitung zu Abschnitt 7.4, »Datenflüsse in Streams«, haben wir die Ströme in zwei Klassen eingeteilt. In dieser Terminologie ist der StreamWriter mit Stream-Parameter ein Pass-Through-Stream, sein erster Parameter ist entweder ein Base-Stream oder selbst ein Pass-Through-Stream. Dadurch kann jeder Strom (nachträglich) mit Textverarbeitungsfähigkeiten ausgestattet werden.
Zum Zugriff auf eine Datei können Sie einfach den Pfad zur Datei angeben, zum Beispiel so:
Dim myStreamWriter As StreamWriter = New StreamWriter("C:\MyText.txt")
Wir erzeugen mit dieser Anweisung einen Base-Stream, der eine Zeichenfolge in eine Datei schreiben kann. Alternativ können wir den StreamWriter auf einem FileStream aufbauen.
Dim fs As FileStream = New FileStream("C:\Test.txt", FileMode.CreateNew)
Dim myStreamWriter As StreamWriter = New StreamWriter(fs)
In der ersten Zeile erzeugen wir einen FileStream, der auf die Datei Test.txt im Stammverzeichnis des Laufwerks C: zugreifen kann. Auf ihn bauen wir in der zweiten Zeile einen StreamWriter auf. Durch die Hintereinanderschaltung von FileStream und StreamWriter werden deren Fähigkeiten kombiniert: Der FileStream-Teil kümmert sich um das byteweise Lesen und Schreiben, während der StreamWriter aus den einzelnen Bytes Zeichen macht bzw. diese in Bytes zerlegt (das Encoding regelt, wie das geschieht). Genauso können Sie natürlich auch einen MemoryStream oder NetworkStream als Argument übergeben.
Standardmäßig verschlüsselt StreamWriter nach UTF-8, Abweichungen davon geben Sie im Parameter vom Typ System.Text.Encoding an, zum Beispiel UTF7Encoding oder UnicodeEncoding (entspricht der UTF-16-Kodierung).
Hinweis |
Welchen Buchstaben ein Bytewert beschreibt, regelt die Kodierung. Unter Windows ist in Mitteleuropa ANSI (Codeseite 1252) üblich, das Codes zwischen 0 und 255 erlaubt und Umlaute und einige Sonderzeichen enthält. Demgegenüber läuft ASCII von 0 bis 127 und ist in diesem Bereich mit ANSI identisch. Diesen starken Restriktionen ist Unicode nicht unterworfen, das 1-4 Byte zur Kodierung benutzt. Je nach minimal benutzter Bitzahl unterscheidet man UTF-7, UTF-8, UTF-16 und UTF-32. Der unter .NET verwendete Standard UTF-8 kodiert Codes zwischen 0 und 127 identisch zu ASCII, sodass für diese keine Umkodierung nötig ist und reine ASCII-Systeme damit zurechtkommen. |
Ein auf einem Pfad basierender StreamWriter gibt im Parameter append an, ob geschriebene Daten an die Datei angehängt werden oder die Datei überschrieben wird. Existiert die Datei nicht, wird sie erstellt, und der Parameter hat keinen Einfluss. Im letzten Parameter können Sie eine Größe für den Puffer angeben.
Schreiben in den Datenstrom
Das folgende Codefragment schreibt einen kurzen Text in eine (neue) Datei:
Dim sw As StreamWriter = New StreamWriter("C:\NewFile.txt")
sw.WriteLine("Es saß ein Wiesel")
sw.WriteLine("auf einem Kiesel")
sw.Close()
Zuerst wird ein StreamWriter-Objekt unter Angabe eines Dateipfads erzeugt. Daraufhin wird entweder die Datei erzeugt oder eine existierende gleichnamige im angegebenen Verzeichnis überschrieben. Mit jedem Aufruf der von TextWriter geerbten Methode WriteLine wird eine Zeile und ein Zeilenumbruch in die Datei geschrieben, hier zwei Zeilen:
Public Overridable Sub WriteLine() |
Der letzten Überladung bedient sich die Klasse Console zur Ausgabe. Korrespondierend zu dieser Methode schreibt Write Daten, ohne einen Zeilenvorschub anzuhängen (es fehlt nur die parameterlose Variante). Die Methoden Write und WriteLine bilden den Kern der Klasse StreamWriter; Tabelle 7.18 zeigt alle ihre Methoden.
Methode | Beschreibung |
Close |
Schließt das aktuelle Objekt sowie alle eingebetteten Streams. |
Flush |
Schreibt die gepufferten Daten in den Stream und löscht den Pufferinhalt. |
Synchronized |
Erstellt einen threadsicheren Wrapper (Shared). |
Write |
Schreibt in den Stream, ohne einen Zeilenumbruch anzuhängen. |
WriteLine |
Schreibt in den Stream und schließt mit einem Zeilenumbruch ab. |
Eigenschaften
Mit AutoFlush=True wird bei jedem Aufruf von Write-/WriteLine der Puffer in den Datenstrom geschrieben. Die Eigenschaft Encoding liefert die Zeichenkodierung.
Dim sw As StreamWriter = _
New StreamWriter("C:\NewFile.txt", False, Encoding.Unicode)
Console.WriteLine("Format: {0}", sw.Encoding.ToString())
BaseStream ist der zugrunde liegende Stream, ob explizit gegeben oder implizit durch eine Pfadangabe erzeugt. Tabelle 7.19 zeigt alle Eigenschaften von StreamWriter.
Eigenschaften | Beschreibung | |
AutoFlush |
Löscht den Puffer nach jedem Aufruf von Write oder WriteLine. |
|
BaseStream |
Liefert eine Referenz auf den Base-Stream zurück. |
R |
Encoding |
Liefert das aktuelle Encoding-Schema zurück. |
R |
FormatProvider |
Art der Formatierung (Zahlen, Datum) |
R |
NewLine |
Verwendeter Zeilenumbruch |
|
Null |
Datenziel »Nirgendwo« (klassengebundenes Feld) |
R |
7.5.2 Texte mit StreamReader lesen 

Die aus der Klasse TextReader abgeleitete Klasse StreamReader ist das Gegenstück zum StreamWriter des vorigen Abschnitts. Statt mir Write zu schreiben, liest sie mit Read.
Die Konstruktoren sind identisch zu StreamWriter, außer dass der append-Parameter wegfällt und der neue Parameter detectEncodingFromByteOrderMarks hinzukommt, der angibt, ob die ersten drei Bytes zur automatischen Erkennung des Encodings benutzt werden sollen.
Tabelle 7.20 enthält die Methoden eines StreamReaders.
Methode | Beschreibung |
Close |
Schließt das aktuelle Objekt sowie alle eingebetteten Streams. |
DiscardBufferedData |
Verwirft gepufferte Daten. |
Peek |
Liest ein Zeichen aus dem Strom, ohne den Positionszeiger zu ändern, und liefert das Zeichen als Integer. Ist der Zeiger hinter dem Datenstrom, ist dieser Wert -1. |
Read |
Liest ein Zeichen aus dem Strom unter Änderung des Positionszeigers und liefert das Zeichen als Integer. Ist der Zeiger hinter dem Datenstrom, ist dieser Wert –1. Eine zweite Variante liest mehrere Zeichen und gibt die Anzahl gelesener Zeichen zurück. |
ReadLine |
Liest eine Zeile aus dem Strom – entweder bis zum Zeilenumbruch oder bis zum Ende des Stroms. Der Rückgabewert hat den Typ String (mit dem Wert Nothing, wenn hinter dem Strom gelesen wird). |
ReadToEnd |
Liest von der aktuellen Position des Positionszeigers bis zum Ende des Stroms alle Zeichen ein (am Stromende steht ein Leerstring). |
Wir wollen nun an einem Codebeispiel das Lesen aus einem Strom testen.
'...\IO\Ströme\Texte.vb |
Option Strict On
Imports System.IO
Namespace EA
Module Texte
Sub Test()
' Datei erzeugen und mit Text füllen
Dim path As String = IO.Path.GetTempFileName()
Dim sw As New StreamWriter(path)
sw.WriteLine("Es saß ein Wiesel")
sw.WriteLine("auf einem Kiesel")
sw.Close()
' Datei einlesen und an der Konsole ausgeben
Dim sr As New StreamReader(path)
While sr.Peek() <> –1
Console.WriteLine(sr.ReadLine())
End While
sr.Close()
File.Delete(path)
Console.ReadLine()
End Sub
End Module
End Namespace
Um etwas zu lesen zu haben, schreiben wir mit einem StreamWriter einen zweizeiligen Text in eine beliebige Datei (die Endung des Dateinamens spielt keine Rolle). Damit ein anderer Strom die Datei auslesen kann, schließen wir den StreamWriter.
Mit einem StremReader, der denselben Pfad benutzt, lesen wir mit ReadLine die Zeilen der Datei in einer Schleife aus. Die Abbruchbedingung der Schleife verwendet Peek zur Erkennung des Dateiendes. Die Methode gibt dann -1 zurück. Die Ausgabe zeigt genau zwei Zeilen:
Es saß ein Wiesel
auf einem Kiesel
Wenn Sie eine Datei komplett einlesen wollen, können Sie statt der Schleife auch
Console.WriteLine(sr.ReadToEnd())
verwenden oder mit
File.ReadAllText(path)
komplett auf den StreamReader verzichten.
Die Read-Methode
Die Read-Methode der Klasse StreamReader ist flexibler als ReadLine und kommt auch mit nicht-zeilenorientierten Strömen zurecht.
Public Overrides Function Read() As Integer |
Die parameterlose Variante liest ein Zeichen ein und setzt den Positionszeiger auf das nächste zu lesende Zeichen. Die parametrisierte Variante liest maximal count Zeichen und schreibt sie ab der Position index in das Array buffer. Sie gibt die Zahl der tatsächlich gelesenen Zeichen zurück (0 bei Stromende).
Bei einer Datei gibt Ihnen die Eigenschaft Length von FileInfo die maximale Zahl einlesbarer Zeichen an (maximal, weil ein Buchstabe aus bis zur vier Byte besteht). Die folgende Zeile liest einen Dateiinhalt komplett in den angegebenen Puffer.
sr.Read(puffer, 0, CType(fi.Length, Integer))
Bitte beachten Sie beim Zeichenzählen, dass jeder Zeilenvorschub auch mitzählt. Unter Windows sind es die beiden Zeichen mit den Codes 10 und 13.
7.5.3 Zeichenketten mit StringWriter und StringReader 

Der Endpunkt der Klassen StringReader/StringWriter ist eine Zeichenkette. Damit können Sie diese mit der ganzen Funktionalität der Ströme »nachrüsten«.
StringReader liest Zeichen eines Strings, der dem Konstruktor übergeben wird.
Public Sub New(s As String) |
Die Klasse hat keine Eigenschaften, aber (fast) alle Methoden, die auch ein StreamReader hat (mit gleicher Funktionalität).
Public Overrides Sub Close() |
Dazu ein Beispiel:
Dim str As String = ""
str += "Dunkel war's, der Mond schien helle, " & Environment.NewLine
str += "Als ein Wagen mit Blitzesschnelle" & Environment.NewLine
str += "Langsam um die Ecke fuhr." & Environment.NewLine
' Einlesen und Ausgeben der ersten beiden Zeilen
Dim sr2 As New StringReader(str)
Console.WriteLine(sr2.ReadLine())
Console.WriteLine(sr2.ReadLine())
StringWriter schreibt die Ausgabe in ein Objekt vom Typ StringBuilder, der im Konstruktor ebenso spezifiziert werden kann wie die Art der Formatierung (Zahlen, Datum).
Public Sub New() |
Mit Write schreiben Sie Einzelzeichen oder Zeichenketten in den StringBuilder:
Dim sw As New StringWriter()
sw.Write("Text des StringWriter-Objekts")
Console.WriteLine(sw.GetStringBuilder())
Außer GetStringBuilder hat die Klasse keine besonderen Methoden:
Public Overrides Sub Close() |
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.