CodeGym /Kurse /C# SELF /Die Klasse FileStream

Die Klasse FileStream

C# SELF
Level 35 , Lektion 3
Verfügbar

1. Die Klasse FileStream: Arbeiten in Stücken

Du kannst dir FileStream wie ein Wasserrohr vorstellen, das direkt mit deiner Datei auf der Festplatte verbunden ist. Durch dieses Rohr kannst du kontrolliert Daten durchlassen: Bytes in die Datei schicken (schreiben) oder Bytes aus der Datei holen (lesen). Im Gegensatz zu High-Level-Methoden, die dir einfach ein „Glas Wasser“ geben, gibt dir FileStream direkten Zugang zum „Wasserhahn“, sodass du den Datenfluss (Bytes) nach Bedarf regulieren kannst.

FileStream arbeitet auf Byte-Ebene. Das heißt, es ist ihm egal, ob es sich um Text oder ein Bild handelt; für ihn ist alles einfach eine Abfolge von Bytes. Wenn du mit Textdateien über FileStream arbeitest, musst du selbst die Strings in Bytes umwandeln (mit einer Kodierung, z.B. UTF-8) beim Schreiben und zurück beim Lesen.

Wann genau brauchst du FileStream?

  • Arbeiten mit sehr großen Dateien: Wenn die Datei so riesig ist, dass du sie nicht komplett in den RAM laden kannst oder willst (z.B. Gigabyte-Logs, Videodateien). FileStream erlaubt dir, Daten stückweise (Chunks) zu lesen oder zu schreiben, was den Speicher effizienter nutzt.
  • Binärdaten: Wenn du mit Dateien arbeitest, die kein normaler Text sind (Bilder, Audio, Video, serialisierte Objekte, Datenbankdateien), ist FileStream das Hauptwerkzeug, weil er dir direkten Zugriff auf die Bytes gibt.
  • Feinsteuerung der Zugriffsmodi: Du willst eine Datei zum Lesen *und* Schreiben gleichzeitig öffnen? Oder so öffnen, dass andere Programme keinen Zugriff bekommen? Oder im Gegenteil, dass mehrere Prozesse gleichzeitig lesen können? FileStream gibt dir die volle Kontrolle über diese Modi.
  • Asynchrone Operationen: In modernen High-Performance-Apps (z.B. Webservern) ist es superwichtig, dass Dateioperationen den Main-Thread nicht blockieren. FileStream unterstützt asynchrone Methoden (ReadAsync, WriteAsync), damit deine App reaktionsschnell bleibt.
  • Teilweises Lesen/Schreiben oder Random Access: Wenn du Daten ab einer bestimmten Stelle in der Datei lesen willst (z.B. ab dem 500-sten Byte) oder mitten in die Datei schreiben willst, kannst du mit FileStream die Position des Dateizeigers steuern.

Typischer Anfängerfehler: FileStream für die einfachsten Aufgaben zu benutzen, wie das Schreiben einer einzigen Textzeile in eine kleine Datei. Dafür ist er Overkill. FileStream ist ein Tool für spezielle und komplexere Szenarien.

2. Erstellen und Öffnen von FileStream

Um mit einer Datei über FileStream zu arbeiten, musst du eine Instanz davon erstellen. Das machst du mit dem Konstruktor, der ein paar wichtige Parameter nimmt, die bestimmen, wie du mit der Datei interagieren willst.

using System;
using System.IO;   // Unbedingt nötig für FileStream
using System.Text; // Für Encodings (Strings in Bytes umwandeln und zurück)

Beispiel für einfaches Lesen einer Textdatei mit FileStream:

Erstmal erstellen wir eine Datei, damit wir was zum Lesen haben.

// Wir erstellen eine einfache Textdatei zum Demonstrieren
string path = "example_filestream.txt";

// Öffne den Stream zum Lesen.
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

// Erstelle einen Buffer (Byte-Array) zum Zwischenspeichern der gelesenen Daten.
byte[] buffer = new byte[fs.Length];

// Lese Bytes aus dem Stream in den Buffer.
int bytesRead = fs.Read(buffer, 0, buffer.Length);

// Wandle die gelesenen Bytes zurück in einen String, mit dem gewünschten Encoding (z.B. UTF-8).
string content = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine("Aus Datei gelesen mit FileStream: " + content);

fs.Close(); //Stream schließen

Wichtige Parameter

  • FileMode: Wie soll die Datei geöffnet werden (Open, Create, Append OpenOrCreate usw.)
  • FileAccess: Was willst du mit der Datei machen (Read, Write, ReadWrite)
  • FileShare: Dürfen andere Prozesse die Datei gleichzeitig benutzen (für einfache Aufgaben meist egal)

Typischer Fehler: Wenn du vergisst, den FileStream zu schließen, kann die Datei „gelockt“ bleiben – dann kannst du sie später nicht löschen oder nochmal öffnen!

3. Konstruktor-Parameter von FileStream

Der Konstruktor von FileStream lässt dich sehr genau einstellen, wie du die Datei öffnen willst. Hier die wichtigsten Parameter:

new FileStream(
    string path,           // Pfad zur Datei (absolut oder relativ)
    FileMode mode,         // Wie öffnen (erstellen, öffnen, überschreiben usw.)
    FileAccess access,     // Welche Rechte (nur lesen, nur schreiben, lesen+schreiben)
    FileShare share        // Wie andere Prozesse auf die Datei zugreifen dürfen (optional)
);

Lass uns die Werte der Enums FileMode und FileAccess anschauen:

FileMode (Datei-Öffnungsmodus):

Bestimmt, wie das Betriebssystem mit der Datei beim Öffnen umgehen soll.

  • FileMode.Open: Öffnet eine existierende Datei. Wenn die Datei am angegebenen Pfad nicht gefunden wird, gibt’s eine FileNotFoundException.
  • FileMode.Create: Erstellt eine neue Datei. Wenn schon eine Datei mit dem Namen existiert, wird sie komplett überschrieben (Inhalt wird gelöscht).

FileAccess (Dateizugriffsrechte):

Legt fest, welche Operationen dein Programm mit der geöffneten Datei machen darf.

  • FileAccess.Read: Datei ist nur zum Lesen geöffnet. Du kannst nichts reinschreiben.
  • FileAccess.Write: Datei ist nur zum Schreiben geöffnet. Du kannst nichts daraus lesen.
  • FileAccess.ReadWrite: Datei ist zum Lesen und Schreiben offen. Das ist am flexibelsten, aber auch etwas tricky, weil du die Position im Stream selbst verwalten musst.

FileShare (Sharing-Modus):

Legt fest, wie andere Prozesse dieselbe Datei öffnen dürfen, während sie von deinem Programm offen ist. Wichtig, um Locks zu vermeiden.

  • FileShare.Read: Andere Prozesse dürfen die Datei lesen, solange dein FileStream sie offen hat, aber nicht schreiben.
  • FileShare.Write: Andere Prozesse dürfen schreiben, solange dein FileStream sie offen hat, aber nicht lesen.
  • FileShare.ReadWrite: Andere Prozesse dürfen lesen und schreiben. Das ist der lockerste Modus, kann aber zu Konflikten führen, wenn mehrere Programme gleichzeitig die Datei ändern.

Mehr zu den Betriebsmodi gibt’s in der nächsten Vorlesung.

4. Lesen und Schreiben von Daten mit FileStream

Mit FileStream hantierst du mit Bytes. Das heißt, für Textdaten musst du selbst zwischen Strings und Byte-Arrays konvertieren, mit den Encoding-Klassen (z.B. System.Text.Encoding.UTF8).

Daten in eine Datei schreiben mit FileStream

Beim Schreiben wandelst du deine Daten (z.B. einen String) in ein Byte-Array um und schreibst diese Bytes in den Stream.

string outputPath = "user_data.txt";
string userName = "Ivan Petrov";

// String in Byte-Array umwandeln, mit UTF-8 Encoding
byte[] userNameBytes = Encoding.UTF8.GetBytes(userName);

// FileStream zum Schreiben öffnen.
FileStream fsWrite = new FileStream(outputPath, FileMode.Create, FileAccess.Write);

// Byte-Array in den Stream schreiben.
fsWrite.Write(userNameBytes, 0, userNameBytes.Length);
Console.WriteLine($"Name '{userName}' wurde erfolgreich in die Datei '{outputPath}' geschrieben.");

fsWrite.Close(); //Stream schließen

Daten aus einer Datei lesen mit FileStream

Beim Lesen holst du Bytes aus dem Stream in einen Buffer und wandelst sie dann zurück in das gewünschte Format (z.B. String).

string inputPath = "user_data.txt";

// FileStream zum Lesen öffnen
FileStream fsRead = new FileStream(inputPath, FileMode.Open, FileAccess.Read);

// Buffer in Dateigröße zum Einlesen aller Bytes erstellen.
byte[] buffer = new byte[fsRead.Length];

// Bytes aus dem Stream in den Buffer lesen. 
int bytesRead = fsRead.Read(buffer, 0, buffer.Length);

// Gelesene Bytes in einen String umwandeln.
string loadedUserName = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Name aus Datei (mit FileStream): {loadedUserName}");

fsRead.Close(); //Stream schließen

5. Arbeiten mit großen Dateien

Der Hauptvorteil von FileStream gegenüber File.ReadAll...-Methoden ist, dass du die Datei stückweise lesen und schreiben kannst – das ist superwichtig für große Dateien, die nicht komplett in den RAM passen.

Große Dateien stückweise lesen

Wenn du eine Datei stückweise liest, benutzt du einen Buffer (Byte-Array) mit fester Größe und liest so lange Daten rein, bis das Ende der Datei erreicht ist.

string bigFilePath = "bigfile.bin";     //große Datei 
int bufferSize = 4096;                  // Buffer-Größe, z.B. 4 KB
byte[] buffer = new byte[bufferSize];   // Buffer erstellen
int bytesRead;                          // Tatsächlich gelesene Bytes
long totalBytesRead = 0;                // Insgesamt gelesene Bytes

// Stream öffnen 
FileStream fs = new FileStream(bigFilePath, FileMode.Open, FileAccess.Read);
int chunkNumber = 1;

// Schleife läuft, solange fs.Read() eine positive Byte-Anzahl zurückgibt
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
    totalBytesRead += bytesRead;
    Console.WriteLine($"Chunk {chunkNumber++}: {bytesRead} Bytes gelesen. Insgesamt: {totalBytesRead} Bytes.");

    // !!! Hier kannst du die gelesenen "Stücke" verarbeiten !!!
   
}

fs.Close(); //Stream schließen

Wichtiger Hinweis: Die Methode fs.Read(buffer, offset, count) garantiert nicht, dass sie den ganzen buffer füllt. Sie gibt die tatsächlich gelesene Byte-Anzahl zurück, die kleiner als count sein kann (vor allem am Ende der Datei). Benutze immer den Rückgabewert bytesRead für die korrekte Verarbeitung.

Große Daten stückweise schreiben

Genauso wie beim Lesen kannst du große Datenmengen stückweise in eine Datei schreiben.

string bigOutputPath = "big_output.txt";
string longText = new string('A', 1_000_000); // String mit einer Million 'A's
byte[] longTextBytes = Encoding.UTF8.GetBytes(longText);
int writeBufferSize = 1024; // Immer 1 KB auf einmal schreiben

// FileMode.Create: Datei erstellen
FileStream fsWrite = new FileStream(bigOutputPath, FileMode.Create, FileAccess.Write);

for (int i = 0; i < longTextBytes.Length; i += writeBufferSize)
{
    // Berechne, wie viele Bytes in dieser Runde noch zu schreiben sind
    int bytesToWrite = Math.Min(writeBufferSize, longTextBytes.Length - i);

    // Schreibe einen Teil der Daten aus longTextBytes, ab Offset 'i'
    fsWrite.Write(longTextBytes, i, bytesToWrite);
    Console.WriteLine($"{bytesToWrite} Bytes geschrieben");
}

fsWrite.Close(); //Stream schließen

6. Unerwartete Stolperfallen und typische Fehler

Auch mit so einem mächtigen Tool wie FileStream gibt’s ein paar Dinge, die du beachten solltest:

Buffering und Flush(): Wie schon erwähnt, können Daten im Speicher-Buffer bleiben und nicht direkt auf die Festplatte geschrieben werden. Wenn du kein using benutzt und Close()/Dispose() nicht aufrufst, sondern das Programm einfach beendest, können Daten verloren gehen. Ruf immer Flush() auf (oder verlasse dich auf using/Close()), um sicherzugehen, dass alles geschrieben wird.

Exception-Handling: IO-Fehler (z.B. IOException beim Schreiben auf eine volle Festplatte, UnauthorizedAccessException wegen fehlender Rechte, FileNotFoundException beim Versuch, eine nicht existierende Datei im Open-Modus zu öffnen) sind ganz normal. Pack deine FileStream-Operationen immer in try-catch-Blöcke.

Encodings: Wenn du mit Textdaten über FileStream arbeitest (der mit Bytes arbeitet), bist du komplett selbst für das richtige Encoding verantwortlich (Encoding.UTF8, Encoding.ASCII, Encoding.Unicode usw.) beim Umwandeln von Strings in Bytes und zurück. Falsches Encoding führt zu „Krakelzeichen“.

Positionsmanagement: Wenn du Seek() benutzt, pass auf die Parameter offset und origin auf, damit du nicht außerhalb der Datei oder an eine unerwartete Stelle springst.

Datei-Locks (FileShare): Wenn du eine Datei mit FileShare.None öffnest, können andere Prozesse nicht darauf zugreifen. Das kann ein Problem sein, wenn ein anderes Programm (oder sogar ein anderer Thread in deinem Programm) dieselbe Datei benutzen will. Wähle immer den passenden FileShare-Modus.

2
Aufgabe
C# SELF, Level 35, Lektion 3
Gesperrt
Lesen einer Textdatei mit FileStream
Lesen einer Textdatei mit FileStream
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION