18.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 wird folgendermaßen vorgegangen:
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. Hier die Syntax der Funktion fopen():
#include <stdio.h>
FILE *fopen(const char *pfadname, const char *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, welche sich ebenso in der Headerdatei <stdio.h> befindet, deklariert. 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 wird gleich eingegangen. 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 von Schreib-oder Lesezeiger |
|
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");
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 muss darauf geachtet werden, dass statt nur einem Backslash zwei (\\) geschrieben werden, um das Zeichen '\' anzuzeigen. Bei Linux/UNIX ist das einfacher. Ist das Verzeichnis folgendes
/home/Texte/test.txt
dann muss sich Selbiges 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, 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) – wie bei Linux/UNIX üblich – erfolgen.
|
Es gibt noch weitere Unterschiede zwischen diesen beiden Betriebssystemen. Hierzu eine Gegenüberstellung von Linux/UNIX und Microsoft-Systemen:
Tabelle 18.2
Systemabhängiges bei Angabe der zu öffnenden Datei
Eigenschaft
|
Linux
|
MS-Windows
|
Erlaubte Zeichen
|
alle Zeichen
|
Buchstaben, Zahlen und einige Sonderzeichen
|
Laufwerksbezeichnung
|
A:, b:, c:, … z:
|
keine
|
18.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. Hier ein Überblick über die vorhandenen Modi und deren Bedeutung:
Tabelle 18.3
Modus zum öffnen einer Datei mit fopen()
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 Readonly-Attribut hat, kann diese nicht geöffnet werden.
|
"a"
|
Öffnet die Datei zum Schreiben oder Anhängen ans 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 bzw. die Datei wird angelegt, falls noch nicht vorhanden. Bei Fehlern oder mangelnden Rechten liefert fopen() NULL zurück.
|
Damit dieses Buch auch als Referenz zu gebrauchen ist, folgt hierzu eine Tabelle für eine schnellere Übersicht der einzelnen Modi:
Tabelle 18.4
Schnellübersicht der Bearbeitungsmodi
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 Wird unter Linux eine neue Datei mit dem Modus "w" oder "a" angelegt, 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:
Tabelle 18.5
Text- und Binärmodus
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 erhalten.
|
t
|
Die Datei wird im Textmodus geöffnet und sollte daher auch lesbare Textzeichen beinhalten.
|
Eine kurze Erklärung des Unterschieds zwischen Textdateien und Binärdateien: Textdateien sind für den Menschen mit einem Editor lesbar. Binärdateien bzw. binäre Zeichen (0,1) bilden die Sprache, die der Computer versteht. Für einen Menschen ist dies 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 (Strg) + (Z) (unter MS-Windows/DOS) und (Strg) + (D) (unter Linux/UNIX) als Dateiende interpretiert und liefert automatisch EOF (End Of File). |
Im Binärmodus wird diese Konvertierung nicht vorgenommen. Bei Linux/UNIX bedeutet das b nichts, wie in der Tabelle oben schon erwähnt, 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 mit Hilfe 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, welche Sie in der Kommandozeile mit dem zweiten Argument angegeben haben, zu öffnen. Tritt dabei ein Fehler auf, liegt dies meistens an einer falschen Pfadangabe oder unzureichenden Rechten einer Datei.
18.5.2 Maximale Anzahl geöffneter Dateien – FOPEN_MAX
 
Bei einem Programm, bei dem sehr viele Dateien gleichzeitig geöffnet werden, sollte eine Überprüfung mit der Konstante FOPEN_MAX aus der Headerdatei <stdio.h> vorgenommen werden. 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 sollte erwähnt werden für den Fall, 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 dem Sie zuvor geschrieben haben, dann müssen Sie eine der Funktionen fsetpos(), fseek() oder rewind() verwenden; außer es wurde das Dateiende (EOF) gelesen. |
Falls Sie die Punkte noch nicht verstanden haben, keine Sorge, Sie werden auf den nächsten Seiten aufgeklärt.
|