CodeGym /Kurse /C# SELF /Beschädigte Dateien und Kodierungen

Beschädigte Dateien und Kodierungen

C# SELF
Level 38 , Lektion 3
Verfügbar

1. Einführung

Es ist Zeit, unangenehme Situationen zu besprechen, wenn die Arbeit mit Dateien zu einer Begegnung mit (manchmal ziemlich rätselhaften) Fehlern wird. Wenn ihr jemals auf Fehler wie System.Text.DecoderFallbackException gestoßen seid, kennt ihr das Thema aus erster Hand!

In dieser Vorlesung behandeln wir:

  • Welche Fehler im Zusammenhang mit Kodierungen in .NET auftreten können;
  • Wie sich beschädigte oder inkorrekte Dateien zeigen;
  • Praktische Beispiele zum Abfangen und Behandeln solcher Fehler;
  • Worauf man achten sollte beim Arbeiten mit fremden Dateien (oder mit „alten guten“ Dateien von einer alten Platte).

Also: wenn ASCII zu simpel war und Unicode zu schlau, gibt es manchmal Dateien, die niemand lesen kann. Genau hier tauchen Ausnahmen auf.

Warum passiert das?

Wenn ihr eine Datei mit StreamReader öffnet und eine Kodierung angebt (oder die Standardkodierung verwendet), geht .NET davon aus, dass alle Bytes der Datei korrekt in Zeichen umgewandelt werden können. Wenn die Datei jedoch Bytes enthält, die in dieser Kodierung keinem Zeichen entsprechen, entsteht ein Dekodierungsfehler.

2. Ausnahmen beim Lesen von Dateien mit falscher Kodierung

Die häufigste Ausnahme — DecoderFallbackException

Diese Ausnahme wirft .NET, wenn eine Bytefolge nicht einem Zeichen in der erwarteten Kodierung zugeordnet werden kann.

Ein einfaches Beispiel, damit es klar wird:


// Angenommen, eine alte Datei ist in Windows-1251 (Kyrillisch)
string win1251File = "win1251_test.txt";
File.WriteAllText(win1251File, "Privet, mir!", Encoding.GetEncoding("windows-1251"));

try
{
    // Versuchen wir, diese Datei als UTF-8 zu lesen
    using var reader = new StreamReader(win1251File, Encoding.UTF8);
    string content = reader.ReadToEnd();
    Console.WriteLine(content); // ...und es werden Kauderwelsch-Zeichen ausgegeben (oder eine Ausnahme geworfen)
}
catch (DecoderFallbackException ex)
{
    Console.WriteLine("Dekodierungsfehler: " + ex.Message);
}

In den meisten Fällen, wenn man eine in Windows-1251 gespeicherte Datei als UTF-8 liest, erhält man eine Folge von „Kauderwelsch“-Zeichen. Standardmäßig wirft StreamReader in solchen Situationen keine Ausnahme, sondern setzt anstelle unverständlicher Bytes das Ersatzzeichen "�". Wenn man jedoch die Kodierung explizit mit hartem DecoderExceptionFallback konfiguriert oder besonders „unverdauliche“ Bytes im Stream sind, wird eine DecoderFallbackException ausgelöst.

DecoderFallbackException im Detail

  • Wann sie auftritt: beim Versuch, eine Bytefolge zu lesen, die in der aktuellen Kodierung nicht in Zeichen umgewandelt werden kann.
  • Was zu tun ist: die Datei mit der richtigen Kodierung lesen! Wenn ihr die Kodierung nicht kennt, versucht sie zu erraten (manchmal hilft das BOM oder der Dateiname) oder klärt es mit der Person, die die Datei erzeugt hat.

3. Beispiel mit expliziter Dateibeschädigung

Jetzt machen wir es schwieriger. Stellt euch vor, die Datei ist beschädigt: in der Bytefolge gibt es abgeschnittene, unvollständige Zeichen. Das passiert bei abgebrochenem Schreiben, Netzwerkfehlern, misslungenen Konvertierungen oder bei „physischem“ Zerlegen der Datei... im wörtlichen Sinn: die Datei wurde irgendwo aufgeschnitten.

Wir erstellen eine „kaputte“ Datei


// Schreiben wir eine gültige Zeichenfolge in UTF-8
byte[] valid = Encoding.UTF8.GetBytes("Privet, mir!");
// Jetzt erstellen wir ein falsches Byte-Array (schneiden einen Teil eines Zeichens ab)
byte[] corrupted = new byte[valid.Length - 1];
Array.Copy(valid, corrupted, valid.Length - 1); // Letztes Byte abgeschnitten
        
// Datei speichern
File.WriteAllBytes("corrupted.txt", corrupted);

try
{
    using var reader = new StreamReader("corrupted.txt", Encoding.UTF8);
    string s = reader.ReadToEnd();
    Console.WriteLine("Gelesener Text: " + s);
}
catch (DecoderFallbackException ex)
{
    Console.WriteLine("Datei ist beschädigt! " + ex.Message);
}

Ergebnis: .NET kann das letzte Zeichen nicht korrekt zusammensetzen. Standardmäßig ersetzt es das durch das spezielle Zeichen "�" (oder "?"), oder wenn die Kodierung so konfiguriert ist, wird eine DecoderFallbackException geworfen.

4. Fallback-Strategien: kann man Ausnahmen vermeiden?

Manchmal möchte man bei einem unverständlichen Zeichen lieber keine Ausnahme werfen, sondern es z.B. durch „?“ oder etwas anderes ersetzen. Dafür gibt es in .NET sogenannte fallback-Strategien.

Beispiel: Fehlerzeichen ersetzen statt Ausnahme


// Ein Array mit ungültiger Sequenz für UTF-8
byte[] data = { 0xD0, 0x9F, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 0xB5, 0xD1, 0x82, 0xD1 }; // Letztes Byte abgeschnitten
File.WriteAllBytes("broken_utf8.txt", data);

// Fallback-Strategie: problematische Zeichen durch Fragezeichen ersetzen
var encodingWithFallback = Encoding.GetEncoding(
    "UTF-8",
    new EncoderReplacementFallback("?"),
    new DecoderReplacementFallback("?")
);

using var reader = new StreamReader("broken_utf8.txt", encodingWithFallback);
string s = reader.ReadToEnd();
Console.WriteLine("Text (Fehler ersetzt): " + s);

Ergebnis: der Text wird gelesen, unbekannte Zeichen werden durch "?" ersetzt. So verhindert man einen Programmabsturz, bekommt aber keinen völlig „authentischen“ Text.

5. Probleme mit BOM und Inkompatibilitäten

Zur Erinnerung: BOM ist das Byte Order Mark, eine spezielle Bytefolge am Anfang einer Datei, die sagt „Hallo, ich bin diese Kodierung!“.

Wann BOM Kopfzerbrechen macht

  • Wenn eine Datei ein BOM enthält, aber die Anwendung nicht damit umgehen kann, sieht das erste Zeichen seltsam aus (z.B. "" oder ein unsichtbares Zeichen).
  • Manchmal führt das Fehlen eines BOM zu einer falschen Erkennung der Kodierung.

Ausnahmen im Zusammenhang mit BOM

Normalerweise kann C# BOM beim Lesen „verarbeiten“, aber wenn man die falsche Kodierung angibt oder das BOM manuell entfernt, riskiert man:

  • Ein unerwartetes Zeichen am Anfang (z.B. "�");
  • Eine Ausnahme, wenn die Kodierung so konfiguriert ist, dass das BOM als ungültige Bytefolge angesehen wird.

Praktischer Tipp: Gebt beim Lesen/Schreiben immer explizit die Kodierung an, wenn ihr auf deren Typ angewiesen seid.

6. Weitere interessante Ausnahmen und Szenarien

Falsche Kodierung beim Schreiben

Wenn ihr versucht, eine Zeichenfolge zu schreiben, die Zeichen enthält, die die gewählte Kodierung nicht unterstützt. Zum Beispiel: versucht, das Emoji „😊“ in eine Datei mit Encoding.ASCII zu speichern:


try
{
    using var writer = new StreamWriter("ascii.txt", false, Encoding.ASCII);
    writer.WriteLine("Eto test 😊");
}
catch (EncoderFallbackException ex)
{
    Console.WriteLine("Kodierungsfehler: " + ex.Message);
}

Ergebnis: ihr bekommt entweder eine EncoderFallbackException oder das Zeichen wird durch "?" ersetzt — abhängig von der gewählten Fallback-Strategie.

Probleme bei Konvertierung zwischen Kodierungen (Datenverlust)

Bei Konvertierung kann man ungewollt Daten verlieren, wenn die Zielkodierung nicht alle Zeichen der Quelle unterstützt (z.B. Konvertierung von UTF-8 nach Windows-1251, wenn die Datei japanischen Text enthält).

Beschädigung durch Festplatte, Netzwerk oder „manuelles Editieren“

Wenn zufällige oder beschädigte Bytes in die Datei gelangen (z.B. nach einem Festplattenfehler oder durch Bearbeitung einer Binärdatei mit einem Texteditor), führt das Lesen solcher Dateien oft zu Dekodierungs-Ausnahmen.

7. Wie fängt und behandelt man Fehler praktisch?

Da Fehler in vielen Phasen der Dateiverarbeitung auftreten können, empfiehlt sich:

  • Verwendet try-catch-Blöcke, um Ausnahmen abzufangen — vor allem DecoderFallbackException und EncoderFallbackException.
  • Sagt dem Benutzer ehrlich Bescheid: wenn die Datei beschädigt ist oder die Kodierung nicht stimmt — das ist besser, als merkwürdigen Text anzuzeigen.
  • Automatisiert, wo möglich, die Erkennung der Kodierung (z.B. per BOM oder mit Bibliotheken wie Ude), gebt dem Benutzer aber immer die Möglichkeit, die Kodierung selbst auszuwählen, falls die Erkennung fehlschlägt.

Typische Code-Struktur:


try
{
    using var reader = new StreamReader("file.txt", Encoding.GetEncoding("windows-1251"));
    string s = reader.ReadToEnd();
    Console.WriteLine(s);
}
catch (DecoderFallbackException ex)
{
    Console.WriteLine($"Die Datei konnte nicht gelesen werden: {ex.Message}");
    // Man kann dem Nutzer vorschlagen, eine andere Kodierung zu versuchen
}
catch (IOException ex)
{
    Console.WriteLine($"Eingabe-/Ausgabefehler: {ex.Message}");
}

8. Ein paar typische Stolperfallen

Versuch, eine UTF-8-Datei als Windows-1251 zu lesen: im besten Fall seht ihr Kauderwelsch, im schlimmsten Fall eine Ausnahme (wenn die Kodierung so konfiguriert ist).

Schreiben in ASCII einer Datei mit russischem Text: alles, was nicht zum englischen Alphabet gehört, wird durch "?" ersetzt oder löst eine EncoderFallbackException aus.

Lesen einer Datei ohne BOM als UTF-8, obwohl sie UTF-16 ist: ihr bekommt Kauderwelsch oder könnt die Datei gar nicht korrekt lesen.

Dateien ohne explizite Kodierung aus unsicheren Quellen: seid vorsichtig: nur weil eine Datei „ohne Fehler“ geöffnet wird, heißt das nicht, dass der Inhalt korrekt ist.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION