1. Einführung: warum Dateien gern „rumzicken“?
Es kommt vor, dass ein Dokument geöffnet wird — und plötzlich meldet das System: "Datei nicht gefunden". Oder ein Speicherversuch endet mit: "Zugriff verweigert". Genau das sind Fälle, in denen mit Dateien etwas Merkwürdiges passiert. Wenn wir bei Encodings auf „mojibake“ stoßen, dann sind die Probleme hier eher mit dem Dateisystem selbst verbunden.
Man kann sich das Dateisystem wie eine große Bibliothek vorstellen, und die Anwendung ist der Bibliothekar. Und wenn sie eine Datei anfragt, gibt es verschiedene Antworten:
- Das Buch (also die Datei) fehlt — es existiert einfach nicht.
- Der Bereich der Bibliothek, wo das Buch sein sollte, existiert auch nicht — das Verzeichnis fehlt.
- Das Buch ist „verschlossen“ oder verliehen — die Datei wird von einem anderen Prozess benutzt oder es fehlen Zugriffsrechte.
- Das Regal ist voll — nicht genug Platz auf der Festplatte, um eine neue Datei anzulegen.
- Oder z.B. der Versuch, ein Buch in die DVD-Rückgabe zu legen — also eine nicht unterstützte Operation.
In C# drücken sich all diese Situationen über Ausnahmen aus. Die Aufgabe des Entwicklers ist nicht einfach zuzusehen, wie das Programm „abstürzt“, sondern mögliche Probleme vorherzusehen und sie sauber zu behandeln. Niemand will, dass der Benutzer eine rätselhafte Fehlermeldung mit einem Haufen unverständlichen Textes sieht.
Wir kennen bereits die Konstruktion try-catch. Das ist unser Rettungsring, mit dem man die Ausnahme „fangen“ und Maßnahmen ergreifen kann, statt das Programm abstürzen zu lassen.
// Das ist unser alter Bekannter, eine Erinnerung aus Vorlesung 57
try
{
// Hier schreiben wir Code, der einen Fehler auslösen kann
// Zum Beispiel ein Versuch, eine Datei zu lesen
}
catch (Exception ex) // Fängt jede Ausnahme
{
// Hier behandeln wir den Fehler
Console.WriteLine($"Ups, ein Fehler ist passiert: {ex.Message}");
}
Heute gehen wir tiefer auf dateispezifische Ausnahmen ein. Das hilft uns, robusteren Code zu schreiben, der mit dem Dateisystem „kommunizieren“ kann, selbst wenn dieses sich mal zickig verhält.
Ausnahmen — das sind keine Bugs, das sind Notrufe!
Wichtig zu verstehen: eine Ausnahme ist nicht immer ein Fehler in deinem Code. Oft ist es ein Signal, dass in der externen Umgebung, mit der dein Code interagiert, etwas schiefgelaufen ist. Das Dateisystem ist ein typisches Beispiel für so eine externe Umgebung. Du kannst absolut korrekten Code zum Lesen einer Datei schreiben, aber wenn der Benutzer die Datei gelöscht hat, bevor dein Programm sie gelesen hat, bekommst du eine Ausnahme. Und das ist völlig normal! Deine Aufgabe als Entwickler ist es, das Programm darauf vorzubereiten.
Schauen wir uns die häufigsten „Notrufe“ an, die beim Arbeiten mit Dateien auftreten können.
2. FileNotFoundException: die Datei war nie da
Das ist vermutlich die häufigste Ausnahme beim Umgang mit Dateien. Sie tritt auf, wenn du versuchst, eine Datei zu öffnen, zu lesen oder sonstwie zu bearbeiten, die unter dem angegebenen Pfad nicht existiert.
Alltagsbeispiel: Du bittest einen Freund, dir das Buch "Programmierung mit C# 14 für Dummies" aus seiner Bibliothek zu holen, und er sagt: "So ein Buch habe ich nicht". Genauso kann dein Programm das Betriebssystem fragen: "Gib mir die Datei settings.txt", und das System antwortet: "Tut mir leid, die gibt es nicht".
Schreiben wir Code, der die Datei abracadabra.txt für unsere Task-Manager-Anwendung liest. Wenn die Datei fehlt, sollten wir den Benutzer darüber informieren, statt einfach abzustürzen.
try
{
using var reader = new StreamReader("abracadabra.txt");
Console.WriteLine(reader.ReadToEnd());
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Datei nicht gefunden: " + ex.FileName);
}
Im Alltag ist das wie an der Bushaltestelle: der Bus kommt nicht — und das Ersatzfahrzeug auch nicht. Traurig.
Merke: Oft geht diese Ausnahme mit einem falschen Pfad einher (z.B. du hast vergessen, dass du aus einem anderen Verzeichnis arbeitest).
3. DirectoryNotFoundException: Verzeichnisse sind einfach verschwunden
Diese Ausnahme ähnelt sehr der FileNotFoundException, bezieht sich aber nicht auf die Datei selbst, sondern auf das Verzeichnis, in dem die Datei liegen sollte. Wenn du einen Pfad wie "C:\MyDocuments\MyProject\Data\report.txt" angibst und das Verzeichnis Data nicht existiert, bekommst du eine DirectoryNotFoundException.
Wenn unsere App Einstellungen in einem Unterordner data speichern wollte, z.B. "./data/app_settings.txt", und dieser Ordner nicht existiert, würde beim Schreiben oder Lesen die Ausnahme auftreten.
DirectoryNotFoundException kann separat gefangen werden, genau wie FileNotFoundException, oder als Teil der allgemeineren IOException auftreten.
try
{
using var writer = new StreamWriter(@"C:\very\strange\path\file.txt");
writer.WriteLine("Hello world");
}
catch (DirectoryNotFoundException ex)
{
Console.WriteLine("Verzeichnis nicht gefunden!");
}
Häufiger Fehler: Ein Ordner kann jederzeit gelöscht werden (z.B. temporäre Dateien), oder dein Schreibpfad ist falsch konfiguriert.
4. UnauthorizedAccessException: Zutritt verboten!
Stell dir vor, du willst ein Buch ins Regal legen, und dort hängt ein Schild "Zugriff nur für Mitarbeiter". Das ist genau UnauthorizedAccessException! Sie tritt auf, wenn dein Programm nicht die nötigen Rechte für die Datei oder das Verzeichnis hat. Gründe können sein:
- Unzureichende Benutzerrechte: Du versuchst, in einen Ordner zu schreiben, in den nur Administratoren schreiben dürfen (z.B. C:\Windows).
- Datei ist schreibgeschützt: Du versuchst, eine Datei zu ändern, die das Attribut "ReadOnly" hat.
- Datei ist system- oder versteckt: Und hat spezielle Einschränkungen.
Das ist sehr verbreitet in Unternehmensumgebungen oder wenn Nutzer Programme in geschützte Verzeichnisse installieren.
Probieren wir, eine Datei in einem Systemordner zu schreiben, in den ein normaler Benutzer keinen Zugriff hat. (Vorsicht: solchen Code sollte man mit Bedacht ausführen, damit man das System nicht zumüllt oder Rechteprobleme verursacht.)
try
{
using var writer = new StreamWriter("/system/settings.conf");
writer.WriteLine("Alle Macht den Studenten!");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("Kein Zugriff auf die Datei oder das Verzeichnis!");
}
Wenn du diesen Code ohne Administratorrechte ausführst, wirst du wahrscheinlich die Meldung "Zugriff auf diesen Ordner verweigert" sehen. Mit Administratorrechten wird die Datei vermutlich erstellt. Das Beispiel zeigt, wie wichtig es ist, UnauthorizedAccessException zu behandeln, damit der Nutzer versteht, warum die Operation fehlgeschlagen ist.
5. IOException: der universelle "ups" des Dateisystems
IOException ist die allgemeinste Ausnahme im Bereich Ein-/Ausgabe. Sie wird geworfen, wenn ein Problem mit dem I/O-Gerät oder dem Dateisystem auftritt, das nicht durch speziellere Ausnahmen wie FileNotFoundException oder UnauthorizedAccessException abgedeckt ist.
Typische Szenarien für IOException:
- Die Datei wird bereits von einer anderen Anwendung verwendet: Zum Beispiel versuchst du, eine Datei zu löschen, die in Notepad oder einem anderen Prozess geöffnet ist.
- Die Festplatte ist voll: Nicht genug Platz, um die Datei zu schreiben.
- Beschädigte Datei oder Dateisystem: Selten, aber möglich.
- Netzwerkprobleme: Wenn die Datei auf einem Netzlaufwerk liegt und die Verbindung abbricht.
- Datei- oder Verzeichnispfad ist zu lang. (Das kann auch PathTooLongException sein, wird aber manchmal als IOException auftreten.)
IOException ist sozusagen ein "Universalwerkzeug" für viele Probleme. Wenn du IOException fängst, lohnt sich ein Blick auf die Eigenschaft Message, um genauere Hinweise zu bekommen, was schiefgelaufen ist.
try
{
using var file = new FileStream("busyfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.None);
// Datei wird von Kräften offen gehalten
// Gleichzeitig an anderer Stelle:
using var writer = new StreamWriter("busyfile.txt");
writer.WriteLine("Schreibversuch...");
}
catch (IOException ex)
{
Console.WriteLine("Ein-/Ausgabe-Fehler: " + ex.Message);
}
Wichtiger Punkt: IOException ist die Basisklasse für viele andere „Datei“-Ausnahmen.
6. Andere, aber nicht weniger wichtige „Nervereien“
PathTooLongException
Das ist seltener, kommt aber vor: Dein Pfad (oder Dateiname/Verzeichnisname) ist zu lang für das Betriebssystem. Wenn du z.B. den Inhalt von "Krieg und Frieden" in den Dateinamen stopfst, wird Windows dir das nicht verzeihen.
Historisch hat Windows eine Grenze von 260 Zeichen für den vollständigen Pfad. Neuere OS- und .NET-Versionen erlauben „lange Pfade“, aber das funktioniert nicht immer standardmäßig.
try
{
string veryLongPath = new string('a', 300); // 300 Zeichen!
using var writer = new StreamWriter(veryLongPath + ".txt");
writer.WriteLine("Dieser Dateiname ist zu lang!");
}
catch (PathTooLongException ex)
{
Console.WriteLine("Dateiname oder Pfad ist zu lang!");
}
NotSupportedException
Das ist ein seltener, aber „surrealer“ Fall, wenn du an einen Konstruktor wie StreamReader oder FileStream eine unzulässige Pfadzeichenfolge übergibst, z.B. mit verbotenen Zeichen oder „magischen“ Pfaden wie C:::\wow???\file.txt.
7. Nützliche Details
Welche Ausnahme wofür zuständig ist
| Ausnahme | Grund für Auftreten | Beispielsituation |
|---|---|---|
|
Datei nicht gefunden | Öffnen einer nicht existierenden Datei |
|
Verzeichnis nicht gefunden | Öffnen einer Datei in einem entfernten Ordner |
|
Kein Zugriff (Rechte) | Schreiben in ein geschütztes Verzeichnis |
|
Allgemeiner Ein-/Ausgabe-Fehler | Datei wird von einem anderen Prozess verwendet |
|
Pfad zu lang | Zu langer Datei-/Ordnername |
|
Ungültiges Pfadformat | Pfad mit verbotenen Zeichen |
Häufige Fehler und Sonderfälle
Beispiel: Datei ist von einem anderen Prozess belegt
Stell dir vor, du hast wie ein echter Hacker eine Textdatei in Notepad geöffnet und vergessen, sie zu schließen. Währenddessen versucht dein Programm, in dieselbe Datei zu schreiben. Genau hier bekommst du IOException (oder eine „sharing violation“).
Beispiel: Kein Zugriff
Versuche, Daten in C:\Windows ohne Admin-Rechte zu speichern — du bekommst UnauthorizedAccessException. Dasselbe passiert, wenn du eine Datei nur mit Lesezugriff geöffnet hast und dann versuchst, hineinzuschreiben.
Beispiel: Ungültiger Pfad
Unter Windows dürfen Dateinamen keine Zeichen wie <>:"/\|?* enthalten. Wenn du versuchst, eine solche Datei anzulegen, wirft das NotSupportedException (oder ArgumentException).
Beispiel: Kein freier Speicherplatz
Auch das wirft IOException — z.B. wenn die Festplatte voll ist (deshalb ist es sinnvoll, gelegentlich den Ordner Downloads aufzuräumen).
Wie man nicht auf die Nase fällt: Best Practices zur Fehlererkennung
- Prüfe die Existenz einer Datei mit File.Exists und die Existenz eines Verzeichnisses mit Directory.Exists bevor du die Datei öffnest. Aber Vorsicht: die Datei kann sich nach der Prüfung wieder ändern (klassische race condition).
- Schlucke Ausnahmen niemals komplett (mach nicht einfach catch { }), außer du implementierst einen strikten Logger. Zumindest logg oder zeig dem Nutzer, was passiert ist.
- Fange möglichst spezifische Ausnahmen (FileNotFoundException, DirectoryNotFoundException) und nicht nur die allgemeine Exception.
- Für plattformübergreifende Anwendungen beachte, dass Zugriffsrechte, Pfadformate und maximale Pfadlängen auf Windows, Linux und macOS unterschiedlich sind.
- Bei Textdateien gib immer explizit die gewünschte Encoding an — sonst gibt's Überraschungen.
- Bei Massenoperationen mit Dateien nutze „Bulk“-Verarbeitung und berücksichtige mögliche Fehler bei jedem Schritt.
Spickzettel zu typischen Ausnahmen
| Ausnahme | Wann tritt sie auf? | Wie verhindern/behandeln? |
|---|---|---|
|
Keine Datei am angegebenen Pfad | Prüfe die Datei mit File.Exists oder erstelle sie |
|
Pfad enthält ein nicht existierendes Verzeichnis | Prüfe den Pfad, erstelle Verzeichnisse mit Directory.CreateDirectory |
|
Keine Rechte für Datei/Ordner, Datei schreibgeschützt, von anderem Prozess belegt | Starte als passender Benutzer, prüfe ACLs, schließe Dateien korrekt |
|
Allgemeiner I/O-Fehler, Datei belegt, kein Speicherplatz | Nutze try-catch, vermeide es, Dateien offen zu halten |
|
Pfad oder Dateiname zu lang | Verkürze den Pfad, verwende relative Pfade |
|
Ungültiges Pfadformat | Prüfe die Pfadzeichenfolge auf verbotene Zeichen |
Ablaufdiagramm „Was tun bei einer Datei-Fehler?“
flowchart TD
A[Dateioperation] --> B{Wurde eine Ausnahme geworfen?}
B -- Nein --> C[Operation erfolgreich abgeschlossen]
B -- Ja --> D{Welcher Ausnahmetyp?}
D -- FileNotFound --> E[Bitten den Benutzer, die richtige Datei anzugeben oder sie zu erstellen]
D -- DirectoryNotFound --> F[Fehlendes Verzeichnis erstellen]
D -- UnauthorizedAccess --> G[Bitten den Benutzer, mit den nötigen Rechten neu zu starten]
D -- IOException --> H[Prüfen, wer die Datei belegt, Festplatte prüfen]
D -- PathTooLong --> I[Pfad verkürzen]
D -- NotSupported --> J[Pfadformat prüfen]
D -- Other --> K[Fehlermeldung anzeigen und Log prüfen]
8. Wie typische Ausnahmen in einer echten Anwendung aussehen
In unserem Lernprojekt nehmen wir an, wir haben ein kleines Programm, das Nutzer-Notizen in eine Datei speichert und sie dann wieder ausliest.
string notesPath = "notes.txt";
Console.Write("Gib eine Notiz ein: ");
string note = Console.ReadLine();
try
{
// Speichern der Notiz
using var writer = new StreamWriter(notesPath, true, Encoding.UTF8);
writer.WriteLine(note);
// Alle Notizen auslesen
Console.WriteLine("Deine Notizen:");
using var reader = new StreamReader(notesPath, Encoding.UTF8);
Console.WriteLine(reader.ReadToEnd());
}
catch (FileNotFoundException)
{
Console.WriteLine("Die Notizdatei wurde nicht gefunden. Versuche, sie manuell zu erstellen.");
}
catch (DirectoryNotFoundException)
{
Console.WriteLine("Der Pfad zur Notizdatei ist ungültig. Prüfe, ob der Ordner existiert.");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Keine Berechtigung zum Schreiben oder Lesen der Datei. Starte das Programm als Administrator.");
}
catch (IOException ex)
{
Console.WriteLine("Ein Ein-/Ausgabe-Fehler ist aufgetreten: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Unerwarteter Fehler: " + ex.Message);
}
Dieses Beispiel zeigt eine typische Situation: Versuch, Daten in eine Datei zu speichern und sie dann wieder zu lesen. Jeder catch-Block behandelt eine bestimmte Fehlerklasse — von fehlender Datei oder Ordner bis zu Zugriffsproblemen und allgemeinen I/O-Fehlern. Dadurch stürzt die Anwendung nicht sofort ab, sondern informiert den Nutzer, was schiefgelaufen ist und was man tun kann. So wird die App robuster und benutzerfreundlicher.
GO TO FULL VERSION