Dateibearbeitung

| ### Programmieren in C von Prof. Dr. Rainer Thomas

# 10. Dateibearbeitung

### 10.1 Allgemeines
10.2 Dateibearbeitung in ANSI-C (Stream-I/O)
10.3 Dateizugriff über Filedescriptor (Low Level I/O)

## 10.1 Allgemeines

### Dateibearbeitung in C - Allgemeines

- Eine Programm- Ein- bzw. Ausgabe erfolgt üblicherweise aus/in Dateien
  (--> Dateibearbeitung, Datei-Transfer, Dateizugriff) oder von/zu Geräten (z.B.Konsole, Drucker).
  C macht keinen Unterschied zwischen den beiden Gruppen.
  --> Geräte werden als Dateien betrachtet und behandelt.

- Die Sprache C selbst enthält keine Elemente zur Dateibearbeitung.
  Jegliche Ein- und Ausgabe (Dateibearbeitung) in C geschieht mittels Bibliotheksfunktionen.

- In der mit ANSI-C definierten Standardbibliothek sind eine Reihe von Funktionen zur Dateibearbeitung (Ein-/Ausgabe-Funktionen) definiert.

  Das diesen Funktionen zu Grunde liegende Model der Ein-/Ausgabe (I/O- Model) basiert auf einer Abbildung der ein- bzw auszugebenden Daten in logische Streams.

  --> Stream-I/O

  Ein Stream ist entweder einer Datei oder einem Gerät zugeordnet. Er besteht aus einer geordneten Folge von Bytes.

  I.a. wird der Begriff Stream mit den Begriffen Datei bzw File gleichgesetzt (wobei Geräte mit eingeschlossen sind).

  Die entsprechenden Funktions-Deklarationen sind in der Standard-Header- Datei

  `<stdio.h>`
  enthalten.
  In dieser Header-Datei befinden sich auch die Definitionen einiger von den Funktionen verwendeter Konstanten und Typen.

  Programmintern wird ein Stream (ein File, eine Datei) über einen Pointer auf eine Variable eines in <stdio.h> definierten structure-Typs, der

  `FILE`
  genannt wird, referiert. --> Filepointer.

  In dieser Variablen sind die von den Bibliotheksfunktionen für die Bearbeitung einer Datei benötigten Informationen zusammengefaßt.

  --> Dateizugriff über Filepointer.

- In UNIX-C (und vielen anderen C-Implementierungen) gibt es neben den ANSI-C-I/O-Funktionen noch eine weitere Gruppe von Dateibearbeitungsfunktionen.
  Diese beruhen auf dem UNIX-I/O-Model.
  Sie verwenden als Dateireferenz den File-Descriptor (File Handle) - eine direkt vom Betriebssystem vergebene Dateibezugs-Nummer.

  --> Dateizugriff über File-Descriptor (File Handle).

  Hierbei handelt es sich um eine sehr betriebssystemnahe und damit nicht unbedingt portable Art des Dateibezugs

  --> Low Level I/O

  Die Deklarationen dieser Funktionen sind i.a. in einer eigenen Header- Datei enthalten. Bei TURBO-C ist es die Header-Datei `<io.h>`.

### Dateibearbeitung in ANSI-C - Stream-I/O

- Den in der ANSI-C-Standardbibliothek definierten Funktionen zur Dateibearbeitung (Programm-Ein-/Ausgabe) liegt das Stream-Model zu Grunde:

  Jede zu bearbeitende Datei bzw jedes Gerät wird logisch als Stream - als kontinuierliche geordnete Folge von Bytes - betrachtet.

- Referiert wird ein Stream über einen Filepointer.
  Dies ist ein Pointer auf eine programminterne - vom Startup-Code bereitgestellten - Variable eines structure-Typs, der FILE genannt wird.
  In dieser Variablen sind die von den Bibliotheksfunktionen für die Bearbeitung einer Datei bzw für die Ein-/Ausgabe von/zu Geräten benötigten Verwaltungsinformationen zusammengefaßt.

- Der genaue Aufbau des Datentyps FILE ist betriebssystem- und implementierungsabhängig. Er ist in <stdio.h> definiert.
  Der Aufbau braucht einem Benutzerprogramm aber nicht bekannt zu sein, und ein Benutzerprogramm sollte auf gar keinen Fall direkt zu der über einen Filepointer referierten Variablen zugreifen.

  Mindestens muß der Datentyp `FILE` enthalten :

  - Referenz zur Datei im Dateisystem des Betriebssystems
  - Zeiger auf den Puffer, über den der Dateizugriff erfolgt
  - Information über die aktuelle Bearbeitungsposition in der Datei
  - Information über die Zugriffsart
  - Kennungen für das Auftreten von Fehlern und das Erreichen des Dateiendes

- Üblicherweise sind die in einem Programm zur Verfügung stehenden Variablen des Datentyps FILE in einem Array zusammengefaßt
  (--> `FILE`-Array, häufig mit `_streams[]`, `_iob[]` oder `_file[]` bezeichnet).
  Die Anzahl der Komponenten dieses Arrays legt fest, wieviel Dateien in einem Programm gleichzeitig geöffnet sein können.

  Durch das Öffnen einer Datei wird eine Komponente des FILE-Arrays be- legt und der entsprechenden Datei zugeordnet. Dabei werden bestimmte Initialisierungsinformationen eingetragen.
  Die Funktion zum Öffnen der Datei liefert einen Pointer auf die belegte `FILE`-Array-Komponente zurück --> den Filepointer. Jeder weitere Zugriff zu dieser Datei erfolgt dann über den Filepointer. Durch das Schließen einer Datei wird die Zuordnung zwischen Datei und `FILE`-Array-Komponente wieder aufgehoben, die `FILE`-Array-Komponente wird freigegeben. Der Filepointer darf danach nicht mehr verwendet werden.

- Die ersten drei Komponenten des FILE-Arrays werden generell beim Programmstart belegt und die damit assoziierten Dateien (tatsächlich Geräte) automatisch geöffnet (Standarddateien, Standardgeräte) :
  In `<stdio.h>` sind für die entsprechenden Filepointer Namen definiert:

  - `stdin` | --> | Standardeingabegerät (i.a. Tastatur der Konsole)
  - `stdout` | --> | Standardausgabegerät (i.a. Bildschirm der Konsole)
  - `stderr` | --> | Standardfehlerausgabe (i.a. ebenfalls Konsolenbildsch.)

  In vielen C-Implementierungen (z.B. in TURBO-C und MS-C) werden noch zwei weitere Dateien (Standardgeräte) automatisch geöffnet :

  - `stdaux` | --> | Standard-Hilfsgerät (bei MS-DOS i.a. COM1)
  - `stdprn` | --> | Standard-Drucker (bei MS-DOS i.a. LPT1)

- ANSI-C unterscheidet zwei Dateiformate :

  - Textdateien
    Sie bestehen aus einer Folge von Zeilen.
    Jede Zeile ist mit einem Newline-Zeichen abgeschlossen.
    (In der realen Datei, auf der Betriebssystemebene, kann durchaus eine andere Art des Zeilenabschlusses vorliegen, gegebenenfalls finden beim Lesen und Schreiben Umsetzungen statt)

  - Binärdateien Sie bestehen aus einer Folge von Bytes und besitzen keine weitere Struktur. Beim Lesen und Schreiben finden grundsätzlich keine Umsetzungen statt.

  Dateien können im Text- oder im Binär-Modus geöffnet werden.
  Die Standarddateien (Standardgeräte) sind alle im Textmodus geöffnet.

- Der Dateizugriff mittels der ANSI-C-(Stream-)I/O-Funktionen findet grundsätzlich gepuffert statt.
  Es wird dabei zwischen
  - Zeilenpufferung (nur für Textdateien) und
  - Blockpufferung (für beliebige Dateien)
  unterschieden.

  Die Puffergröße bei Blockpufferung ist durch die ebenfalls in `<stdio.h>` definierte Konstante `BUFSIZ` festgelegt.

  Der Puffer wird defaultmäßig vom System zur Verfügung gestellt und ist dem Programm nicht direkt zugänglich.
  Es existieren aber Funktionen zum Setzen eines benutzerdefinierten Puffers. Mit diesen Funktionen kann eine Pufferung auch ganz ausgeschaltet werden --> ungepufferter Dateizugriff.

- Es existieren in der Standardbibliothek

  - Funktionen zum Öffnen und Schließen von Dateien,
  - Funktionen zum Schreiben und Lesen
  - weitere Funktionen zur Dateibearbeitung (z.B. Positionieren)
  - Funktionen zur Fehlerbehandlung
  - Funktionen zur Dateiverwaltung

- In der Header-Datei `<stdio.h>` sind neben

  - den Funktionsdeklarationen,
  - der Definition des Datentyps `FILE`,
  - der Deklaration des `FILE`-Arrays,
  - der Definition der Standard-Filepointer
  - und der Definition der Konstanten `BUFSIZ`

  insbesondere enthalten :

  - Definition der Konstanten `EOF`
  - Definition der Konstanten `NULL` (--> NULL-Pointer)
  - Definition des Datentyps `size_t` (ein ganzzahliger Typ)
  - Definitionen weiterer von den Funktionen verwendeter Konstanten

### Beispiele für die Definition des Datentyps FILE in <stdio.h>

    /* -------------------------------------------------------------------- */

    /* Turbo-C V.2.0 (1988) */

    #if __STDC__
    #define _Cdecl
    #else
    #define _Cdecl  cdecl
    #endif

    #ifndef NULL
    #   if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__)
    #   define      NULL    0
    #   else
    #   define      NULL    0L
    #   endif
    #endif

    /* Definition of the control structure for streams
    */
    typedef struct  {
            short           level;          /* fill/empty level of buffer */
            unsigned        flags;          /* File status flags    */
            char            fd;             /* File descriptor      */
            unsigned char   hold;           /* Ungetc char if no buffer */
            short           bsize;          /* Buffer size          */
            unsigned char   *buffer;        /* Data transfer buffer */
            unsigned char   *curp;          /* Current active pointer */
            unsigned        istemp;         /* Temporary file indicator */
            short           token;          /* Used for validity checking */
    }       FILE;                           /* This is the FILE object */

    /* End-of-file constant definition
    */
    #define EOF     (-1)                    /* End of file indicator */

    /* Number of files that can be open simultaneously
    */
    #define OPEN_MAX 20                     /* Total of 20 open files */
    #define SYS_OPEN 20

    /* Default buffer size use by "setbuf" function
    */
    #define BUFSIZ  512                     /* Buffer size for stdio */

    /* Standard I/O predefined streams
    */
    extern  FILE    _Cdecl _streams[];

    #define stdin   (&_streams[0])
    #define stdout  (&_streams[1])
    #define stderr  (&_streams[2])
    #define stdaux  (&_streams[3])
    #define stdprn  (&_streams[4])

    /* -------------------------------------------------------------------- */

    /* Microsoft-C */

    #define  BUFSIZ  512
    #define  _NFILE  20
    #define  FILE    struct _iobuf
    #define  EOF     (-1)

    extern FILE {
           char *_ptr;
           int   _cnt;
           char *_base;
           char  _flag;
           char  _file;
           } _iob[_NFILE];

    #define  stdin   (&_iob[0])
    #define  stdout  (&_iob[1])
    #define  stderr  (&_iob[2])
    #define  stdaux  (&_iob[3])
    #define  stdprn  (&_iob[4])

    /* -------------------------------------------------------------------- */

    /* Intel C-86 (MWC86 V.2.5.2) */

    #define NULL    ((char *) 0)         /* Null pointer */
    #define EOF     (-1)                 /* End of file */
    #define BUFSIZ  128                  /* Size of a buffer */
    #define _NBUF   0                    /* # of buffers in dq$open call */
    #define _NFILE  20                   /* # of FILE structures */

    /*
     * This is a FILE; there is a FILE for every open stream.
     * An external array of these files is used by the I/O library.
     * If the flags are 0, the FILE is not in use.
    */
    typedef struct   FILE {
            unsigned int _flag;          /* Flags */
            unsigned int _count;         /* Count remaining */
            int      _ugetc;             /* Ungot character */
            unsigned char *_ptr;         /* Pointer */
            unsigned char *_buf;         /* Buffer */
            unsigned int _conn;          /* Connection */
            unsigned int _resp;          /* Response mailbox token for RMX */
    }       FILE;

    extern  FILE    _file[_NFILE];       /* The file structures */

    #define stdin           (&_file[0])
    #define stdout          (&_file[1])
    #define stderr          (&_file[2])

    /* -------------------------------------------------------------------- */

## 10.2 Dateibearbeitung in ANSI-C (Stream-I/O)

### Funktionen der C-Std-Bibliothek für den Dateizugriff (1)

    /* -------------------------------------------------------------------- */
    /*             fopen          -           Öffnen einer Datei            */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

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

      /* path      Pathname der Datei    */
      /* mode      Zugriffsmodus         */

`fopen` öffnet die durch `path` bezeichnete Datei und liefert einen Pointer auf die dadurch belegte Komponente des FILE-Arrays (--> Filepointer)
Über diesen Filepointer erfolgt jeder weitere Dateibezug.

Funktionswert :
  Filepointer
  bzw. NULL-Pointer im Fehlerfall

Gueltige Angaben fuer `mode` sind :

"r" | Lesen | (Datei muß existieren)
"w" | Schreiben | (evtl. exist. Datei wird gelöscht)
"a" | Anhaengen (append) | (evtl. exist. Datei wird nicht gelöscht)
"r+" | Lesen und Schreiben | (Datei muß existieren) *)
"w+" | Schreiben und Lesen | (evtl. exist. Datei wird gelöscht) *)

"a+" | Lesen (an beliebiger Position) und Schreiben am Dateiende

*) | Zwischen Lesen und Schreiben muß jeweils ein explizites Herausschreiben der Dateipuffer erfolgen (Aufruf von `fflush`) oder die Dateibeartungsposition verändert werden (Aufruf von `fpos` oder `rewind`)

Durch Anhängen des Zeichens 'b' oder 't' kann zusätzlich angegeben werden, ob es sich bei der zu bearbeitenden Datei um eine Binär- oder Textdatei handelt :

"...t" | Textdatei | (default in ANSI-C) **)
"...b" | Binärdatei |

**) Default bei TURBO-C : einstellbar über globale Variable `_fmode`

    /* -------------------------------------------------------------------- */
    /*             fclose         -         Schließen einer Datei           */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    int fclose(FILE *fp);

    /* fp      Filepointer */

`fclose` schreibt noch nicht herausgeschriebene Filebuffer in die durch `fp` referierte Datei, gibt die Buffer und die `FILE`-Array-Komponente, auf die `fp` zeigt, frei und schließt die Datei.

Funktionswert :
  im Erfolgsfall der Wert "0"
  bzw. im Fehlerfall der Wert `EOF`

    /* -------------------------------------------------------------------- */
    /*             fgetc          -         Zeichenweises Lesen             */
    /* -------------------------------------------------------------------- */
    /*             getc           -         Zeichenweises Lesen             */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    int fgetc(FILE *fp);          /* bzw:   int getc(FILE *fp);             */

      /* fp     Filepointer */

`fgetc` bzw. `getc` liest das nächste Zeichen aus der durch `fp` referierten Datei.

Funktionswert :
  das gelesene Zeichen (als int-Wert !)
  bzw. `EOF` bei Dateiende oder im Fehlerfall

`getc(stdin) == getchar()` !

    /* -------------------------------------------------------------------- */
    /*       ungetc     -    Rückgabe eines Zeichens in den Eingabepuffer   */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    int ungetc(int c, FILE *fp);

      /* c      zurueckzugebendes Zeichen */
      /* fp     Filepointer               */

`ungetc` gibt das Zeichen `c` (umgewandelt in unsigned char) in den Eingabepuffer der durch `fp` referierten Datei zurück.

Funktionswert :
  das zurückgegebene Zeichen
  bzw. `EOF` im Fehlerfall

    /* -------------------------------------------------------------------- */
    /*             fputc          -         Zeichenweises Schreiben         */
    /* -------------------------------------------------------------------- */
    /*             putc           -         Zeichenweises Schreiben         */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    int fputc(int c, FILE *fp);   /* bzw:   int putc(int c, FILE *fp);      */

      /* c      zu schreibendes Zeichen */
      /* fp     Filepointer             */

`fputc` bzw. `putc` schreibt das Zeichen `c` (umgewandelt in unsigned char) in die durch `fp` referierte Datei

Funktionswert :
  das geschriebene Zeichen (als int-Wert !)
  bzw. `EOF` im Fehlerfall

`putc(c,stdout) == putchar(c)` !

### Programmbeispiel zum zeichenweisen Lesen von Dateien

    /* -------------------------------------------------------------------- */
    /* Programm CAT                                                         */
    /* -------------------------------------------------------------------- */
    /* Konkatenation von Dateien
    /* Die Dateien, deren Namen als Command Line Parameter übergeben wer-   */
    /* den, werden aneinandergehaengt nach stdout ausgegeben.               */
    /* Wird kein Command Line Parameter uebergeben wird stdin nach stdout   */
    /* kopiert                                                              */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    void main(int argc, char *argv[])
    {
      FILE *fp;
      void copy_to_stdout(FILE *);

      if (argc==1)
        copy_to_stdout(stdin);
      else
        while (--argc>0)
          { if ((fp=fopen(*++argv,"r")) == NULL)
              printf("\nDatei \"%s\" kann nicht geöffnet werden\n\n",*argv);
            else
            { copy_to_stdout(fp);
              fclose(fp);
            }
          }
    }

    void copy_to_stdout(FILE *fp)
    {
      int c;

      while((c=fgetc(fp)) !=EOF)
        putchar(c);
    }

### Funktionen der C-Std-Bibliothek für den Dateizugriff (2)

    /* -------------------------------------------------------------------- */
    /*             fscanf         -         Formatiertes Lesen              */
    /*--------------------------------------------------------------------- */

    #include <stdio.h>

    int fscanf(FILE *fp, const char *ctrl , ...);

      /* fp        Filepointer                                        */
      /* ctrl      String zur Steuerung des Eingabeformats            */
      /* ...       weitere Parameter :  Pointer auf Variable, die     */
                                        Eingabewerte aufnehmen sollen */

`fscanf` liest die nächsten Zeichen aus der durch `fp` referierten Datei, interpretiert sie entsprechend den im Steuerstring `ctrl` vorliegenden Typ- und Formatangaben und weist die demgemäß konvertierten Werte den durch ihre Pointer (weitere Parameter !) referierten Variablen zu.

Funktionswert :

  - | die Anzahl der erfolgreich zugewiesenen Werte

  - | bzw. `EOF` wenn - beim Lesen des ersten Werts - versucht wurde über das Dateiende zu lesen

Aufbau und Bedeutung von `ctrl` wie bei der Funktion `scanf`.

`fscanf(stdin,ctrl, ... ) == scanf(ctrl, ... )`

    /* -------------------------------------------------------------------- */
    /*             fprintf        -         Formatiertes Schreiben          */
    /*--------------------------------------------------------------------- */

    #include <stdio.h>

    int fprintf(FILE *fp, const char *ctrl, ...);

      /* fp        Filepointer                                              */
      /* ctrl      String zur Festlegung des Ausgabeformats                 */
      /* ...       weitere Parameter: Ausgabewerte, Anzahl und Typ entspre- */
      /*                              chend Konvertierungszeichen in "ctrl" */

`fprintf` gibt die als weitere Parameter übergebenen Werte entsprechend den Formatangaben in `ctrl` als Zeichenfolgen in die durch `fp` referierte (Text-) Datei aus.

Funktionswert :
  Anzahl der ausgegebenen Zeichen

Aufbau und Bedeutung von `ctrl` wie bei der Funktion `printf`.

`fprintf(stdout,ctrl, ... ) == printf(ctrl, ... )`

    /* -------------------------------------------------------------------- */
    /*             fgets          -         Zeilenweises Lesen              */
    /*--------------------------------------------------------------------- */

    #include <stdio.h>

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

      /* s           String zum Ablegen der gelesenen Zeile  */
      /* n           max Anz der zu lesenden Zeichen + 1     */
      /* fp          Filepointer                             */

`fgets` liest die nächste Zeile (einschliesslich '\n') aus der durch `fp` referierten Datei, hoechstens jedoch "n"-1 Zeichen.
Die gelesenen Zeichen werden in dem durch `s` referierten String abgelegt (einschliesslich '\n' sofern es gelesen wurde), wobei '\0' angefügt wird.

Funktionswert :

  - | String `s` (genauer: Pointer `s` auf String)
  - | bzw. NULL-Pointer bei Erreichen des Dateiendes ohne vorheriges Lesen von Zeichen oder im Fehlerfall

Hinweis :
  Anders als bei gets() wird ein eventuell gelesener '\n'-Character mit im String abgelegt

    /* -------------------------------------------------------------------- */
    /*             fputs          -         Stringweises Schreiben          */
    /*--------------------------------------------------------------------- */

    #include <stdio.h>

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

      /* s           auszugebender String   */
      /* fp          Filepointer            */

`fputs` schreibt den durch `s` referierten String (ohne abschließenden '\0'-Character) in die durch `fp` referierte Datei.

Funktionswert :

  - | ein nicht-negativer Wert bei Fehlerfreiheit
  - | bzw. `EOF` (= -1) im Fehlerfall

Hinweis :
  Anders als bei `puts()` wird an den ausgegebenen String kein '\n'-Character angefügt

    /* -------------------------------------------------------------------- */
    /*             fread          -        Datenobjektorientiertes Lesen    */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    size_t fread(void *bptr, size_t size, size_t count, FILE *fp);

      /* bptr      Pointer auf Buffer zur Ablage der gelesenen Datenobjekte */
      /* size      Länge eines Datenobjekts in Bytes                        */
      /* count     Anzahl der zu lesenden Datenobjekte                      */
      /* fp        Filepointer                                              */

Anm. :
  Der Typ `size_t` ist der "natürliche" unsigned Typ (in `<stdio.h>` und `<stdlib.h>` definiert)

`fread` liest maximal `count` Datenobjekte der Länge `size` Bytes aus der durch `fp` referierten Datei und legt sie in dem durch `bptr` bezeichneten Buffer ab.

Funktionswert :
  Anzahl der tatsächlich in voller Länge gelesenen Datenobjekte
  (diese kann im Fehlerfall oder bei Erreichen des Dateiendes weniger als `count` sein)

    /* -------------------------------------------------------------------- */
    /*             fwrite        -      Datenobjektorientiertes Schreiben   */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    size_t fwrite(const void *bptr, size_t size, size_t count, FILE *fp);

      /* bptr      Pointer auf Buffer mit den zu schreibenden Datenobjekten */
      /* size      Länge eines Datenobjekts in Bytes                        */
      /* count     Anzahl der zu schreibenden Datenobjekte                  */
      /* fp        Filepointer                                              */

Anm. :
  Der Typ `size_t` ist der "natürliche" unsigned Typ
  (in `<stdio.h>` und `<stdlib.h>` definiert)

`fwrite` schreibt `count` Datenobjekte der Länge `size` Bytes in die durch `fp` referierte Datei.
Die auszugebenden Datenobjekte werden dem Buffer entnommen, auf den `bptr` zeigt.

Funktionswert :
  Anzahl der geschriebenen Datenobjekte
  (diese kann im Fehlerfall weniger als `count` sein)

`/* --------------------------------------------------------------- */`

Anmerkung :
  Mittels den Funktionen `fread()` und `fwrite()` können typenbezogene Dateien, die Datenobjekte im internen Maschinenformat enthalten, bearbeitet werden.
  (entsprechend PASCAL : `FILE OF <datentyp>`, aber flexibler, da `<datentyp>` in der Datei wechseln kann)

### Demonstrationsprogramm zu den Funktionen "fwrite" und "fread"

    /* -------------------------------------------------------------------- */
    /* Programm DFDATEI                                                     */
    /* -------------------------------------------------------------------- */
    /* Einlesen einer Reihe von float-Werten von stdin,                     */
    /* Ausgabe derselben in ein "file of float",                            */
    /* anschliessend Einlesen der in der Datei abgespeicherten Werte        */
    /* und Ausgabe nach stdout.                                             */
    /* Zum Vergleich Ausgabe der in der Datei gespeicherten Werte als       */
    /* sedezimale Bytefolge nach stdout                                     */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    #define ANZ1 6
    #define ANZ2 24
    #define FLDATEI "daten.flt"
    #define WTRENN(i,a) putchar((++i%a)==0 ? '\n' : ' ')

    void main(void)
    {
      FILE *pDatei;
          float fWert;
          int i,iByte;

      pDatei=fopen(FLDATEI,"wb");
      printf("\nBitte geben Sie Zahlen ein :\n");
      while (scanf("%f",&fWert) != EOF)
        fwrite(&fWert,sizeof(float),1,pDatei);
      fclose(pDatei);
      pDatei=fopen(FLDATEI,"rb");
      i=0;
      printf("\nAus der Datei wiedergelesene Zahlen :\n");
      while (fread(&fWert,sizeof(float),1,pDatei) != 0)
      { printf("%9.4f ",fWert);
        WTRENN(i,ANZ1);
      }
      fclose(pDatei);
      pDatei=fopen(FLDATEI,"rb");
      i=0;
      printf("\n\nDer Dateiinhalt als sedezimale Bytefolge :\n");
      while ((iByte=getc(pDatei)) != EOF)
      { printf("%02x",iByte&0xff);
        WTRENN(i,ANZ2);
      }
      fclose(pDatei);
      putchar('\n');
    }

Bildschirmausgabe :

    G:\>dfdatei

    Bitte geben Sie Zahlen ein :
    1.0  0  -1.0  2.6  3.2e-3 ^Z

    Aus der Datei wiedergelesene Zahlen :
       1.0000     0.0000    -1.0000     2.6000     0.0032

    Der Dateiinhalt als sedezimale Bytefolge :
    00 00 80 3f 00 00 00 00 00 00 80 bf 66 66 26 40 17 b7 51 3b

### Funktionen der C-Std-Bibliothek für den Dateizugriff (3)

    /* -------------------------------------------------------------------- */
    /*           fseek          -     Veraendern der Bearbeitungsposition   */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    int fseek(FILE *fp, long offset, int origin);

      /* fp            Filepointer                              */
      /* offset        Anzahl Bytes relativ zur Bezugsposition  */
      /* origin        Bezugsposition                           */

`fseek` veraendert die aktuelle Bearbeitungsposition der durch `fp` referierten Datei um `offset` Bytes gegenüber der Bezugsposition `origin`
( --> wahlfreier Dateizugriff ! ).

Die Bezugsposition wird durch einen der folgenden 3 Werte fuer `origin` gekennzeichnet :

`0 (SEEK_SET)` | Dateianfang
`1 (SEEK_CUR)` | augenblickliche Bearbeitungsposition
`2 (SEEK_END)` | Dateiende

Achtung :
  Bei Textdateien muß `offset` = 0 oder ein von der Funktion `ftell` zurückgegebener Wert (wobei dann `origin`=0 (Dateianfang !) sein muß) sein.

Funktionswert :
  "0", wenn die Bearbeitungsposition wie angegeben veraendert werden konnte
  bzw. ein Wert ungleich "0" im Fehlerfall
  bzw. undefiniert bei I/O-Geraeten

    /* -------------------------------------------------------------------- */
    /*           rewind         -      Zuruecksetzen auf den Dateianfang    */
    /*--------------------------------------------------------------------- */

    # include <stdio.h>

    void rewind(FILE *fp);

      /* fp            Filepointer */

`rewind` setzt die durch `fp` referierte Datei auf den Anfang zurueck.

Funktionswert :
  keiner

`rewind(fp) == (void)fseek(fp, 0L, SEEK_SET)` !

    /* -------------------------------------------------------------------- */
    /*        ftell      -    Ermitteln der aktuellen Bearbeitungsposition  */
    /*--------------------------------------------------------------------- */

    # include <stdio.h>

    long ftell(FILE *fp);

      /* fp         Filepointer */

`ftell` ermittelt die aktuelle Bearbeitungsposition der durch `fp` referierten Datei.
Bei Binärdateien entspricht diese Bearbeitungsposition der Anzahl Bytes relativ zum Dateianfang.
Bei Textdateien muß das nicht notwendigerweise der Fall sein.
Der für diese Dateien ermittelte Wert für die Bearbeitungsposition dient aber als sinnvoller "offset" für den Aufruf von `fseek`.
Er stellt eine Rückkehr zur Bearbeitungsposition zum Zeitpunkt des Aufrufs von `ftell` sicher.

Funktionswert :

  - | aktuelle Bearbeitungsposition bei Binärdateien : Anzahl Bytes relativ zum Dateianfang bei Textdateien : für `fseek` verwertbare Information (s. oben)
  - | bzw. "-1L" im Fehlerfall
  - | bzw. undefiniert bei I/O-Geräten

    /* -------------------------------------------------------------------- */
    /*        remove     -     Entfernen einer Datei aus dem Dateisystem    */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    int remove(const char *path);

      /* path     Pathname der zu entfernenden Datei  */

`remove` entfernt die durch `path` bezeichnete Datei aus dem Dateisystem.
Falls diese Datei zm Zeitpunkt des Aufrufs von `remove` geöffnet ist, ist das Verhalten der Funktion implementierungsabhängig.

Funktionswert :

  - | "0", falls erfolgreich
  - | bzw. ein Wert ungleich "0" im Fehlerfall

    /* -------------------------------------------------------------------- */
    /*        fflush     -        Herausschreiben eines Dateipuffers        */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    int fflush(FILE *fp);

      /* fp         Filepointer */

Wenn fp eine Datei referiert, die zum Schreiben oder Update - bei zuletzt durchgeführter Schreiboperation - geöffnet wurde, veranlaßt `fflush`, daß der zugehörige Dateipuffer geleert, d.h. an das Betriebssystem zum Herausschreiben in die Datei übergeben wird; die Datei bleibt geöffnet.
Andernfalls ist das Verhalten undefiniert.

(NB : | Ein Dateipuffer wird automatisch geleert, wenn er voll ist, wenn die Datei geschlossen wird oder wenn das Programm normal beendet wird)

Wird für `fp` der NULL-Pointer übergeben, so werden die Dateipuffer von allen für Schreiben oder Update - bei zuletzt durchgeführter Schreiboperation - geöffneten Dateien geleert.

Funktionswert :

  - | "0", wenn der Puffer erfolgreich geleert werden konnte
  - | bzw. `EOF`, wenn beim Schreiben ein Fehler aufgetreten ist.

    /* -------------------------------------------------------------------- */
    /*     setbuf  -  Bereitstellen eines benutzerdefinierten Dateipuffers  */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    void setbuf(FILE *fp, char *buffer);

      /* fp         Filepointer                                  */
      /* buffer     Pointer auf benutzerdefinierten Dateipuffer  */

`setbuf` stellt für die durch `fp` referierte Datei einen durch `buffer` referierten benutzerdefinierten Dateipuffer zur Verfügung.
`buffer` muss auf den Anfang eines char-Arrays der Laenge `BUFSIZ` (definiert in `stdio.h`) zeigen.
Ist `buffer` gleich NULL erfolgt der Zugriff zur Datei ungepuffert.

Funktionswert :
  keiner

## 10.3 Dateizugriff über Filedescriptor (Low Level I/O)

- In UNIX-C ist eine zweite Möglichkeit für den Dateizugriff implementiert worden :
  Dateizugriff über File-Descriptor

  Hierbei handelt es sich um eine direkte Schnittstelle zum UNIX-Betriebs- system :
  Die Bibliotheksfunktionen für diese Art des Dateizugriffs realisieren direkt die entsprechenden Betriebssystemfunktionen (System Calls)

  --> Low Level I/O

- Der File-Descriptor (File Handle, Path Number) ist eine kleine positive Zahl, die vom UNIX-Betriebssystem vergeben wird und über die die Betriebssystemfunktionen Dateien (bzw. Geräte) referieren.

- In einigen C-Implementierungen für andere Betriebssysteme - insbesondere für solche, die eine UNIX-ähnliche Dateiverwaltung realisieren (wie z.B. MS-DOS) - ist diese Möglichkeit für den Dateizugriff ebenfalls aufgenommen worden (z.B. in TURBO-C).
  Die entsprechenden Bibliotheksfunktionen sind aber nicht genormt, also keine ANSI-C-Funktionen. Insbesondere sind ihre Bezeichnung, ihr Umfang und ihre Wirkungsweise nicht unbedingt einheitlich.
  Auch die Bedeutung und die zulässigen Werte der Funktionsparameter sind zum Teil implementierungsabhängig.
  --> Portabilitätsprobleme.

- Vordefinierte File-Descriptoren für automatisch bei Programmstart für Low Level I/O geöffnete Dateien (Geräte) :

  0 | Standardeingabe (stdin)
  1 | Standardausgabe (stdout)
  2 | Standardfehlerausgabe (stderr)
  3 | Standard-Hilfsgerät (stdaux)
  4 | Standard-Drucker (stdprn)
  } UNIX-C |
  }
  }
  | TURBO-C

- Dateien, die für Low Level I/O geöffnet werden, belegen keine FILE- Array-Komponente --> ein Filepointer existiert nicht.
  --> Dateizugriff über Filepointer ist nicht möglich.

  Es gibt jedoch eine Bibliotheksfunktion mit der eine bereits für Low Level I/O geöffnete Datei zusätzlich für Stream-I/O geöffnet wird.
  Bei einer derartig "doppelt" geöffneten Datei ist dann auch ein Zugriff über Filepointer möglich.

  Umgekehrt existiert für Dateien, die für Stream-I/O geöffnet werden, jedoch ein File-Descriptor.
  Zu seiner Ermittlung steht eine Bibliotheksfunktion zur Verfügung.

  --> | Eine zunächst für Stream-I/O geöffenete Datei kann mit Low Level I/O-Funktionen weiter bearbeitet werden.

- Der Dateizugriff über File-Descriptor ist ungepuffert.
  Er erfolgt rein byteweise. Eine Formatierung ist nicht möglich.

- Die Deklarationen für die Low Level I/O-Funktionen von TURBO-C sind in der Header-Datei `<io.h>` enthalten.
  Spezielle für diese Funktionen verwendbare Konstante sind in den Header-Dateien `<fcntl.h>` und `<sys\stat.h>` definiert.

### Low-Level-I/O-Funktionen von TURBO-C (System-Interface zu MS-DOS) (1)

    /* -------------------------------------------------------------------- */
    /*             open          -           Öffnen einer Datei             */
    /* -------------------------------------------------------------------- */

    #include <io.h>
    #include <fcntl.h>
    #include <sys\stat.h>

    int open(char *path,int mode [,int perm]);

      /*  path        Dateizugriffspfad                                     */
      /*  mode        Bearbeitungsmodus                                     */
      /*  perm        Zugriffsberechtignug  (nur erforderlich  für O_CREAT) */

`open` öffnet die durch `path` bezeichnete Datei für den durch `mode` festgelegten Bearbeitungsmodus und liefert einen File Descriptor (handle)
Über diesen File Descriptor erfolgt jeder weitere Filebezug.

Funktionswert :
  - File Descriptor
  - bzw. -1 im Fehlerfall

Zulässige Werte für `mode` :

Es sind drei Arten von Konstanten in `<fcntl.h>` definiert, aus deren bitweiser ODER-Verknüpfung die zulässigen Werte zu bilden sind :

- Flags für Zugriffsmodus:

  `O_RDONLY` | ( 0 ) | nur Lesen
  `O_WRONLY` | ( 1 ) | nur Schreiben
  `O_RDWR` | ( 2 ) | Schreiben und Lesen

- Flags für Eröffnungsart:

  `O_CREAT` | `(0x0100)` | eine nicht existierende Datei wird mit den in `perm` angegebenen Attributen erzeugt und geöffnet, bei existierender Datei ohne Wirkung
  `O_APPEND` | `(0x0800)` | die Datei wird nur zum Anhängen geöffnet
  `O_TRUNC` | `(0x0200)` | die Datei wird geöffnet und gelöscht
  `O_EXCL` | `(0x0400)` | exclusive open, nur aus UNIX-Kompatibiltät definiert, bei MS-DOS ohne Wirkung
  `O_TEXT` | `(0x4000)` | Eröffnen einer Textdatei
  `O_BINARY` | `(Ox8000)` | Eröffnen einer Binärdatei

- Flags für File-Sharing-Modus (erst ab DOS 3.0 wirksam)

  `O_DENYALL` | `(0x10)` | Zugriff zur Datei durch andere Programme nicht zulässig
  `O_DENYWRITE` | `(0x20)` | Zugriff zur Datei durch andere Programme nur zum Lesen
  `O_DENYREAD` | `(0x30)` | Zugriff zur Datei durch andere Programme nur zum Schreiben
  `O_DENYNONE` | `(0x40)` | Zugriff zur Datei durch andere Programme unbeschränkt erlaubt
  `O_NOINHERIT` | `(0x80)` | Zugriff zur Datei durch Child Process unzulässig

Für `mode` muß immer genau ein Flag der ersten Gruppe (Zugriffsmodus) angegeben werden.
Die Flags der beiden weiteren Gruppen sind optional und können - soweit sie sich nicht gegensätzlich ausschließen - beliebig miteinander und mit dem Flag der ersten Gruppe bitweis ODER-verknüpt werden.

Zulässige Werte für `perm` (in `<sys\stat.h>` definiert) :

`S_IREAD` | `(0x0100)` | Lesen erlaubt (ist in MS-DOS immer der Fall)
`S_IWRITE` | `(0x0080)` | Schreiben erlaubt
`S_IREAD | S_IWRITE` | `(0x0180)` | Lesen und Schreiben erlaubt

    /* -------------------------------------------------------------------- */
    /*           creat          -        Öffnen einer neuen Datei           */
    /* -------------------------------------------------------------------- */

    #include <io.h>
    #include <sys\stat.h>

    int creat(char *path, int perm);

      /*  path        Dateizugriffspfad     */
      /*  perm        Zugriffsberechtigung  */

`creat` erzeugt eine neue Datei, die durch den Zugriffspfad `path` angesprochen wird, mit der durch `perm` festgelegten Zugriffsberechtigung (Dateiattribute) und öffnet sie.
Existiert die durch `path` bezeichnete Datei bereits, so wird ihr Inhalt gelöscht, falls Schreiben zulässig ist. Die Zugriffsberechtigung bleibt in diesem Fall erhalten und wird nicht gemäß "perm" geändert.
Ist die Datei schreibgeschützt, wird `creat` mit einem Fehler beendet.

Als Funktionswert liefert "creat" einen File Descriptor (handle).
Über diesen File Descriptor erfolgt jeder weitere Filebezug.

Funktionswert :
  - File Descriptor
  - bzw. -1 im Fehlerfall

Zulässige Werte für `perm` (in `<sys\stat.h>` definiert) :

`S_IREAD` | `(0x0100)` | Lesen erlaubt (ist in MS-DOS immer der Fall)
`S_IWRITE` | `(0x0080)` | Schreiben erlaubt
`S_IREAD | S_IWRITE` | `(0x0180)` | Lesen und Schreiben erlaubt

Die Datei wird grundsätzlich in dem durch die globale Variable `_fmode` gesetzten Modus (`O_TEXT` bzw. `O_BINARY`) geöffnet.
Gegebenenfalls muß `_fmode` vorher entsprechend geändert werden.

Extern-Deklaration : `extern int _fmode;`

    /* -------------------------------------------------------------------- */
    /*          close           -          Schließen einer Datei            */
    /* -------------------------------------------------------------------- */

    #include <io.h>

    int close(int fd);

      /*  fd         File Descriptor (file handle)  */

`close` schließt die durch `fd` referierte Datei und macht `fd` wieder verfügbar für die Zuordnung zu einer anderen Datei.

Funktionswert :
  - 0 falls erfolgreich
  - bzw. -1 im Fehlerfall

    read          -             Lesen  einer Datei             */
    /* -------------------------------------------------------------------- */

    #include <io.h>

    int read(int fd, void *bptr, unsigned count);

      /*  fd        File Descriptor (file handle)                           */
      /*  bptr      Pointer auf Buffer, zur Aufnahme der zu lesenden Bytes  */
      /*  count     Anzahl der zu lesenden Zeichen                          */

`read` liest die nächsten `count` Bytes aus der durch `fd` referierten Datei und legt sie in dem durch bptr bezeichneten Buffer ab.

Funktionswert :

  - | Anzahl der tatsächlich gelesenen Zeichen (diese kann bei Erreichen des Dateiendes weniger als `count` sein)
  - | bzw. 0 wenn der Leseversuch am Dateiende beginnt
  - | bzw. -1 im Fehlerfall

    /* -------------------------------------------------------------------- */
    /*          write          -          Schreiben in eine Datei           */
    /* -------------------------------------------------------------------- */

    #include <io.h>

    int write(int fd, void *bptr, unsigned count);

      /*  fd      File Descriptor (file handle)                             */
      /*  bptr    Pointer auf Buffer, der die zu schreibenden Bytes enthält */
      /*  count   Anzahl der zu lesenden Zeichen                            */

`write` schreibt `count` Bytes in die durch `fd` referierte Datei.
Die zu schreibenden Bytes werden dem Buffer entnommen, auf den `bptr` zeigt.

Funktionswert :

  - | Anzahl der tatsächlich geschriebenen Bytes (diese kann weniger als `count` sein, z.B. bei Platzmangel auf dem Datenträger)
  - | bzw. -1 im Fehlerfall

### Beispiel für den Datei-Zugriff über File-Descriptoren

    /*------------------------------------------------------------------*/
    /* Programm FCOPY                                                   */
    /*------------------------------------------------------------------*/
    /* Kopieren einer Quelldatei in eine Zieldatei unter Verwendung     */
    /* der betriebssystemnahen Low-Level-Dateifunktionen                */
    /* (Filezugriff über File-Descriptor)                               */
    /*------------------------------------------------------------------*/

    #include <io.h>
    #include <fcntl.h>
    #include <sys\stat.h>
    #include <stdio.h>

    void main(int argc, char *argv[])
    {
      int iFdq, iFdz;                 /* File Descriptoren (File Handles) */
      char cZeich;
      char acQdname[50], acZdname[50];
      char *szQpfad, *szZpfad;

      if (argc>1)
      { szQpfad=argv[1];
        if (argc>2)
          szZpfad=argv[2];
        else
        { printf("\nZieldateiname ? ");
          scanf("%s",acZdname);
          szZpfad=acZdname;
        }
      }
      else
      { printf("\nQuelldateiname ? ");
        scanf("%s",acQdname);
        szQpfad=acQdname;
        printf("\nZieldateiname  ? ");
        scanf("%s",acZdname);
        szZpfad=acZdname;
      }
      iFdq=open(szQpfad,O_RDONLY);
      iFdz=creat(szZpfad, S_IREAD | S_IWRITE);
      while (read(iFdq,&cZeich,1)>0)
        write(iFdz,&cZeich,1);
      close(iFdq);
      close(iFdz);
    }

### Low-Level-I/O-Funktionen von TURBO-C (System-Interface zu MS-DOS) (2)

    /* -------------------------------------------------------------------- */
    /*       lseek      -     Verändern der Datei-Bearbeitungsposition      */
    /* -------------------------------------------------------------------- */

    #include <io.h>

    long lseek(int fd, long offset, int origin);

      /*  fd          File-Descriptor (File Handle)            */
      /*  offset      Anzahl Bytes relativ zur Bezugsposition  */
      /*  origin      Bezugsposition                           */

`lseek` verändert die aktuelle Bearbeitungsposition der durch `fd` referierten Datei um `offset` Bytes gegenüber der Bezugsposition `origin`
( --> wahlfreier Dateizugriff)

Zulässige Werte für `origin` : (in `<io.h>` definiert)

`SEEK_SET` (0) | Dateianfang
`SEEK_CUR` (1) | augenblickliche Bearbeitungsposition
`SEEK_END` (2) | Dateiende

Bei Dateien, die für Lesen und Schreiben geöffnet wurden, kann nach dem Aufruf von `lseek` die Bearbeitungsart gewechselt werden.

Funktionswert :

  - | Abstand der neuen Bearbeitungsposition vom Dateianfang in Bytes
  - | bzw. undefiniert bei I/O-Geräten
  - | bzw. -1 im Fehlerfall

    /* -------------------------------------------------------------------- */
    /*     tell   -   Ermittlung der aktuellen Datei-Bearbeitungsposition   */
    /* -------------------------------------------------------------------- */

    #include <io.h>

    long tell(int fd);

      /*  fd      File-Descriptor (File Handle)  */

`tell` ermittelt die aktuelle Datei-Bearbeitungsposition als Byte-Offset zum Dateianfang.

Funktionswert :

  - | Aktuelle Bearbeitungsposition als Byte-Offset zum Dateianfang
  - | bzw. -1L im Fehlerfall

    /* -------------------------------------------------------------------- */
    /*  fdopen  -  Zuordnen eines Filepointers zu bereits geöffneter Datei  */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    FILE *fdopen(int fd, char *mode);

      /*  fd        File-Descriptor (File Handle)  */
      /*  mode      Zugriffsmodus                  */

`fdopen` ordnet der bereits geöffneten und durch `fd` referierten Datei einen Filepointer zu, d.h. die Datei wird zusätzlich als Stream, also auf höherer Ebene geöffnet.
--> Eine zunächst für Low-Level-I/O geöffnete Datei kann mit den High-Level-ANSI-C-(Stream-I/O-)Funktionen weiterbearbeitet werden.
Der durch `mode` festgelegte Zugriffsmodus darf dem Zugriffsmodus, für den die Datei ursprünglich geöffnet wurde (mit open), nicht wiedersprechen.

Gueltige Angaben fuer `mode` sind (wie bei `fopen`) :

"r" | Lesen | (File muß existieren)
"w" | Schreiben | (evtl. exist. File wird gelöscht)
"a" | Anhaengen (append) | (evtl. exist. File wird nicht gelöscht)
"r+" | Lesen und Schreiben | (File muß existieren)
"w+" | Schreiben und Lesen | (evtl. exist. File wird gelöscht)

"a+" | Lesen und Schreiben am Dateiende

Durch Anhängen des Zeichens 'b' oder 't' kann zusätzlich angegeben werden, ob es sich bei der zu bearbeitenden Datei um eine Binär- oder Textdatei handelt :

"...t" | Textdatei | (default in ANSI-C) **)
"...b" | Binärdatei |

**) Default bei TURBO-C : einstellbar über globale Variable `_fmode`

Funktionswert :
  - FILE-Pointer
  - bzw. NULL-Pointer im Fehlerfall

    /* -------------------------------------------------------------------- */
    /* fileno - Ermittlung des File Descriptors zu bereits geöffneter Datei */
    /* -------------------------------------------------------------------- */

    #include <stdio.h>

    int fileno(FILE *fp);

      /*  fp        Filepointer  */

`fileno` liefert den zu der bereits geöffneten und über `fp` referierten Datei gehörenden File-Descriptor (File Handle) zurück.
--> Eine zunächst für den Zugriff mittels Stream-I/O-Funktionen geöffnete Datei kann mit Low-Level-I/O-Funktionen weiterbearbeitet werden.

Funktionswert :
  - File-Descriptor (File Handle)
  - bzw. undefiniert, falls `fp` keine offene Datei referiert

Zum Inhaltsverzeichnis | Zum nächsten Abschnitt

###### Copyright © FH München, FB 04, Prof. Dr. Rainer Thomas
V - PC - A10 - 00 - TH - 03
V - PC - A11 - 00 - TH - 03
V - PC - A12 - 00 - TH - 03
V - PC - A13 - 01 - TH - 06
V - PC - A13 - 02 - TH - 06
V - PC - A21 - 00 - TH - 06
V - PC - A22 - 00 - TH - 05
V - PC - A23 - 00 - TH - 03
V - PC - A24 - 00 - TH - 05
V - PC - A25 - 00 - TH - 05
V - PC - A26 - 00 - TH - 05
V - PC - A27 - 00 - TH - 07
V - PC - A28 - 01 - TH - 05
V - PC - A28 - 02 - TH - 03
V - PC - A28 - 03 - TH - 04
V - PC - A30 - 00 - TH - 01
V - PC - A31 - 00 - TH - 02
V - PC - A32 - 00 - TH - 02
V - PC - A33 - 00 - TH - 02
V - PC - A34 - 00 - TH - 04
V - PC - A35 - 00 - TH - 02
V - PC - A36 - 00 - TH - 02