11.1. Öffnen und Schließen von Dateien
Um sich bei Ein-/Ausgabe-Operationen auf eine Datei beziehen zu können, müssen Sie als ersten Schritt einen Zeiger auf den Datentyp FILE definieren. Bei FILE handelt es um eine Struktur (siehe Kapitel 13), die Informationen für die Abwicklung von Datei-Operationen enthält und in stdio.h definiert wird. Ihre Zusammensetzung ist von System zu System verschieden. Für den Programmierer ist dabei aber nur entscheidend, daß er sich dank FILE nicht um die Besonderheiten einzelner Dateisysteme kümmern muß und Dateien überall auf die gleiche Weise ansprechen kann.
Eine Datei wird durch den Aufruf der Funktion fopen() geöffnet. Sie richtet einen Datenstrom ein und verbindet diesen mit der Datei. Bei erfolgreicher Ausführung gibt sie einen Zeiger auf diese Datei zurück, ansonsten einen NULL-Pointer. Mit Hilfe dieses Zeigers können Sie diese Datei bei allen künftigen Ein-/Ausgabe-Operationen ansprechen. Der Dateiname muß als erstes Argument in Form einer Zeichenkette an fopen() übergeben werden. Er darf auch Pfadangaben enthalten, wobei auf Systemen, die einen Backslash als Trenner zwischen den Komponenten des Pfads verwenden (DOS, OS/2), für diesen Zweck ein doppelter Backslash anzugeben ist (z.B. "c:\\dos\\info.txt"). Dies rührt daher, daß der Backslash in C die sogenannten Escape-Sequenzen einleitet (siehe dazu Abschnitt 3.2.3).
Die Funktion fopen() erwartet neben dem Dateinamen als zweites Argument eine Zeichenkette, die den Modus der Dateiöffnung spezifiziert. Sie können Dateien grundsätzlich auf drei Arten öffnen: zum Lesen, zum Schreiben und zum Anhängen von Daten an bestehende Dateien. Diese drei Grundmuster lassen sich durch Modifizierer noch weiter bestimmen. Folgende Tabelle gibt eine Aufstellung über gültige Bezeichner für die Modi der Dateieröffnung:
Bezeichner | Eine Datei zum... |
---|---|
"r" | ...Lesen öffnen. Falls sie nicht existiert, gibt fopen() einen NULL-Pointer zurück. |
"w" | ...Schreiben öffnen. Falls die Datei bereits existiert, wird ihr Inhalt gelöscht, andernfalls wird sie erstellt. |
"a" | ...Anhängen von Daten ans Dateiende öffnen. Falls die Datei nicht existiert, wird sie erzeugt. |
"r+" | ...Lesen und Schreiben öffnen. Falls die Datei nicht existiert, gibt fopen() einen NULL-Zeiger zurück. |
"w+" | ...Lesen und Schreiben öffnen. Falls die Datei bereits existiert, geht ihr Inhalt verloren; wenn nicht, wird sie erstellt. |
"a+" | ...Lesen und Schreiben öffnen; der Positionszeiger wird auf das Dateiende gesetzt. Falls die Datei nicht existiert, wird sie erstellt. |
Bei Systemen, die zwischen Text- und Binär-Streams unterscheiden, ist der Textmodus voreingestellt. Wenn Sie Binärdateien öffnen wollen, dann müssen Sie an die Zeichenkette für den Eröffnungsmodus ein b anhängen, also z.B. "rb" oder "w+b".
Sobald Sie alle Schreib-/Lese-Operationen für eine Datei beendet haben, sollten Sie sie wieder schließen. Die dafür zuständige Funktion fclose() schließt Dateien, die zuvor mit fopen() geöffnet wurden und sorgt dafür, daß alle Daten aus den internen Datei-Puffern zurückgeschrieben werden.
Zwar können Sie im allgemeinen davon ausgehen, daß das Betriebssystem nach der Beendigung eines Programms alle offenen Dateien schließt, doch empfiehlt es sich sehr, bei der Benutzung von Dateien selbst die nötige Sorgfalt walten zu lassen. Zum einen ist die Anzahl der Dateien, die gleichzeitig geöffnet sein dürfen, auf einigen Systemen begrenzt, zum anderen führen Programmabstürze bei offenen Dateien regelmäßig zu Datenverlusten.
Die Funktion fclose() liefert bei erfolgreicher Ausführung den Wert 0 zurück, jeder andere Wert deutet auf einen Fehler hin. Im allgemeinen treten beim Schließen von Dateien keine Fehler auf, es sei denn, der Datenträger wurde vorzeitig entfernt oder er verfügt über keinen Speicherplatz mehr. Folgendes Beispiel öffnet die Datei beispiel.dat und schließt sie wieder, ohne irgendwelche Daten aus ihr zu lesen oder in sie zu schreiben:
/* fopen.c */
#include <stdio.h> /* wg. fopen(),FILE,fclose() */
#include <stdlib.h> /* wg. exit() */
int main(void){
FILE *SampleFile;
/* Datei "beispiel.dat" zum Schreiben öffnen und
* den von fopen() zurückgegebenen FILE-Pointer
* an SampleFile zuweisen
*/
SampleFile = fopen( "beispiel.dat", "w");
/* Der Erfolg oder Mißerfolg von fopen() muß
* unbedingt überprüft werden !
*/
if(SampleFile == NULL){
puts("Fehler beim Öffnen von beispiel.dat");
exit(EXIT_FAILURE);
}
else
puts("beispiel.dat erfolgreich geöffnet!");
/* Datei wieder schließen */
fclose(SampleFile);
return 0;
}
Nach dem Öffnen einer Datei müssen Sie unbedingt den Erfolg dieses Vorgangs überprüfen! Wenn fopen() nämlich scheitert und einen NULL-Zeiger zurückgibt, dann sorgt die nächste E/A-Operation mit einem solchen FILE-Pointer garantiert für ein spektakuläres vorzeitiges Ende des Programms! Das Scheitern von fopen() ist keine Seltenheit und bedarf keiner großen Systemfehler: Beim Öffnen für "Lesen" reicht es, wenn die angesprochene Datei nicht existiert.
Um unter PC-Betriebssystemen wie DOS oder OS/2 Daten auf einen Drucker auszugeben, müssen Sie anstelle einer Datei nur eine parallele oder serielle Schnittstelle öffnen. Für einen Drucker am zweiten parallelen Port ist folgende Vorgangsweise zu wählen:
/* openlpt2.c */
#include <stdio.h> /* wg. fopen(), FILE, fclose() */
#include <stdlib.h> /* wg. exit() */
int main(void){
FILE *PrinterLpt2;
PrinterLpt2= fopen( "LPT2", "w");
if(PrinterLpt2 == NULL){
puts("Fehler bei beim Drucken auf LPT2!");
exit(EXIT_FAILURE);
}
.
.
}
Um einen bestehenden Stream einer anderen Datei zuzuordnen, ist es nicht notwendig, dafür fclose() und anschließend erneut fopen() aufzurufen. Die Umleitung eines Datenstroms läßt sich durch einen Aufruf von freopen() erledigen. Diese Funktion schließt die Datei, mit der ein Stream bisher verbunden war und ordnet ihm eine neue zu. Besonders nützlich ist freopen(), um einen der drei Standard-Streams umzuleiten. Wenn Sie z.B. alle Fehlermeldungen in einer Datei ERROR.LOG protokollieren wollen, dann könnten Sie stderr auf diese Weise umleiten:
/* freopen.c
* demonstriert die Umleitung des Standard-
* Streams stderr in die Datei error.log
*/
#include <stdio.h>
#include <stdlib.h>
int main(void){
FILE *ErrorLog;
ErrorLog = freopen( "error.log", "a", stderr);
if(ErrorLog == NULL){
puts("Fehler beim Umleiten von stderr");
exit(EXIT_FAILURE);
}
else
puts("stderr auf errror.log umgeleitet!");
fclose(ErrorLog);
return 0;
}