{"id":55000479,"date":"2026-04-01T00:00:00","date_gmt":"2026-05-16T19:27:49","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=479"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Textdateien_und_Stream_mit_dem_FileSystemObject","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/Textdateien_und_Stream_mit_dem_FileSystemObject\/","title":{"rendered":"Textdateien und Stream mit dem FileSystemObject"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg08.met.vgwort.de\/na\/670f92b4c40742ab875f496d854768f1\" width=\"1\" height=\"1\" alt=\"\"><b>Im Artikel <b>Dateimanagement mit dem FileSystemObject<\/b> (<b>www.vbentwickler.de\/478<\/b>) haben wir die Klasse <b>FileSystemObject<\/b> kennengelernt und damit Laufwerke, Verzeichnisse und Dateien verwaltet. Dabei haben wir drei Methoden und Funktionen erw&auml;hnt, die wir in einem separaten Artikel behandeln wollten: <b>CreateTextFile<\/b>, <b>OpenTextFile<\/b> und <b>OpenAsTextStream<\/b>. Diese drei Elemente sind das Tor zur Klasse <b>TextStream<\/b>, mit der wir Textdateien erstellen, beschreiben und auslesen k&ouml;nnen. In diesem Artikel schauen wir uns zuerst an, wie wir <b>TextStream<\/b>-Objekte erzeugen, und gehen dann alle Eigenschaften und Methoden der Klasse durch. Abschlie&szlig;end bauen wir ein praxisnahes Beispiel, in dem wir eine CSV-Datei erzeugen und wieder einlesen.<\/b><\/p>\n<h2>Beispieldatenbank<\/h2>\n<p>Die Beispiele dieses Artikels findest Du in der Beispieldatenbank zum Artikel. Alle Prozeduren befinden sich im Modul <b>mdlTextStream<\/b>. Um die Funktionen des <b>FileSystemObject<\/b> nutzen zu k&ouml;nnen, ben&ouml;tigst Du einen Verweis auf die Bibliothek <b>Microsoft Scripting Runtime<\/b>. Diesen richtest Du &uuml;ber den Men&uuml;punkt <b>Extras|Verweise<\/b> im VBA-Editor ein.<\/p>\n<h2>Drei Wege zum TextStream<\/h2>\n<p>Die Klasse <b>TextStream<\/b> l&auml;sst sich nicht direkt instanziieren. Wir k&ouml;nnen also kein Objekt mit <b>New TextStream<\/b> erzeugen. Stattdessen liefern uns drei Funktionen beziehungsweise Methoden ein solches Objekt zur&uuml;ck:<\/p>\n<ul>\n<li><b>CreateTextFile<\/b>: Erstellt eine neue Textdatei und gibt ein <b>TextStream<\/b>-Objekt zum Beschreiben zur&uuml;ck.<\/li>\n<li><b>OpenTextFile<\/b>: &Ouml;ffnet eine vorhandene Textdatei und gibt ein <b>TextStream<\/b>-Objekt zum Lesen, Schreiben oder Anh&auml;ngen zur&uuml;ck.<\/li>\n<li><b>OpenAsTextStream<\/b>: Methode der <b>File<\/b>-Klasse, die ein <b>TextStream<\/b>-Objekt auf Basis eines bereits referenzierten <b>File<\/b>-Objekts &ouml;ffnet.<\/li>\n<\/ul>\n<p>Alle drei Varianten liefern am Ende ein Objekt vom Typ <b>TextStream<\/b>, das &uuml;ber dieselben Eigenschaften und Methoden verf&uuml;gt. Der Unterschied liegt nur darin, wie wir an dieses Objekt gelangen.<\/p>\n<h2>Textdatei erstellen mit CreateTextFile<\/h2>\n<p>Die Funktion <b>CreateTextFile<\/b> ist eine Methode des <b>FileSystemObject<\/b>. Sie erwartet als ersten Parameter den Pfad zu der zu erstellenden Datei. Optional k&ouml;nnen wir mit dem zweiten Parameter (<b>Overwrite<\/b>) angeben, ob eine bereits vorhandene Datei &uuml;berschrieben werden soll. Der Standardwert ist <b>True<\/b>. Ein dritter optionaler Parameter (<b>Unicode<\/b>) legt fest, ob die Datei im Unicode-Format erstellt werden soll. Der Standardwert ist <b>False<\/b>, was eine ANSI-Datei erzeugt.<\/p>\n<p>Die folgende Prozedur erstellt eine neue Textdatei im aktuellen Datenbankverzeichnis und schreibt drei Zeilen hinein:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TextdateiErstellen()\r\n    <span style=\"color:blue;\">Dim <\/span>objFSO<span style=\"color:blue;\"> As <\/span>Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Dim <\/span>objTextstream<span style=\"color:blue;\"> As <\/span>Scripting.TextStream\r\n    <span style=\"color:blue;\">Dim <\/span>strPfad<span style=\"color:blue;\"> As String<\/span>\r\n    strPfad = CurrentProject.Path & \"\\Beispiel.txt\"\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = objFSO.CreateTextFile(strPfad, <span style=\"color:blue;\">True<\/span>)\r\n    objTextstream.WriteLine \"Erste Zeile\"\r\n    objTextstream.WriteLine \"Zweite Zeile\"\r\n    objTextstream.WriteLine \"Dritte Zeile\"\r\n    objTextstream.Close\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = Nothing\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Wir erzeugen ein <b>FileSystemObject<\/b> und rufen dessen <b>CreateTextFile<\/b>-Methode auf. Das Ergebnis ist ein <b>TextStream<\/b>-Objekt, das bereits zum Schreiben ge&ouml;ffnet ist. Mit <b>WriteLine<\/b> schreiben wir drei Zeilen in die Datei und schlie&szlig;en den Stream anschlie&szlig;end mit <b>Close<\/b>.<\/p>\n<p>&Uuml;brigens: <b>CreateTextFile<\/b> gibt es nicht nur am <b>FileSystemObject<\/b>, sondern auch an der <b>Folder<\/b>-Klasse. Dort &uuml;bergeben wir lediglich den Dateinamen ohne Verzeichnisangabe, weil das Verzeichnis bereits durch das <b>Folder<\/b>-Objekt festgelegt ist.<\/p>\n<h2>Textdatei &ouml;ffnen mit OpenTextFile<\/h2>\n<p>Um eine vorhandene Textdatei zu &ouml;ffnen, verwenden wir die Funktion <b>OpenTextFile<\/b> des <b>FileSystemObject<\/b>. Diese erwartet als ersten Parameter den Pfad zur Datei.<\/p>\n<p>Der zweite Parameter (<b>IOMode<\/b>) gibt den Zugriffsmodus an. Hier stehen drei Konstanten zur Verf&uuml;gung:<\/p>\n<ul>\n<li><b>ForReading<\/b> (<b>1<\/b>): &Ouml;ffnet die Datei nur zum Lesen.<\/li>\n<li><b>ForWriting<\/b> (<b>2<\/b>): &Ouml;ffnet die Datei zum Schreiben. Dabei wird der vorhandene Inhalt &uuml;berschrieben.<\/li>\n<li><b>ForAppending<\/b> (<b>8<\/b>): &Ouml;ffnet die Datei zum Anh&auml;ngen. Neuer Inhalt wird am Ende der Datei angef&uuml;gt.<\/li>\n<\/ul>\n<p>Optional kann ein dritter Parameter (<b>Create<\/b>) angegeben werden, der festlegt, ob die Datei erstellt werden soll, wenn sie noch nicht existiert. Der Standardwert ist <b>False<\/b>. Ein vierter Parameter (<b>Format<\/b>) steuert die Zeichenkodierung: <b>TristateFalse<\/b> (<b>0<\/b>) f&uuml;r ANSI, <b>TristateTrue<\/b> (<b>-1<\/b>) f&uuml;r Unicode und <b>TristateUseDefault<\/b> (<b>-2<\/b>) f&uuml;r die Systemvorgabe.<\/p>\n<p>Das folgende Beispiel &ouml;ffnet die zuvor erstellte Datei zum Lesen und gibt den gesamten Inhalt im Direktbereich aus:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TextdateiLesen()\r\n    <span style=\"color:blue;\">Dim <\/span>objFSO<span style=\"color:blue;\"> As <\/span>Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Dim <\/span>objTextstream<span style=\"color:blue;\"> As <\/span>Scripting.TextStream\r\n    <span style=\"color:blue;\">Dim <\/span>strPfad<span style=\"color:blue;\"> As String<\/span>\r\n    strPfad = CurrentProject.Path & \"\\Beispiel.txt\"\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = objFSO.OpenTextFile(strPfad, _\r\n        ForReading)\r\n    <span style=\"color:blue;\">Debug.Print<\/span> objTextstream.ReadAll\r\n    objTextstream.Close\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = Nothing\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Hier nutzen wir die Methode <b>ReadAll<\/b>, um den gesamten Inhalt der Datei auf einmal einzulesen. Die Details zu <b>ReadAll<\/b> und den &uuml;brigen Methoden der <b>TextStream<\/b>-Klasse folgen weiter unten.<\/p>\n<h2>TextStream &uuml;ber ein File-Objekt &ouml;ffnen mit OpenAsTextStream<\/h2>\n<p>Der dritte Weg f&uuml;hrt &uuml;ber die <b>File<\/b>-Klasse. Wenn wir bereits ein <b>File<\/b>-Objekt referenziert haben, k&ouml;nnen wir dessen Methode <b>OpenAsTextStream<\/b> aufrufen. Die Parameter sind identisch mit denen von <b>OpenTextFile<\/b>, allerdings ohne den Dateipfad, da dieser bereits durch das <b>File<\/b>-Objekt feststeht.<\/p>\n<p>Das folgende Beispiel zeigt, wie wir zun&auml;chst ein <b>File<\/b>-Objekt holen und darauf einen <b>TextStream<\/b> &ouml;ffnen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TextdateiPerFileObjekt()\r\n     <span style=\"color:blue;\">Dim <\/span>objFSO<span style=\"color:blue;\"> As <\/span>Scripting.FileSystemObject\r\n     <span style=\"color:blue;\">Dim <\/span>objFile<span style=\"color:blue;\"> As <\/span>Scripting.File\r\n     <span style=\"color:blue;\">Dim <\/span>objTextstream<span style=\"color:blue;\"> As <\/span>Scripting.TextStream\r\n     <span style=\"color:blue;\">Dim <\/span>strPfad<span style=\"color:blue;\"> As String<\/span>\r\n     strPfad = CurrentProject.Path _\r\n          & \"\\Beispiel.txt\"\r\n     <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n     <span style=\"color:blue;\">Set<\/span> objFile = objFSO.GetFile(strPfad)\r\n     <span style=\"color:blue;\">Set<\/span> objTextstream = objFile.OpenAsTextStream( _\r\n          ForReading)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> objTextstream.ReadAll\r\n     objTextstream.Close\r\n     <span style=\"color:blue;\">Set<\/span> objTextstream = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> objFile = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> objFSO = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Wir holen zun&auml;chst mit <b>GetFile<\/b> ein <b>File<\/b>-Objekt und rufen dann dessen <b>OpenAsTextStream<\/b>-Methode auf. Das Ergebnis ist wiederum ein <b>TextStream<\/b>-Objekt, das wir wie gewohnt verwenden k&ouml;nnen.<\/p>\n<p>Dieser Weg bietet sich vor allem an, wenn wir ohnehin bereits mit <b>File<\/b>-Objekten arbeiten, zum Beispiel beim Durchlaufen der <b>Files<\/b>-Auflistung eines Verzeichnisses.<\/p>\n<h2>Die Eigenschaften der TextStream-Klasse<\/h2>\n<p>Die <b>TextStream<\/b>-Klasse bietet vier Eigenschaften, mit denen wir Informationen &uuml;ber die aktuelle Position innerhalb des Streams abfragen k&ouml;nnen. Alle vier sind schreibgesch&uuml;tzt.<\/p>\n<ul>\n<li><b>AtEndOfLine<\/b> gibt <b>True<\/b> zur&uuml;ck, wenn sich die Leseposition am Ende einer Zeile befindet, also direkt vor dem Zeilenumbruch. Diese Eigenschaft steht nur bei Streams zur Verf&uuml;gung, die zum Lesen ge&ouml;ffnet wurden. Versuchen wir, sie bei einem zum Schreiben ge&ouml;ffneten Stream abzufragen, erhalten wir einen Laufzeitfehler.<\/li>\n<li><b>AtEndOfStream<\/b> gibt <b>True<\/b> zur&uuml;ck, wenn die Leseposition das Ende der Datei erreicht hat. Auch diese Eigenschaft ist nur bei lesend ge&ouml;ffneten Streams verf&uuml;gbar. Sie entspricht in etwa der <b>EOF<\/b>-Funktion, die wir bei den klassischen VBA-Dateioperationen mit <b>Open<\/b> und <b>Close<\/b> verwenden.<\/li>\n<li><b>Column<\/b> liefert die aktuelle Spaltenposition innerhalb der Zeile. Die Z&auml;hlung beginnt bei 1. Nach dem &Ouml;ffnen einer Datei zum Lesen steht <b>Column<\/b> auf 1. Nach dem Lesen des ersten Zeichens steht der Wert auf 2 und so weiter.<\/li>\n<li><b>Line<\/b> gibt die aktuelle Zeilennummer zur&uuml;ck. Auch hier beginnt die Z&auml;hlung bei 1. Jedes Mal, wenn ein Zeilenumbruch &uuml;berschritten wird, erh&ouml;ht sich der Wert um eins.<\/li>\n<\/ul>\n<p>Mit <b>Column<\/b> und <b>Line<\/b> zusammen k&ouml;nnen wir jederzeit feststellen, an welcher Stelle in der Datei wir uns beim Lesen befinden. Das kann zum Beispiel bei der Fehleranalyse hilfreich sein, wenn beim Einlesen einer Datei ein unerwarteter Wert auftritt und wir die genaue Position im Fehlerprotokoll festhalten m&ouml;chten.<\/p>\n<h2>Methoden zum Lesen: Read, ReadLine und ReadAll<\/h2>\n<p>Die <b>TextStream<\/b>-Klasse bietet drei Methoden zum Lesen von Inhalten. Alle drei setzen voraus, dass der Stream zum Lesen ge&ouml;ffnet wurde (Modus <b>ForReading<\/b>).<\/p>\n<p><b>Read<\/b> erwartet als Parameter die Anzahl der zu lesenden Zeichen und gibt diese als Zeichenkette zur&uuml;ck. Nach dem Aufruf wird die Leseposition um die entsprechende Anzahl Zeichen weiterger&uuml;ckt. Das folgende Beispiel liest die ersten zehn Zeichen einer Datei:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>ErsteZeichenLesen()\r\n    ...\r\n    <span style=\"color:blue;\">Dim <\/span>strPfad<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strErgebnis<span style=\"color:blue;\"> As String<\/span>\r\n    strPfad = CurrentProject.Path & \"\\Beispiel.txt\"\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = _\r\n        objFSO.OpenTextFile(strPfad, ForReading)\r\n    strErgebnis = objTextstream.Read(10)\r\n    <span style=\"color:blue;\">Debug.Print<\/span> strErgebnis\r\n    ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b>ReadLine<\/b> liest eine komplette Zeile bis zum n&auml;chsten Zeilenumbruch und gibt diese ohne den Zeilenumbruch zur&uuml;ck. Die Leseposition wird in die n&auml;chste Zeile verschoben. In Kombination mit <b>AtEndOfStream<\/b> k&ouml;nnen wir damit alle Zeilen einer Datei durchlaufen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>AlleZeilenLesen()\r\n    ...\r\n    strPfad = CurrentProject.Path & \"\\Beispiel.txt\"\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = _\r\n        objFSO.OpenTextFile(strPfad, ForReading)\r\n    <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> objTextstream.AtEndOfStream\r\n        <span style=\"color:blue;\">Debug.Print<\/span> objTextstream.ReadLine\r\n    <span style=\"color:blue;\">Loop<\/span>\r\n    ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dies ist das Gegenst&uuml;ck zu der aus den klassischen VBA-Dateioperationen bekannten Kombination aus <b>Line Input #<\/b> und <b>EOF<\/b>.<\/p>\n<p><b>ReadAll<\/b> liest den gesamten verbleibenden Inhalt der Datei in eine einzige Zeichenkette. Das ist praktisch, wenn wir den vollst&auml;ndigen Dateiinhalt auf einmal verarbeiten m&ouml;chten. Bei sehr gro&szlig;en Dateien sollte man diese Methode allerdings mit Bedacht einsetzen, da der gesamte Inhalt im Arbeitsspeicher gehalten wird.<\/p>\n<h2>Methoden zum Schreiben: Write, WriteLine und WriteBlankLines<\/h2>\n<p>F&uuml;r das Schreiben stehen ebenfalls drei Methoden bereit. Diese setzen voraus, dass der Stream zum Schreiben (<b>ForWriting<\/b>) oder zum Anh&auml;ngen (<b>ForAppending<\/b>) ge&ouml;ffnet wurde.<\/p>\n<p><b>Write<\/b> schreibt eine Zeichenkette in den Stream, ohne einen Zeilenumbruch anzuh&auml;ngen. Mehrere aufeinanderfolgende Aufrufe von <b>Write<\/b> erzeugen damit zusammenh&auml;ngenden Text in derselben Zeile:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TextOhneUmbruch()\r\n    ...\r\n    strPfad = CurrentProject.Path & \"\\WriteTest.txt\"\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = _\r\n        objFSO.CreateTextFile(strPfad, <span style=\"color:blue;\">True<\/span>)\r\n    objTextstream.Write \"Vorname\"\r\n    objTextstream.Write \";\"\r\n    objTextstream.Write \"Nachname\"\r\n    ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Das Ergebnis in der Datei w&auml;re eine einzelne Zeile mit dem Inhalt <b>Vorname;Nachname<\/b>.<\/p>\n<p><b>WriteLine<\/b> funktioniert wie <b>Write<\/b>, h&auml;ngt aber automatisch einen Zeilenumbruch an. Jeder Aufruf von <b>WriteLine<\/b> erzeugt also eine eigene Zeile in der Datei. Rufen wir <b>WriteLine<\/b> ohne Parameter auf, wird lediglich eine Leerzeile geschrieben.<\/p>\n<p><b>WriteBlankLines<\/b> erwartet als Parameter die Anzahl der einzuf&uuml;genden Leerzeilen. Der folgende Aufruf f&uuml;gt drei leere Zeilen in die Datei ein:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>LeerzeilenEinfuegen()\r\n    ...\r\n    strPfad = CurrentProject.Path & \"\\Leerzeilen.txt\"\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = objFSO.CreateTextFile( _\r\n        strPfad, <span style=\"color:blue;\">True<\/span>)\r\n    objTextstream.WriteLine \"Abschnitt 1\"\r\n    objTextstream.WriteBlankLines 3\r\n    objTextstream.WriteLine \"Abschnitt 2\"\r\n    ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese Methode ist n&uuml;tzlich, wenn wir in einer Ausgabedatei Abschnitte visuell voneinander trennen m&ouml;chten.<\/p>\n<h2>Navigieren im Stream: Skip und SkipLine<\/h2>\n<p>Neben den Lese- und Schreibmethoden bietet die <b>TextStream<\/b>-Klasse zwei Methoden, mit denen wir Inhalte beim Lesen &uuml;berspringen k&ouml;nnen, ohne diese in eine Variable einzulesen.<\/p>\n<p><b>Skip<\/b> erwartet als Parameter die Anzahl der zu &uuml;berspringenden Zeichen. Die Leseposition wird um die angegebene Anzahl Zeichen vorger&uuml;ckt:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>ZeichenUeberspringen()\r\n    ...\r\n    strPfad = CurrentProject.Path & \"\\Beispiel.txt\"\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = _\r\n        objFSO.OpenTextFile(strPfad, ForReading)\r\n    objTextstream.Skip 5\r\n    <span style=\"color:blue;\">Debug.Print<\/span> objTextstream.ReadLine\r\n    ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b>SkipLine<\/b> &uuml;berspringt die aktuelle Zeile vollst&auml;ndig, einschlie&szlig;lich des Zeilenumbruchs. Die Leseposition steht danach am Anfang der n&auml;chsten Zeile.<\/p>\n<p>Beide Methoden funktionieren nur bei Streams, die zum Lesen ge&ouml;ffnet wurden. Ein typischer Anwendungsfall f&uuml;r <b>SkipLine<\/b> ist das &Uuml;berspringen einer Kopfzeile beim Einlesen von CSV-Dateien.<\/p>\n<p>Statt die erste Zeile mit <b>ReadLine<\/b> in eine Variable zu lesen, die wir anschlie&szlig;end gar nicht ben&ouml;tigen, &uuml;berspringen wir sie einfach.<\/p>\n<h2>Stream schlie&szlig;en mit Close<\/h2>\n<p>Die <b>Close<\/b>-Methode schlie&szlig;t den <b>TextStream<\/b> und gibt die zugrunde liegende Dateiressource frei. Nach dem Aufruf von <b>Close<\/b> kann das <b>TextStream<\/b>-Objekt nicht mehr verwendet werden. Es empfiehlt sich, nach dem Schlie&szlig;en auch die Objektvariable auf <b>Nothing<\/b> zu setzen:<\/p>\n<pre>ts.Close\r\n<span style=\"color:blue;\">Set<\/span> ts = Nothing<\/pre>\n<p>Vergisst man das Schlie&szlig;en, wird der Stream sp&auml;testens beim Aufr&auml;umen der Objektvariablen durch VBA geschlossen. Es ist aber guter Stil, Ressourcen explizit freizugeben, sobald sie nicht mehr ben&ouml;tigt werden.<\/p>\n<h2>Vergleich: TextStream versus Open\/Close<\/h2>\n<p>Im Artikel <b>VBA und Textdateien: Alles &uuml;ber Open, Close und Co.<\/b> (<b>www.vbentwickler.de\/454<\/b>) haben wir die klassischen VBA-Anweisungen <b>Open<\/b>, <b>Close<\/b>, <b>Print #<\/b> und <b>Line Input #<\/b> kennengelernt. Nun stellt sich die Frage: Wann greift man zu den klassischen Anweisungen und wann zum <b>TextStream<\/b>?<\/p>\n<p>Die klassischen Anweisungen ben&ouml;tigen keinen Verweis auf eine externe Bibliothek und funktionieren direkt in jedem VBA-Projekt. Au&szlig;erdem bieten sie mit <b>Write #<\/b> und <b>Input #<\/b> eine M&ouml;glichkeit, strukturierte Daten typsicher zu schreiben und wieder einzulesen.<\/p>\n<p>Das <b>TextStream<\/b>-Objekt punktet dagegen mit einer saubereren, objektorientierten Syntax. Statt mit Dateinummern zu hantieren, arbeiten wir mit einem Objekt und dessen Methoden.<\/p>\n<p>Au&szlig;erdem k&ouml;nnen wir mit <b>OpenTextFile<\/b> direkt Unicode-Dateien lesen und schreiben, was mit den klassischen Anweisungen nicht m&ouml;glich ist. Wenn wir ohnehin bereits das <b>FileSystemObject<\/b> f&uuml;r Dateioperationen verwenden, liegt es nahe, auch die Textdateioperationen dar&uuml;ber abzuwickeln.<\/p>\n<h2>Praxisbeispiel: CSV-Export und -Import<\/h2>\n<p>Zum Abschluss verkn&uuml;pfen wir die vorgestellten Methoden in einem praxisnahen Szenario. Wir exportieren den Inhalt einer Access-Tabelle in eine CSV-Datei und lesen diese anschlie&szlig;end wieder zeilenweise ein.<\/p>\n<p>Die Tabelle <b>tblArtikel<\/b> enth&auml;lt drei Felder: <b>ArtikelID<\/b> (Autowert), <b>Bezeichnung<\/b> (Text) und <b>Preis<\/b> (W&auml;hrung) &#8211; siehe Bild 1.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_02\/pic_479_001.png\" alt=\"Die Beispieltabelle tblArtikel\" width=\"424,6267\" height=\"228,8841\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Die Beispieltabelle tblArtikel<\/span><\/b><\/p>\n<p>Die Prozedur <b>CSVExport<\/b> &ouml;ffnet ein Recordset auf die Tabelle und schreibt zuerst eine Kopfzeile mit den Feldnamen. Dann durchl&auml;uft sie alle Daten&auml;tze und schreibt die Feldwerte durch Semikolon getrennt in jeweils eine Zeile (siehe Listing 1).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>CSVExport()\r\n    <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n    <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n    <span style=\"color:blue;\">Dim <\/span>objFSO<span style=\"color:blue;\"> As <\/span>Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Dim <\/span>objTextstream<span style=\"color:blue;\"> As <\/span>Scripting.TextStream\r\n    <span style=\"color:blue;\">Dim <\/span>strPfad<span style=\"color:blue;\"> As String<\/span>\r\n    strPfad = CurrentProject.Path & \"\\Artikel.csv\"\r\n    <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n    <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"tblArtikel\", _\r\n        dbOpenSnapshot)\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = objFSO.CreateTextFile(strPfad, <span style=\"color:blue;\">True<\/span>)\r\n    objTextstream.WriteLine \"ArtikelID;Bezeichnung;Preis\"\r\n    <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n        objTextstream.WriteLine rst!ArtikelID & \";\" & rst!Bezeichnung & \";\" & rst!Preis\r\n        rst.Move<span style=\"color:blue;\">Next<\/span>\r\n    <span style=\"color:blue;\">Loop<\/span>\r\n    objTextstream.Close\r\n    rst.Close\r\n    <span style=\"color:blue;\">Set<\/span> objTextstream = Nothing\r\n    <span style=\"color:blue;\">Set<\/span> rst = Nothing\r\n    <span style=\"color:blue;\">Set<\/span> objFSO = Nothing\r\n    <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Prozedur zum Exportieren von Tabellendaten in eine .csv-Datei<\/span><\/b><\/p>\n<p>Die Prozedur verwendet <b>CreateTextFile<\/b>, um die CSV-Datei neu anzulegen. Mit <b>WriteLine<\/b> schreiben wir zuerst die Kopfzeile und dann je Datensatz eine Zeile mit den durch Semikolon getrennten Feldwerten. Am Ende schlie&szlig;en wir sowohl den Stream als auch das Recordset.<\/p>\n<p>Das Ergebnis sehen wir in Bild 2.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_02\/pic_479_002.png\" alt=\"Die als .csv-Datei exportierten Artikeldaten\" width=\"499,6267\" height=\"324,7574\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Die als .csv-Datei exportierten Artikeldaten<\/span><\/b><\/p>\n<p>Hier k&ouml;nnte es noch ein Problem geben, n&auml;mlich wenn die zu speichernden Werte selbst Semikola enthalten. Diese w&uuml;rden als neues Feld interpretiert werden. Das k&ouml;nnen wir allerdings verhindern, indem wir die zu schreibenden Werte in Anf&uuml;hrungszeichen einfassen. Hier ist die korrigierte Zeile:<\/p>\n<pre>objTextstream.WriteLine \"\"\"\" & rst!ArtikelID _\r\n    & \"\"\";\"\"\" & rst!Bezeichnung & \"\"\";\"\"\" & rst!Preis & \"\"\"\"<\/pre>\n<h2>CSV-Datei wieder importieren<\/h2>\n<p>F&uuml;r den Import der CSV-Datei nutzen wir <b>OpenTextFile<\/b> mit dem Modus <b>ForReading<\/b>. Die Prozedur <b>CSVImport<\/b> &uuml;berspringt die Kopfzeile mit <b>SkipLine<\/b> und liest dann alle weiteren Zeilen mit <b>ReadLine<\/b> ein. Jede Zeile wird an den Semikolons aufgeteilt und die einzelnen Werte werden im Direktbereich ausgegeben (siehe Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>CSVImport()\r\n     <span style=\"color:blue;\">Dim <\/span>objFSO<span style=\"color:blue;\"> As <\/span>Scripting.FileSystemObject\r\n     <span style=\"color:blue;\">Dim <\/span>objTextstream<span style=\"color:blue;\"> As <\/span>Scripting.TextStream\r\n     <span style=\"color:blue;\">Dim <\/span>strPfad<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strZeile<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>arrFelder()<span style=\"color:blue;\"> As String<\/span>\r\n     strPfad = CurrentProject.Path & \"\\Artikel.csv\"\r\n     <span style=\"color:blue;\">Set<\/span> objFSO = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n     <span style=\"color:blue;\">Set<\/span> objTextstream = objFSO.OpenTextFile(strPfad, ForReading)\r\n     objTextstream.SkipLine\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> objTextstream.AtEndOfStream\r\n          strZeile = objTextstream.ReadLine\r\n          arrFelder = <span style=\"color:blue;\">Split<\/span>(strZeile, \";\")\r\n          <span style=\"color:blue;\">Debug.Print<\/span> \"ID: \" & arrFelder(0) & \", Bezeichnung: \" & arrFelder(1) & \", Preis: \" & arrFelder(2)\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     objTextstream.Close\r\n     <span style=\"color:blue;\">Set<\/span> objTextstream = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> objFSO = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Prozedur zum Einlesen einer CSV-Datei<\/span><\/b><\/p>\n<p>Hier sehen wir <b>SkipLine<\/b> in Aktion: Wir &uuml;berspringen die Kopfzeile, weil wir deren Inhalt nicht ben&ouml;tigen. Danach durchlaufen wir mit <b>Do While Not ts.AtEndOfStream<\/b> alle verbleibenden Zeilen. Jede Zeile wird mit der VBA-Funktion <b>Split<\/b> am Semikolon aufgeteilt und die Einzelwerte werden ausgegeben.<\/p>\n<p>Nat&uuml;rlich k&ouml;nnten wir statt der Ausgabe im Direktbereich die eingelesenen Daten auch in eine Tabelle schreiben, in ein Array sammeln oder anderweitig weiterverarbeiten.<\/p>\n<h2>Text an eine vorhandene Datei anh&auml;ngen<\/h2>\n<p>Ein h&auml;ufiger Anwendungsfall ist das Protokollieren von Vorg&auml;ngen in einer Logdatei.<\/p>\n<p>Dazu &ouml;ffnen wir die Datei im Modus <b>ForAppending<\/b>, damit der vorhandene Inhalt erhalten bleibt und die neuen Eintr&auml;ge am Ende angef&uuml;gt werden.<\/p>\n<p>Die folgende Prozedur schreibt einen Eintrag mit Zeitstempel in eine Logdatei:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>LogEintrag(strMeldung<span style=\"color:blue;\"> As String<\/span>)\r\n    <span style=\"color:blue;\">Dim <\/span>fso<span style=\"color:blue;\"> As <\/span>Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Dim <\/span>ts<span style=\"color:blue;\"> As <\/span>Scripting.TextStream\r\n    <span style=\"color:blue;\">Dim <\/span>strPfad<span style=\"color:blue;\"> As String<\/span>\r\n    strPfad = CurrentProject.Path & \"\\Protokoll.log\"\r\n    <span style=\"color:blue;\">Set<\/span> fso = <span style=\"color:blue;\">New<\/span> Scripting.FileSystemObject\r\n    <span style=\"color:blue;\">Set<\/span> ts = fso.OpenTextFile(strPfad, _\r\n    ForAppending, <span style=\"color:blue;\">True<\/span>)\r\n    ts.WriteLine Format(Now, \"yyyy-mm-dd hh:nn:ss\") _\r\n        & \" - \" & strMeldung\r\n    ts.Close\r\n    <span style=\"color:blue;\">Set<\/span> ts = Nothing\r\n    <span style=\"color:blue;\">Set<\/span> fso = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Beachte den dritten Parameter <b>True<\/b> bei <b>OpenTextFile<\/b>: Damit wird die Datei automatisch erstellt, falls sie noch nicht existiert.<\/p>\n<p>Beim ersten Aufruf wird die Logdatei also angelegt, bei allen weiteren Aufrufen werden die Eintr&auml;ge angeh&auml;ngt.<\/p>\n<p>Einen Aufruf dieser Prozedur k&ouml;nnten wir so gestalten:<\/p>\n<pre>LogEintrag \"Import abgeschlossen\"<\/pre>\n<p>Damit landet in der Logdatei eine Zeile wie die folgende:<\/p>\n<pre>2025-06-15 14:30:22 - Import abgeschlossen<\/pre>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Mit der <b>TextStream<\/b>-Klasse der Bibliothek <b>Microsoft Scripting Runtime<\/b> k&ouml;nnen wir Textdateien komfortabel erstellen, beschreiben und auslesen. Die drei Wege zum <b>TextStream<\/b> &#8211; <b>CreateTextFile<\/b>, <b>OpenTextFile<\/b> und <b>OpenAsTextStream<\/b> &#8211; bieten f&uuml;r jede Situation den passenden Einstieg.<\/p>\n<p>Die Methoden zum Lesen (<b>Read<\/b>, <b>ReadLine<\/b>, <b>ReadAll<\/b>), zum Schreiben (<b>Write<\/b>, <b>WriteLine<\/b>, <b>WriteBlankLines<\/b>) und zum Navigieren (<b>Skip<\/b>, <b>SkipLine<\/b>) decken alle typischen Anforderungen ab.<\/p>\n<p>In Kombination mit den Eigenschaften <b>AtEndOfStream<\/b>, <b>AtEndOfLine<\/b>, <b>Line<\/b> und <b>Column<\/b> haben wir jederzeit volle Kontrolle &uuml;ber die aktuelle Position im Stream.<\/p>\n<p>Gegen&uuml;ber den klassischen VBA-Anweisungen <b>Open<\/b>, <b>Close<\/b>, <b>Print #<\/b> und <b>Line Input #<\/b> bietet der <b>TextStream<\/b> eine objektorientierte Alternative, die besonders dann naheliegt, wenn wir ohnehin mit dem <b>FileSystemObject<\/b> arbeiten.<\/p>\n<p>F&uuml;r Szenarien, in denen wir auch Unicode-Dateien verarbeiten m&uuml;ssen, ist der <b>TextStream<\/b> sogar die einzige Option innerhalb der Scripting-Welt.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>TextdateienUndStreamMitFileSytemObject.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/4B5DC863-3E1B-4C5B-9C76-0BAFD2297970\/vbe_479.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im Artikel Dateimanagement mit dem FileSystemObject (www.vbentwickler.de\/478) haben wir die Klasse FileSystemObject kennengelernt und damit Laufwerke, Verzeichnisse und Dateien verwaltet. Dabei haben wir drei Methoden und Funktionen erw&auml;hnt, die wir in einem separaten Artikel behandeln wollten: CreateTextFile, OpenTextFile und OpenAsTextStream. Diese drei Elemente sind das Tor zur Klasse TextStream, mit der wir Textdateien erstellen, beschreiben und auslesen k&ouml;nnen. In diesem Artikel schauen wir uns zuerst an, wie wir TextStream-Objekte erzeugen, und gehen dann alle Eigenschaften und Methoden der Klasse durch. Abschlie&szlig;end bauen wir ein praxisnahes Beispiel, in dem wir eine CSV-Datei erzeugen und wieder einlesen.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[66022026,662026,44000023,44000025,44000028],"tags":[],"yst_prominent_words":[],"class_list":["post-55000479","post","type-post","status-publish","format-standard","hentry","category-66022026","category-662026","category-PowerApps","category-VBAProgrammierung","category-Word_programmieren"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000479"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000479\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000479"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}