Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Input/Output

stdio-Bibliothek

In C sind die Eingabe- und Ausgabeoperationen nicht Teil der Sprache, sondern werden durch eine Bibliothek zur Verfügung gestellt. Da diese Bibliothek im C-Standard definiert wird, sollte sie auf allen Plattformen und bei allen C-Compilern zur Verfügung stehen. Ausnahmen sind eingebettete Systeme, die kein Input/Output unterstützen.

Dreh und Angelpunkt der I/O in C ist die stdio-Bibliothek, die eine ganze Reihe von Funktionen anbietet.

  • Eingabe/Ausgabe wird über die stdio-Bibliothek realisiert
    • #include <stdio.h>
    • wird automatisch gelinkt
  • Definiert den Typ FILE* und Funktionen für Dateizugriff
  • Definiert stdin, stdout, stderr

stdin, stdout und stderr sind vordefinierte FILE-Objekte, die den Zugriff auf die Standard-Ein- und -Ausgabe ermöglichen.

Neben den Funktionen in stdio.h, die im C-Standard definiert werden, gibt es eine Reihe von korrespondierenden Low-Level-I/O-Funktionen, die vom POSIX-Standard festgelegt werden. Diese Funktionen sind näher am Betriebssystem und bieten weniger Komfort. Außerdem sind sie nur auf Systemen verfügbar, die den POSIX-Standard unterstützen, wozu allerdings die relevanten Betriebssysteme gehören. Deswegen sollte man prinzipiell die C-Standard-Funktionen benutzen und die Low-Level-Funktionen ignorieren.

Beispiele Low-Level-Funktionen sind:

  • int open(const char *pathname, int flags, mode_t mode);
  • int creat(const char *pathname, mode_t mode);
  • int close(int fd);

Ken Thompson, einer der Erfinder von Unix, wurde einmal gefragt, was er heute anders machen würde, wenn er Unix noch einmal entwickeln dürfte. Seine Antwort war „I'd spell creat with an e.“

Dateien öffnen und schließen

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

  • öffnet die Datei path
  • Modus (mode)
    • "r": Lesen
    • "r+": Lesen und schreiben (ab Anfang)
    • "w": Anlegen und schreiben (ab Anfang)
    • "w+": Anlegen, lesen und schreiben (ab Anfang)
    • "a": Schreiben (am Ende anhängen)
    • "a+": Lesen und schreiben (am Ende anhängen)
  • bei Erfolg wird ein FILE* zurückgegeben, im Fehlerfall NULL

int fclose(FILE *stream)

  • schließt die Datei stream
  • 0 bei Erfolg, EOF im Fehlerfall
    FILE* handle = fopen("/tmp/file", "r");

    if (handle == NULL) {
      /* Fehler */
      exit(0);
    }

    /* Mit Datei arbeiten */

    if (fclose(handle)) {
      /* Fehler beim Schließen */
    }

Die Standardbibliothek spricht hier von Streams und nicht Dateien, weil die Metoden auch auf andere Arten von I/O angewendet werden können, z. B. die Console, die keine Datei ist.

Zeichen-I/O

Funktionen für den Zugriff auf einzelne Zeichen. Alle Funktionen geben EOF zurück, wenn Datei/stream zu Ende ist oder ein Fehler auftritt

  • int getchar()
    Liest das nächste Zeichen von stdin
  • int fgetc(FILE *in)
    List das nächste Zeichen aus Datei in
  • int putchar(int c)
    Schreibt ein Zeichen auf stdout
  • int fputc(int c, FILE *out)
    Schreibt ein Zeichen in Datei out

Bemerkenswert ist aber die Tatsache, dass die Funktion getchar() ein int als Rückgabetyp hat und kein char, obwohl ein Stream byteweise gelesen wird. Der größere Datentyp ist nötig, da durch ein EOF signalisiert wird, dass der Stream zu Ende ist. EOF hat den Wert -1. Da aber -1 ein gültiger char-Wert ist, hat man hier den größeren Datentyp nehmen müssen. Aus diesem Grund ist es falsch den Rückgabewert von getchar() vor dem Vergleich mit EOF zu casten. Erst muss mit EOF (als int) verglichen werden, dann gecastet, sonst bricht der Lesevorgang möglicherweise zu früh ab, nämlich wenn in den Daten zufällig ein 0xFF vorkommt, das vorzeichenbehaftet einer -1 entspricht.

Korrekter Umgang mit EOF
int c;
FILE* fd = fopen("test.txt", "r");
while ((c = fgetc(fd)) != EOF) {
    printf("%c", (unsigned char) c);
}

Dass die putchar()- und fputc()-Funktionen auch einen int und kein unsigned char nehmen, dient der Bequemlichkeit des Programmierers, der sich so teilweise einen Cast ersparen kann.

Zeilen-I/O

Anstatt einzelne Zeichen kann man auch direkt auf Zeilen arbeiten. Die entsprechenden Funktionen akzeptieren bzw. liefern Zeichenketten in Form von char*.

Funktionen für den Zugriff auf Zeilen

  • char *fgets(char *buf, int size, FILE *in)
    • liest die nächste Zeile von in in buf
    • kehrt bei '\n' zurück oder wenn size - 1 Zeichen gelesen wurden
    • '\n' selbst wird auch zurückgegeben
    • gibt Zeiger auf buf zurück oder NULL im Fehlerfall
    • auf keinen Fall gets(char *) verwenden ⇒ Buffer-Overflow
  • int fputs(const char *str, FILE *out)
    • schreibt den String str in die Datei out
    • stoppt beim '\0'
    • gibt die Anzahl der geschriebenen Zeichen zurück, oder EOF bei Fehler

Formatierte I/O

  • int fscanf(FILE *in, const char *format, ...)
    Analog zu scanf aber für Datei
  • int fprintf(FILE *out, const char *format, ...)
    Analog zu sprintf aber für Datei
  • int printf(const char *format, ...)
    fprintf(stdout, format, ...)
fscanf(...) sollte man nicht verwenden. Besser fgets(str, ...) zusammen mit sscanf(str, ...)

Von der Console lesen

  • Mit fgets(str, ...) und sscanf(str, ...) kann man Daten von der Konsole lesen
int main() {
    char buffer[255];
    int zahl;
    printf("Bitte geben Sie eine Zahl ein: ");

    if (fgets(buffer, 254, stdin) != 0) {
        sscanf(buffer, "%d", &zahl);
        printf("Die Zahl war %d\n", zahl);
    }
}
Ausgabe
Bitte geben Sie eine Zahl ein: 62
Die Zahl war 62

Copyright © 2022 Thomas Smits