16.5 Datei (Stream) öffnen – »fopen« 

Die Bearbeitung von Dateien erfolgt in C immer zeichenorientiert. Da Dateien zunächst nichts anderes sind als eine unstrukturierte Folge von Einzelzeichen, spielt es keine Rolle, mit welcher Art von Daten gearbeitet wird. Erst bei der Verarbeitung der Daten bekommen die Einzelzeichen eine Bedeutung und eine Struktur.
Zuerst soll eine einfache Textdatei zum Lesen geöffnet werden. Dabei gehen Sie folgendermaßen vor:
FILE *datei; ... datei = fopen("textdatei.txt", "r");
Es wurde eine Textdatei mit dem Namen textdatei.txt geöffnet. Mithilfe des Zeigers datei vom Typ FILE wird dabei ein Lese-Stream zu dieser Textdatei eingerichtet. Die Syntax der Funktion fopen() lautet:
#include <stdio.h> FILE *fopen( const char * restrict pfadname, const char * restrict modus);
Als Pfadangabe (pfadname) ist jeder zulässige String erlaubt. Sollten Sie unter einem Microsoft-Betriebssystem programmieren, kann auch eine Laufwerksangabe erfolgen. Die maximale Stringlänge für pfadname ist in der Konstante FILENAME_MAX deklariert, die sich ebenso in der Headerdatei <stdio.h> befindet. Mit modus geben Sie an, wie auf den Stream zugegriffen wird. Im Beispiel wurde der Modus "r" (für read) zum Lesen von der Datei verwendet. Auf die einzelnen möglichen Modi gehe ich gleich ein. Wenn beim Öffnen einer Datei alles planmäßig verlief, wird der FILE-Zeiger zurückgegeben. Bei einem Fehler erhalten Sie hingegen den NULL-Zeiger zurück.
Der FILE-Zeiger – es wird ja auch von einem FILE-Stream gesprochen – ist eine Struktur, die in der Headerdatei <stdio.h> deklariert ist. Diese Struktur beinhaltet alle Informationen, die für die höheren Datei-E/A-Funktionen benötigt werden, beispielsweise:
- den Puffer – die Anfangsadresse, den aktuellen Zeiger, die Größe
- den File-Deskriptor (mehr dazu bei den Funktionen der niedrigeren Ebene)
- die Position des Schreib- oder Lesezeigers
- die Fehler- und EOF-Flags
Natürlich können Sie auch mehrere Dateien auf einmal öffnen:
FILE *datei, *datei2; ... // Datei textdatei.txt zum Lesen öffnen datei = fopen("textdatei.txt", "r"); // Datei textdat2.txt zum Lesen öffnen datei2 = fopen("textdat2.txt", "r");
Kommen wir jetzt zu einem ausführbaren Beispiel der Funktion fopen():
/* fopen1.c */ #include <stdio.h> #include <stdlib.h> int main(void) { FILE *datei; /* Bitte Pfad und Dateinamen anpassen */ datei = fopen("test.txt", "r"); if(NULL == datei) { printf("Konnte Datei \"test.txt\" nicht öffnen!\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; }
Das Programm öffnet (falls vorhanden) die Datei test.txt. Konnte diese Datei nicht geöffnet werden bzw. ist sie nicht vorhanden, dann liefert die Funktion fopen() den NULL-Zeiger zurück. In diesem Beispiel muss sich die Datei test.txt im selben Verzeichnis befinden wie das ausführbare Programm. Liegt die Datei test.txt hingegen im Verzeichnis
C:\Dokumentationen\Texte\test.txt
dann muss das erste Argument in der Funktion fopen() folgendermaßen aussehen:
datei = fopen("c:\\Dokumentationen\\Texte\\test.txt", "r");
Bei Microsoft-Systemen müssen Sie darauf achten, dass statt nur einem Backslash zwei (\\) geschrieben werden, um das Zeichen '\' anzuzeigen. Bei Linux/UNIX ist das einfacher. Ist das Verzeichnis
/home/Texte/test.txt
dann muss sich Folgendes im ersten Argument befinden:
datei = fopen("/home/Texte/test.txt", "r");
Unter UNIX/Linux gibt es außerdem keine Laufwerksbezeichnung, da dort jedes Gerät – egal ob Festplatte, CD/DVD-ROM oder Diskette – als Datei betrachtet werden kann.
Hinweis |
Bei den meisten Compilern unter MS-Windows kann mittlerweile die Pfadangabe ebenfalls mit einem einfachen Slash (c:/pfad/pfad) erfolgen – wie bei Linux/UNIX üblich. |
Es gibt noch weitere Unterschiede zwischen diesen beiden Betriebssystemen. Tabelle 16.2 zeigt eine Gegenüberstellung von Linux/UNIX und Microsoft-Systemen.
Eigenschaft | Linux | MS-Windows |
Erlaubte Zeichen |
alle Zeichen |
Buchstaben, Zahlen und einige Sonderzeichen |
Laufwerksbezeichnung |
keine |
A:, B:, C:, Z: |
16.5.1 Modus für »fopen()« 

Außer dem Lesezugriff ("r"), den Sie bereits verwendet haben, gibt es eine Reihe weiterer Zugriffsmöglichkeiten auf einen Stream. Tabelle 16.3 enthält einen Überblick über die vorhandenen Modi und deren Bedeutung.
Modus | Bedeutung |
"r" |
Öffnen einer Datei zum Lesen. Wenn die Datei nicht existiert oder nicht geöffnet werden konnte, gibt fopen() NULL zurück. |
"w" |
Anlegen einer Datei zum Ändern. Wenn die Datei nicht geändert werden kann bzw. wenn keine Schreibberechtigung besteht, liefert hier fopen() NULL zurück. Wenn unter Windows/MS-DOS die Datei ein Read-only-Attribut hat, kann sie nicht geöffnet werden. |
"a" |
Öffnet die Datei zum Schreiben oder zum Anhängen an das Ende der Datei. Wenn die Datei nicht vorhanden ist, liefert fopen() wieder NULL zurück. Auch NULL wird zurückgeliefert, wenn keine Zugriffsrechte bestehen. |
"r+" |
Öffnet die Datei zum Lesen und Schreiben, also zum Verändern. Bei Fehlern oder mangelnden Rechten liefert fopen() auch hier NULL zurück. |
"w+" |
Anlegen einer Datei zum Ändern. Existiert eine Datei mit gleichem Namen, wird diese zuvor gelöscht. Bei Fehlern oder mangelnden Rechten liefert fopen() hier NULL zurück. |
"a+" |
Öffnen einer Datei zum Lesen oder Schreiben am Ende der Datei. Falls noch keine Datei vorhanden ist, wird eine angelegt. Bei Fehlern oder mangelnden Rechten liefert fopen() NULL zurück. |
Damit dieses Buch auch als Referenz zu gebrauchen ist, bietet Tabelle 16.4 eine schnellere Übersicht über die einzelnen Modi.
Bewirkt | r | w | a | r+ | w+ | a+ |
Datei ist lesbar. |
x |
x |
x |
x |
||
Datei ist beschreibbar. |
x |
x |
x |
x |
x |
|
Datei ist nur am Dateiende beschreibbar. |
x |
x |
||||
Existierender Dateiinhalt geht verloren. |
x |
x |
Hinweis |
Wenn Sie unter Linux eine neue Datei mit dem Modus "w" oder "a" anlegen wollen, schreibt der POSIX-Standard vor, dass die Datei mit folgenden Rechten angelegt wird: -rw-rw-rw |
An diese Modi können außerdem zwei weitere Zeichen angehängt werden, die zwischen Text- und Binärdateien unterscheiden (siehe Tabelle 16.5).
Zusätzlicher Modus | Bedeutung |
b |
Die Datei wird im Binärmodus geöffnet. Die Zeichen werden dabei nicht verändert bzw. konvertiert. Das heißt, jedes Zeichen wird so weitergegeben, wie es in der Datei steht, und es wird so in die Datei geschrieben, wie die Schreibfunktion eingestellt ist. Der Modus b wird bei Linux nicht verwendet und bei Angabe ignoriert. Er wird nur aus Kompatibilitätsgründen zu ANSI C beibehalten. |
t |
Die Datei wird im Textmodus geöffnet und sollte daher auch lesbare Textzeichen beinhalten. |
Lassen Sie ich kurz den Unterschied zwischen Textdateien und Binärdateien erläutern: Textdateien sind für den Menschen mit einem Editor lesbar, wogegen Binärdateien bzw. binäre Zeichen (0,1) die Sprache bilden, die der Computer versteht. Für einen Menschen sind Binärdateien kaum lesbar. Daher bestehen Textdateien immer aus sichtbaren ASCII-Zeichen und ein paar Steuercodes, wie etwa Zeilenschaltungen oder Tabulatoren. Für die Bearbeitung reiner Textdateien ist der Modus t gedacht. Da bei MS-DOS ein Zeilenende mit der Sequenz \r\n angezeigt wird und bei Linux nur durch ein einzelnes \n, führen Compiler für MS-DOS/Windows im Textmodus t folgende Konvertierung durch:
- Beim Schreiben in eine Textdatei wird ein \n automatisch in ein \r\n konvertiert.
- Beim Lesen einer Textdatei wird ein \r\n in ein einzelnes \n konvertiert.
- Beim Lesen einer Textdatei wird die Tastenkombination
+
(unter MS-Windows/DOS) und
+
(unter Linux/UNIX) als Dateiende interpretiert und liefert automatisch EOF (End Of File).
Hinweis |
Um die Anpassung müssen Sie sich als Programmierer allerdings nicht mehr direkt kümmern, weil dies von den Funktionen der Standard-Bibliothek vorgenommen wird. |
Im Binärmodus wird diese Konvertierung nicht vorgenommen. Bei Linux/UNIX bedeutet das b nichts, wie in Tabelle 16.5 schon erwähnt wurde, und wird bei Verwendung ignoriert. Unter Linux wird außerdem jede Datei binär gespeichert.
Den Namen der zu öffnenden Datei können Sie natürlich auch mithilfe von Argumenten aus der Kommandozeile angeben. Ein Beispiel:
/* fopen2.c */ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { FILE *datei; if(argc < 2) { printf("Verwendung : %s [datei_zum_Oeffnen]\n", *argv); return EXIT_FAILURE; } datei = fopen(argv[1], "r"); if(datei != NULL) printf("Datei erfolgreich geöffnet\n"); else { printf("Fehler beim Öffnen der Datei"); return EXIT_FAILURE; } return EXIT_SUCCESS; }
Zuerst wird überprüft, ob zwei Argumente in der Kommandozeile eingegeben wurden. Ist dies nicht der Fall, wird eine entsprechende Fehlermeldung ausgegeben. Ansonsten wird versucht, die Datei zu öffnen, die Sie in der Kommandozeile mit dem zweiten Argument angegeben haben. Tritt dabei ein Fehler auf, liegt dies meistens an einer falschen Pfadangabe oder unzureichenden Rechten einer Datei.
16.5.2 Maximale Anzahl geöffneter Dateien – »FOPEN_MAX« 

Bei einem Programm, bei dem sehr viele Dateien gleichzeitig geöffnet werden, sollten Sie eine Überprüfung mit der Konstante FOPEN_MAX aus der Headerdatei <stdio.h> vornehmen. Diese Konstante legt fest, wie viele Dateien gleichzeitig pro Prozess geöffnet werden dürfen. Testen können Sie dies z. B. so:
/* fopen3.c */ #include <stdio.h> #include <stdlib.h> int main(void) { printf("Max. offene Dateien : %d\n",FOPEN_MAX); return EXIT_SUCCESS; }
Hinweis für Fortgeschrittene |
Laut ANSI C sollten Sie per fopen() mindestens acht Dateien mit einem Prozess öffnen können. Meistens liegt dieser Wert aber weitaus höher. Außerdem sind mit acht Dateien reale Streams gemeint, also ohne die Standard-Streams stdin, stdout und stderr. Dies möchte ich für den Fall erwähnen, dass Sie die Struktur FILE tatsächlich auf die Anzahl offener Dateien überprüfen und sich wundern, warum dabei immer mehr Streams offen sind, als Sie in Wirklichkeit geöffnet haben. |
Damit alles reibungslos mit dem erfolgreich zurückgegebenen Stream verläuft und Sie problemlos in Dateien schreiben bzw. aus diesen lesen können, müssen Sie bei der Anwendung der Funktion fopen() noch folgende Punkte berücksichtigen:
- Fehlerflags und EOF-Flags werden beim Öffnen einer Datei zurückgesetzt.
- Wollen Sie, nachdem Sie aus einem Stream gelesen haben, in diesen schreiben, so geht dies nur, wenn Sie vorher eine der folgenden Funktionen verwenden: fflush(), fsetpos(), fseek() oder rewind().
- Wollen Sie aus einem Stream lesen, in den Sie zuvor geschrieben haben, dann müssen Sie eine der Funktionen fsetpos(), fseek() oder rewind() verwenden; außer es wurde das Dateiende (EOF) gelesen.
Machen Sie sich keine Sorgen, falls Sie diese Punkte noch nicht verstanden haben – Sie werden auf den nächsten Seiten aufgeklärt.
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.