Dateiverarbeitung

In ANSI C werden Dateien (files) über einen Dateizeiger (File-Pointer) vom Typ FILE verwaltet, der in der Headerdatei stdio.h deklariert ist[29].

Vordefinierte Dateien stdin, stdout und stderr

Auch ohne in C explizit auch nur eine Datei deklariert zu haben, sind drei Dateien (bzw. File-Pointer darauf) bereits geöffnet: der Standardeingabekanal stdin (Default: Tastatur), die Standardausgabe stdout (Default: Bildschirm) und der Fehlerkanal stderr (Default: Bildschirm). Selbstverständlich können beim Aufruf eines Programms diese Standardkanäle umgelenkt werden. Unter dem Betriebssystem UNIX[30] kann das Programm a.out aufgerufen werden z.B. in der Form

a.out > outfile < infile 2> errorfile

Damit ist dann stdin ein Pointer auf die Datei infile, stdout einer auf outfile und stderr ein Pointer auf die Datei errorfile.

Sequentieller Dateizugriff und I/O-Funktionen

Die sequentielle Dateiverarbeitung in C läuft nach dem Grobmuster ab: Öffnen der Datei mit fopen(), Verarbeiten der Daten(sätze) mit verschiedenen Bibliotheksroutinen, Schließen der Datei mit fclose(). Dabei kann in zwei Modi gearbeitet werden: als Textdatei (bei den meisten Compilern der Default) oder als Binärdatei. Der Unterschied ist der, daß bei Binärdateien keinerlei Konvertierungen beim Lesen aus oder Schreiben in Dateien stattfinden, während bei Textdateien betriebssystemspezifische Umwandlungen erfolgen können. So ist unter MS-DOS das Zeilenende als "\r\n" (Carriage Return + Line Feed) definiert, während es in C (und unter UNIX) nur "\n" (Line Feed) ist. Hier wird beim Arbeiten im Textmodus automatisch eine Umwandlung vorgenommen.

Die Prototypen der hier vorgestellten Funktion befinden sich im stdio.h-Headerfile.

Ablaufschema:

1.Schritt: Öffnen der Datei mit fopen()

2.Schritt: Verarbeiten der Datensätze, das heißt:

a) zeichenweise Ein-/Ausgabe (fgetc(), fputc())

b) zeilenweise Ein-/Ausgabe (fgets(), fputs()) (Textfiles)

c) formatierte Ein-/Ausgabe (fscanf(), fprintf())

d) blockweise Ein-/Ausgabe (fread(), fwrite())

3.Schritt: Schließen der Datei mit fclose() [mittelbar bei exit()]

Wir wollen dies im folgenden schrittweise ausformulieren.

Öffnen der Datei

Prototyp:

FILE * fopen(const char * filename, const char * mode);

Aktion: fopen() versucht die Datei filename zu öffnen in dem unter mode beschriebenen Modus.

Dabei kann mode folgendes sein:

Rückgabe: fopen() liefert einen FILE-Pointer zurück oder NULL, falls der Zugriff scheitert.

Beispiel:

if ((fp=fopen("beispiel","wb"))==NULL)
{
   fprintf(stderr,"Fehler beim Öffnen der Datei\n");
   exit(EXIT_FAILURE);
} /* Öffnen der Datei beispiel zum binären Schreiben */

Verarbeiten der Datensätze

Zeichenweise Ein-/Ausgabe

Prototyp:

int fgetc(FILE *fp);

Aktion: fgetc() holt das nächste Zeichen aus der Datei fp.

Rückgabe: Die Funktion liefert den ASCII-Wert des aktuellen Zeichens aus der korrekt geöffneten Datei mit dem FILE-Pointer fp zurück oder EOF im Fehlerfalle oder am Ende der Datei.

Prototyp:

int fputc(int c, FILE *fp);

Aktion: Das Zeichen c wird von fputc() in die Datei mit dem FILE-Pointer fp geschrieben.

Rückgabe: Das Zeichen c wird (im Sinne des numerischen Wertes) zurückgeliefert, EOF im Fehlerfalle.

Zeilenweise Ein-/Ausgabe bei Textdateien

Prototyp:

char *fgets(char *s, int length, FILE *fp);

Aktion: Es wird bis EOF, bis '\n' oder zum Erreichen der angegebenen Länge length (bzw. length-1) aus der Datei fp gelesen und in den Buffer s geschrieben, der genügend Platz bereitgestellt haben muß.

Rückgabe: Im Erfolgsfall wird ein Pointer auf s zurückgeliefert, im Fehlerfalle ein NULL-Pointer.

Beispiel:

fgets(buffer,128,fp);

Prototyp:

int fputs(char *s, FILE *fp);

Aktion: Die Zeichenkette s wird in die Datei mit dem FILE-Pointer fp geschrieben. Das Stringendezeichen '\0' wird dabei nicht in die Datei übernommen.

Rückgabe: fputs() liefert die Anzahl der übertragenen Zeichen zurück, im Fehlerfalle EOF.

Beispiel:

fputs(buffer,fp);

Formatierte Ein-/Ausgabe

Prototyp:

int fscanf(FILE *fp, char *format, ...);

Aktion: Vgl. scanf(); die Daten werden lediglich statt von stdin der übergebenen Datei fp entnommen.

Rückgabe: Im Erfolgsfalle liefert fscanf() die Anzahl der ausgelesenen und abgespeicherten Parameter zurück, ansonsten EOF.

Prototyp:

int fprintf(FILE *fp, char *format, ...);

Aktion: Analog zu printf(); fprintf() schreibt jedoch in die über den FILE-Pointer fp geöffnete Datei statt nach stdout.

Rückgabe: Die Anzahl der geschriebenen Bytes oder einen negativen Wert im Fehlerfall.

Blockweise Ein-Ausgabe

Prototyp:

size_t fread(void *buf, size_t size, size_t n, FILE *fp);

Aktion: fread() liest aus der Datei mit dem FILE-Pointer fp n*size Bytes in den übergebenen Buffer buf ein.

Rückgabe: Die Funktion liefert die Anzahl der erfolgreich gelesenen Einheiten (nicht Bytes) zurück oder 0 im Fehlerfalle.

Beispiel:

fread(buf,sizeof(struct Kundendaten),100,fp);

Prototyp:

size_t fwrite(void *buf, size_t size, size_t n, FILE *fp);

Aktion: Die Funktion fwrite() schreibt in die Datei mit dem FILE-Pointer fp n*size Bytes aus dem Buffer buf.

Rückgabe: Die Funktion liefert die Anzahl der erfolgreich geschriebenen Einheiten (nicht Bytes) zurück oder 0 im Falle eines Fehlers.

Beispiel:

fwrite(buf,sizeof(struct Kundendaten),2000,fp);

Schließen der Datei

Prototyp:

int fclose(fp);

Aktion: fclose() schließt die Datei, auf die fp zeigt.

Rückgabe: 0 im Erfolgsfalle, EOF im Falle eines Fehlers.

Beispiele zur sequentiellen Dateiarbeit

Die nachfolgenden Beispielprogramme files1.c und files2.c sollen die in den vorigen Abschnitten vorgestellten Funktionen im konkreten Programm zeigen.

/* files1.c */
#include <stdio.h>
#include <stdlib.h> /* für EXIT_SUCCESS, EXIT_FAILURE */
#define STRLEN 128
int main(void)
{
   FILE *fp;
   char line[STRLEN], filename[STRLEN]="/tmp/probedatei";
   if (fp=fopen(filename,"w")) /* fp != NULL ? */
   {
      fprintf(fp,"Eine Zeile Text...");
      if (fclose(fp)==EOF)
      {
         fprintf(stderr,"\nFehler beim Schließen der Datei %s!\n",filename);
         return EXIT_FAILURE;
      }
   }
   else
   {
      fprintf(stderr,"\nFehler beim Öffnen der Datei %s!\n",filename);
      return EXIT_FAILURE;
   }
   return EXIT_SUCCESS;
} /* end main */

Die Erfahrung zeigt, daß insbesondere die beiden Funktionen fread() und fwrite() für Anfänger in C größere Schwierigkeiten bereiten. Darum hierzu ein konkretes Beispiel, das Programm files2.c.

Das erwartungsgemäße Ablauflisting dieses Programms sieht so aus:

1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20

Wahlfreier Zugriff (random access)

Neben den rein sequentiellen Routinen zum Lesen oder Schreiben einer Datei (fscanf(), fprintf()) sieht ANSI C auch einige in stdio.h deklarierte Funktionen für den wahlfreien Zugriff (random access) vor.

Mit fseek() kann der File-Pointer auf eine bestimmte Position gesetzt werden, mit rewind() wird der Dateizeiger speziell auf den Anfang der Datei gesetzt, mit ftell() kann abgefragt werden, an welcher Position sich der File-Buffer momentan befindet, mit fread() und fwrite() schließlich können Datensätze (bzw. gleich größere Datenmengen) gelesen und geschrieben werden.

Das Programm files3.c und sein Ablauflisting sollen dies etwas konkreter illustrieren.

Nachstehend noch das Ablauflisting von files3.c sowie, vielleicht ganz interessant, ein Blick in die Datendatei /tmp/probe, so wie sie auf einem PC unter DOS von dem Programm angelegt wird. (Zusatzfrage für Kenner: woran könnten Sie der Datei ansehen, daß sie auf einem PC erstellt worden ist?)

Ablauflisting:

Der erste Datensatz: 10: Isaak Asimov

Der dritte Datensatz: 12: Elias Canetti

Die Datensätze in /tmp/probe sind nun:

10: Isaak Asimov

11: Katharina Blum

12: Elias Canetti

14: Friedrich Dürrenmatt

Inhalt der Datei tmp/probe:

hexadezimal: ASCII:

0A 00 41 73 69 6D 6F 76 00 24 24 24 24 24 24 24 ..Asimov.$$$$$$$
24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 00 $$$$$$$$$$$$$$$.
49 73 61 61 6B 00 21 21 21 21 21 21 21 21 21 21 Isaak.!!!!!!!!!!
21 21 21 21 21 21 21 21 00 2E 0B 00 42 6C 75 6D !!!!!!!!....Blum
00 76 00 24 24 24 24 24 24 24 24 24 24 24 24 24 .v.$$$$$$$$$$$$$
24 24 24 24 24 24 24 24 24 00 4B 61 74 68 61 72 $$$$$$$$$.Kathar
69 6E 61 00 21 21 21 21 21 21 21 21 21 21 21 21 ina.!!!!!!!!!!!!
21 21 00 2E 0C 00 43 61 6E 65 74 74 69 00 24 24 !!....Canetti.$$
24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 $$$$$$$$$$$$$$$$
24 24 24 00 45 6C 69 61 73 00 63 68 00 21 21 21 $$$.Elias.ch.!!!
21 21 21 21 21 21 21 21 21 21 21 21 00 2E 0D 00 !!!!!!!!!!!!....
44 81 72 72 65 6E 6D 61 74 74 00 24 24 24 24 24 Dürrenmatt.$$$$$
24 24 24 24 24 24 24 24 24 24 24 24 24 00 46 72 $$$$$$$$$$$$$.Fr
69 65 64 72 69 63 68 00 21 21 21 21 21 21 21 21 iedrich.!!!!!!!!
21 21 21 21 21 21 00 2E !!!!!!..