Was währe eine Programmiersprache, ohne das Daten auf einen Datenträger gespeichert werden können. In diesem sehr Umfangreichen Kapitel geht es Ausschließlich um den Fluss der Daten. Damit ist die Eingabe und Ausgabe von Daten gemeint. C C++ C/C++ Datei Stream Eingabe Ausgabe fopen fgets fputs fread fwrite fgetc fputc gets puts sscanf sspintf ... Datei Stream Eingabe Ausgabe fopen fgets fputs fread fwrite fgetc fputc gets puts sscanf sspintf ... Kapitel 19: Datei Ein- Ausgabe

19.25. Low-Level-Datei-I/O Funktionen (nicht ANSI-C)            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

Mit den Funktionen der höheren Ebene (High-Level) wurde auf die Daten mit einem hohen Abstraktionsniveau zugegriffen. Das bedeutet, die Daten eines Programms wurden formatiert ausgegeben oder eingelesen. Bei der niedrigeren Ebene (Low-Level) wird auf einem tieferen Niveau gearbeitet. Der Zugriff auf die Daten erfolgt als eine Folge von unstrukturierten Bytes und bietet somit die Möglichkeit, Bytesequenzen vorgegebener Länge einzulesen oder auszugeben. Nicht nur die Namen der Funktionen der höheren und der niedrigeren Ebene sind ähnlich, auch ihre Abarbeitung ist es, nur mit einem, aber sehr prinzipiellen Unterschied, der etwas verwirrend ist. Während beim High-Level-Dateizugriff mit einem FILE-Zeiger (Stream) auf die Datei zugegriffen wird, geschieht dies auf der niedrigeren Ebene mit einem so genannten File-Deskriptor. Dieser Deskriptor ist kein Zeiger wie bei der höheren Ebene, sondern ein normaler int-Wert, der beim Öffnen einer Datei zurückgegeben wird.

Die höheren Dateifunktionen bauen auf den Funktionen der niedrigeren Ebene auf. Die niedrigere Ebene ist also die Grundlage der höheren Funktionen in der Standard-Bibliothek. Doch allein die Funktionen der niedrigeren Ebene arbeiten ungepuffert.

Wenn zum Beispiel Bibliotheksfunktionen wie printf() oder scanf() zum Aus- bzw. Eingeben benutzt werden, verwenden Sie eigentlich die Systemfunktionen write() und read(). Dies sind Funktionen (Systemfunktionen) im Low-Level-Bereich. Also egal, ob fgets(), fputs(), gets(), puts(), putc(), getc() ... usw. eingesetzt werden, all diese Funktionen bauen auf Systemfunktionen auf.

Natürlich ist anzumerken, dass diese Funktionen der niedrigeren Ebene systemabhängig sind und somit nicht dem ANSI C-Standard entsprechen können. Das heißt konkret, dass es Probleme mit der Portabilität der Programme geben kann.

Hinweis
 

Bei Linux/UNIX-Betriebssystemen sind Low-Level-Funktionen direkte Schnittstellen zum System. Dateizugriffe mit diesen Funktionen realisieren direkt entsprechende Betriebssystemfunktionen (so genannte System Calls).

19.26. Datei öffnen - open            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Öffnen Sie eine Datei mit der Funktion open(), so wird der Datei ein Deskriptor - auch File-Deskriptor genannt - zugeordnet. Ein File-Deskriptor ist eine positive kleine Zahl, die vom Betriebssystem vergeben wird. Über diesen Deskriptor geben Sie an, ob gelesen oder geschrieben werden soll. Mit der Funktion open() kann zudem auch gleich eine Datei angelegt werden. Hier die Syntax mit den dazugehörenden Headerdateien, die mit eingebunden werden müssen, für Linux/UNIX:

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int open(const char *pfad, int modus)
;

und die Syntax mit Headerdateien für Windows/MS-Dos:

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

int open(const char *pfad, int modus);

Der Parameter pfad dürfte für sich selbst sprechen. Beim Modus gibt es mehrere Varianten, aber diese sind systemabhängig. Einer von den folgenden drei Modi muss immer angegeben werden:

Modus Bedeutung
O_WRONLY Nur zum Schreiben öffnen
O_RDWR Zum Lesen und Schreiben öffnen
O_RDONLY Nur zum Lesen öffnen

Tabelle 19.11: Bearbeitungsmodi für open()

Dies sieht dann bei Linux/UNIX folgendermaßen aus:

int fh;
if((fh = open("/home/programmname", O_RDONLY)) != -1)

oder mit Windows/MS-DOS:

if((fh = open("c:\\config.sys", O_RDONLY)) != -1) 

In den beiden Beispielen wird eine Datei nur zum Lesen geöffnet. Zu einem dieser drei genannten Modi können Sie mit dem bitweisen ODER-Zeichen (|) weitere Aktionen verknüpfen. Dies sieht dann so aus:

if((fh = open("c:\\test.sys", O_WRONLY|O_CREAT)) != -1) 

Hiermit wird eine Datei zum Schreiben geöffnet. Existiert diese Datei nicht, so wird sie erzeugt (O_CREAT). Existiert diese Datei, so wird der zusätzliche Modus O_CREATE ignoriert. Folgende zusätzliche Modi stehen für Linux/UNIX und Windows/MS-DOS darüber hinaus zur Verfügung:

Modus Bedeutung
O_CREAT Falls Datei nicht existiert, wird sie neu angelegt. Falls die Datei existiert, ist O_CREAT ohne Wirkung.
O_APPEND Datei öffnen zum Schreiben am Ende.
O_EXCL O_EXCL kombiniert mit O_CREAT bedeutet, dass die Datei nicht geöffnet werden kann, wenn sie bereits existiert und open() den Wert -1 zurückliefert (-1 == Fehler).
O_TRUNC Datei, die zum Schreiben geöffnet wird, wird geleert. Darauf folgendes Schreiben bewirkt erneutes Beschreiben der Datei von Anfang an. Die Attribute der Datei bleiben erhalten.

Tabelle 19.12: Weitere Bearbeitungsmodi für open()

Beispielsweise wird mit

if((fh = open("/home/Name.txt", O_WRONLY | O_TRUNC)) != -1)

erreicht, dass die Datei "Name.txt" zum Schreiben geöffnet wird. Der Inhalt dieser Datei wird gelöscht, und sie kann neu beschrieben werden. Weitere Modi, die nur für Linux/UNIX zur Verfügung stehen, sind:

Modus Bedeutung
O_NOCTTY Falls der Pfadname der Name eines Terminals ist, so sollte dies nicht der Kontrollterminal des Prozesses werden.
O_NONBLOCK Falls der Pfadname der Name einer FIFO oder einer Gerätedatei ist, wird diese beim Öffnen und bei nachfolgenden I/O-Operationen nicht blockiert.
O_SYNC Nach dem Schreiben mit write() wird gewartet, bis der Schreibvorgang vollständig abgeschlossen ist.

Tabelle 19.13: Bearbeitungsmodi nur für Linux/Unix

Für Windows/MS-DOS gibt es folgende extra Modi:

Modus Bedeutung
O_BINARY Legt den Binärmodus der Datei fest
O_TEXT Legt den Textmodus der Datei fest

Tabelle 19.14: Bearbeitungsmodi nur für MS-DOS/Windows

Diese Modi können alleine stehen oder mit dem bitweisen ODER-Operator verknüpft werden. Jetzt folgen noch einige Modi, mit denen die Zugriffsrechte auf eine Datei erteilt werden können. Zuerst für Windows/MS-DOS:

Modus für Zugriffe Bedeutung
S_IWRITE Schreiben erlaubt
S_IREAD Lesen erlaubt
S_IREAD | SIWRITE Lesen und Schreiben erlaubt

Tabelle 19.15: Zugriffsrechte auf eine Datei erteilen (MS-DOS/Windows)

Mit folgender Zeile wird z.B. eine Datei erzeugt, die nur gelesen werden darf:

if((fh=open("new.xxx", O_CREAT | S_IREAD)) == -1) 

Wenn Lesen und Schreiben erlaubt sein soll, sieht dies so aus:

if((fh=open("new.xxx",O_CREAT|S_IREAD|S_IWRITE)) == -1)

Diese drei Zugriffsrechte für Windows/MS-DOS stehen in der Headerdatei <sys\stat.h>.

Für Linux/UNIX können Sie folgende Zugriffsrechte erteilen, welche sich in der Headerdatei <sys/stat.h> befinden. Hier die Modi für Zugriffsrechte unter Linux/UNIX:

Modus für Zugriffe Bedeutung
S_ISUID set-user-ID Bit
S_ISGID set-group-ID Bit
S_ISVTX sticky Bit (saved-text Bit)
S_IRUSR read (user; Leserecht für Eigentümer)
S_IWUSR write (user; Schreibrecht für Eigentümer)
S_IXUSR execute (user; Ausführrecht für Eigentümer)
S_IRWXU read, write, execute (user; Lese-, Schreib-, Ausführungsrecht für Eigentümer)
S_IRGRP read (group; Leserecht für Gruppe)
S_IWGRP write (group; Schreibrecht für Gruppe)
S_IXGRP execute (group; Ausführungsrecht für Gruppe)
S_IRWXG read, write, execute (group; Lese-, Schreib-, Ausführungsrecht für Eigentümer)
S_IROTH read (other; Leserecht für alle anderen Benutzer)
S_IWOTH write (other; Schreibrecht für alle anderen Benutzer)
S_IXOTH execute (other; Ausführungsrecht für alle anderen Benutzer)
S_IRWXO read, write, execute (other; Lese-, Schreib-, Ausführungsrecht für alle anderen Benutzer)

Tabelle 19.16: Zugriffsrechte auf eine Datei erteilen (Linux/Unix)

Im folgenden Listing soll eine Datei zum Lesen und Schreiben geöffnet werden. Existiert diese nicht, wird eine neue Datei erzeugt. Falls sie existiert, wird der Inhalt gelöscht und neu beschrieben. Hier der Quellcode, der portabel gehalten wurde:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __unix__
      #include <unistd.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
      #include <io.h>
#endif

int main()
{
   int fh;

   if((fh=open("adress.txt",O_RDWR|O_CREAT|O_TRUNC))==-1)
      {
         printf("Fehler bei open()\n");
         exit (0);
      }
   close(fh);
   return 0;
}

Falls Sie jetzt noch die Zugriffsrechte auf diese Datei vergeben wollen, muss für Linux/UNIX die Headerdatei <sys/stat.h> und für MS-DOS/Windows <sys\stat.h> eingebunden werden (beachten Sie den Slash und Backslash). Soll bspw. der User unter Linux diese Datei nur lesen dürfen, so muss nur mithilfe des ODER-Operators der Modus S_IRUSR hinzugefügt werden:

open("adress.txt", O_RDWR|O_CREAT|O_TRUNC|S_IRUSR)) == -1) 

Ein weiteres Beispiel mit open() ist z.B. das Aufrufen von Programmen aus der Kommandozeile:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __unix__
      #include <unistd.h>
      #include <sys/stat.h>
      #include <sys/types.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
      #include <io.h>
      #include <sys\stat.h>
#endif

int main(int argc,char **argv)
{
   int fh;

   if((fh=open(*++argv,O_RDONLY)) == -1)
      perror(*argv);
   else
      close(fh);
   return 0;
}

In diesem Beispiel wird eine Datei zum Lesen geöffnet, deren Name als zweites Argument in der Kommandozeile angegeben wurde. Falls die Datei nicht existiert, wird eine entsprechende Fehlermeldung wie

Programmname:No such File in Directory 

ausgegeben. Danach wird der File-Deskriptor wieder geschlossen. Falls die Datei, die eben aufgerufen wurde, nicht existiert, aber anschließend erzeugt werden soll, dann ist dies so möglich:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __unix__
      #include <unistd.h>
      #include <sys/stat.h>
      #include <sys/types.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
      #include <io.h>
      #include <sys\stat.h>
#endif

int main(int argc,char **argv)
{
   int fh;
   if(*++argv == NULL)
      return; /* Keine Argumente vorhanden */
   if((fh=open(*argv,O_RDONLY)) == -1)
      if((fh=open(*argv,O_RDWR|O_CREAT)) == -1)
         printf("Konnte %s nicht oeffnen bzw. erzeugen\n");
   close(fh);
   return 0;
}

Hiermit wird zuerst versucht, die Datei zu öffnen. Falls die Datei nicht existiert, wird diese gleich zum Lesen und Schreiben erzeugt.

Bei der Erläuterung der Low-Level-Datei-E/A-Funktionen konnten Sie schon erkennen, warum eher auf die höhere Ebene zurückgriffen wird. Wer auf mehreren Systemen programmiert, kann schnell durcheinander kommen. Was bei dem einen System gelingt, ist bei dem anderen nicht machbar. Andererseits ist es durchaus hilfreich, beim Erzeugen einer Datei die Zugriffsrechte von Beginn an festzulegen (gilt speziell unter Linux). Daraus ergibt sich, dass Low-Level-Datei-E/A vorwiegend zur Systemprogrammierung eingesetzt werden. Vor allem für Linux/UNIX-Programmierer ist diese Art, eine Datei zu öffnen, eine sehr wichtige Schnittstelle.

19.27. Datei schließen - close            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

close() dient dazu, eine Datei mit dem Deskriptor fh zu schließen, die zuvor mit open() oder create() geöffnet bzw. erzeugt wurde. Denn auch mit der Funktion open() kann nur eine bestimmte Anzahl von Dateien gleichzeitig geöffnet werden Die Anzahl der maximal offenen Dateien ist in der Konstante OPEN_MAX deklariert. Hier die Syntax zu close():

int close(int fh); 

Der Rückgabewert der Funktion ist bei Fehler -1, ansonsten 0. Hierzu noch ein kurzes Listing:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __unix__
      #include <unistd.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
      #include <io.h>
#endif

int main()
{
   int fh;

   if((fh=open("adressen.txt",O_RDONLY | O_CREAT)) == -1)
      {
         printf("Fehler\n");
         exit (0);
      }
   if((close(fh)) == -1)
      printf("Fehler beim Schliessen der Datei\n");
   else
      printf("Datei wurde ordentlich geschlossen\n");
   return 0;
}

Sie öffnen hier mit open() eine Datei zum Lesen. Falls diese nicht existiert, wird eine neue erzeugt. Danach wird der Deskriptor wieder mit einer Überprüfung, ob der Schließvorgang ordnungsgemäß verlief, geschlossen. Bei Programmende schließen sich die offenen Deskriptoren selbst.

19.28. Datei erzeugen - creat            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

Außer mit open() kann auch mit der Funktion creat() eine neue Datei angelegt werden. Die Syntax von creat() sieht bei Linux/UNIX so aus:

#include <fcntl.h>
#inlcude <sys/types.h>
#include <sys/stat.h>

int creat(const char *pfad, int modus);

und unter Windows/MS-DOS:

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

int creat(const char *pfad, int modus);

creat() arbeitet genauso wie die Funktion open(). pfad ist der Name der neu anzulegenden Datei mit dem Pfad. Mit modus sind die Modi gemeint, die in Abschnitt 19.26 zu open() geschildert wurden. Existiert eine Datei bereits, wird diese geöffnet und geleert.

Mit der Funktion open() und den Modi O_CREAT und O_TRUNC erreichen Sie dasselbe wie mit creat(). Und somit ist die Funktion creat() eigentlich völlig überflüssig und umständlich, da eine neu mit creat() angelegte Datei nur beschrieben werden kann. Um diese Datei lesen zu können, muss diese zuerst mit close() geschlossen werden, um sie anschließend mit open() zum Lesen zu öffnen.

creat() wurde zu einer Zeit benötigt, als die Funktion open() noch nicht die Angabe von O_CREAT kannte. Jetzt bleibt die Funktion natürlich weiterhin bestehen, da sonst alte Programme, die zu dieser Zeit entwickelt wurden, ohne Änderung am Quellcode nicht mehr übersetzt werden können.

Weiter mit 19.29. Schreiben und Lesen - write und read            zum Inhaltsverzeichnis