Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 18 Ein-/Ausgabe-Funktionen
  gp 18.1 Was ist eine Datei?
  gp 18.2 Formatierte und unformatierte Ein-/Ausgabe
  gp 18.3 Streams
  gp 18.4 Höhere Ein-/Ausgabe-Funktionen
  gp 18.5 Datei (Stream) öffnen – fopen
    gp 18.5.1 Modus für fopen()
    gp 18.5.2 Maximale Anzahl geöffneter Dateien – FOPEN_MAX
  gp 18.6 Zeichenweise Lesen und Schreiben – getchar und putchar
    gp 18.6.1 Ein etwas portableres getch()
  gp 18.7 Zeichenweise Lesen und Schreiben – putc/fputc und getc/fgetc
  gp 18.8 Datei (Stream) schließen – fclose
  gp 18.9 Formatiertes Einlesen/Ausgeben von Streams mit fprintf und fscanf
  gp 18.10 Standard-Streams in C
    gp 18.10.1 Standard-Streams umleiten
  gp 18.11 Fehlerbehandlung von Streams – feof, ferror und clearerr
  gp 18.12 Gelesenes Zeichen in die Eingabe zurück-schieben – ungetc
  gp 18.13 (Tastatur-)Puffer leeren – fflush
    gp 18.13.1 Pufferung
  gp 18.14 Stream positionieren – fseek, rewind und ftell
  gp 18.15 Stream positionieren – fsetpos, fgetpos
  gp 18.16 Zeilenweise Ein-/Ausgabe von Streams
    gp 18.16.1 Zeilenweise Lesen mit gets/fgets
    gp 18.16.2 Zeilenweise Schreiben mit puts/fputs
    gp 18.16.3 Zeilenweise Einlesen vom Stream mit getline() (nicht ANSI C)
    gp 18.16.4 Rezepte für zeilenweises Einlesen und Ausgeben
  gp 18.17 Blockweise Lesen und Schreiben – fread und fwrite
    gp 18.17.1 Blockweises Lesen – fread()
    gp 18.17.2 Blockweises Schreiben – fwrite()
    gp 18.17.3 Big-Endian und Little-Endian
  gp 18.18 Datei (Stream) erneut öffnen – freopen
  gp 18.19 Datei löschen oder umbenennen – remove und rename
    gp 18.19.1 remove()
    gp 18.19.2 rename()
  gp 18.20 Pufferung einstellen – setbuf und setvbuf
  gp 18.21 Temporäre Dateien erzeugen – tmpfile und tmpnam
    gp 18.21.1 mkstemp() – Sichere Alternative für Linux/UNIX (nicht ANSI C)
  gp 18.22 Fehlerausgabe mit strerror und perror
  gp 18.23 Formatiert in einem String schreiben und formatiert aus einem String lesen – sscanf und sprintf
  gp 18.24 Fortgeschrittenes Thema
  gp 18.25 Low-Level-Datei-I/O-Funktionen (nicht ANSI C)
  gp 18.26 Datei öffnen – open
  gp 18.27 Datei schließen – close
  gp 18.28 Datei erzeugen – creat
  gp 18.29 Schreiben und Lesen – write und read
  gp 18.30 File-Deskriptor positionieren – lseek
  gp 18.31 File-Deskriptor von einem Stream – fileno
  gp 18.32 Stream von File-Deskriptor – fdopen


Galileo Computing - Zum Seitenanfang

18.29 Schreiben und Lesen – write und read  toptop

Zuerst die Syntax der Funktion write ():

#include <unistd.h> /* für UNIX/LINUX */
#include <io.h>     /* für MS-DOS     */
int write(int fh, const void *puffer, size_t bytezahl);

Mit der Funktion write() wird unformatiert in die Datei mit dem File-Deskriptor fh geschrieben. Um den geeigneten File-Deskriptor zu erhalten, muss die Datei zuvor mit open() oder create() geöffnet werden. Dann schreibt write() von der Datei mit dem fh-File-Deskriptor bytezahl Bytes in die Speicheradresse von puffer. Dieser ist wieder ein typenloser void-Zeiger und kann sich somit jeden beliebigen Datentyps annehmen. Bei einem Fehler liefert diese Funktion den Wert –1 zurück, ansonsten die Anzahl der erfolgreich geschriebenen Bytes. Hierzu ein einfaches Beispiel mit write():

/* write1.c */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#ifdef __unix__
      #include <unistd.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
      #include <io.h>
#endif
int main(void) {
   int fh;
   char puffer[100];
   strcpy(puffer,"Dieser Text steht in \"test.txt\"\n");
   if((fh=open("test.txt",O_RDWR|O_CREAT|O_TRUNC)) == –1) {
      perror(NULL);
      return EXIT_FAILURE;
    }
   if((write(fh, &puffer, sizeof(puffer))) == –1) {
      perror("Fehler bei write");
      return EXIT_FAILURE;
   }
   printf("Erfolgreich in \"test.txt\" geschrieben\n");
   return EXIT_SUCCESS;
}

Zuerst wird mit strcpy() ein String in das Array puffer kopiert. Danach wird mit open() die Datei »test.txt« geöffnet bzw. erzeugt. In dem von open() zurückgegebenen File-Deskriptor werden dann mit der Funktion write() von der Adresse puffer Bytes der Anzahl sizeof(puffer) geschrieben.

Die Funktion write() eignet sich genauso wie fwrite() dazu, ganze Strukturen auf einmal zu schreiben, wie das folgende Listing demonstriert:

/* write2.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifdef __linux__
      #include <unistd.h>
      #include <sys/stat.h>
      #include <sys/types.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
      #include <io.h>
      #include <sys\stat.h>
#endif
#define MAXADRESSEN 10
#define MAX 30
struct kunde  {
   char name[MAX];
   char vorname[MAX];
   int kundenummer;
   char ort[MAX];
   char strasse[MAX];
   int hausnummer;
   int vorwahl;
   int telefonnr;
};
struct kunde k[MAXADRESSEN];
static int counter=0;
void neukunde(void) {
   int fh;
   if(counter==MAXADRESSEN)
      printf("Kein Speicherplatz mehr frei!!!\n");
   else {
      printf("Name...................: ");
      fgets(k[counter].name, MAX, stdin);
      printf("Vorname................: ");
      fgets(k[counter].vorname, MAX, stdin);
      k[counter].kundenummer=counter;
      printf("Ort....................: ");
      fgets(k[counter].ort, MAX, stdin);
      printf("Strasse................: ");
      fgets(k[counter].strasse, MAX, stdin);
      printf("Hausnummer.............: ");
      do {
         scanf("%d",&k[counter].hausnummer);
      } while(getchar() != '\n');
      printf("Vorwahl................: ");
      do {
         scanf("%d",&k[counter].vorwahl);
      } while(getchar() != '\n');
      printf("Telefonnummer..........: ");
      do {
         scanf("%d",&k[counter].telefonnr);
      } while(getchar() != '\n');
      if((fh=creat("kunden.dat",S_IREAD|S_IWRITE)) == –1)
         printf("Konnte\"kunden.dat\" nicht öffnen\n");
      else if((write(fh,&k,sizeof(k))) == –1)
         printf("Konnte nicht in \"kunden.dat\" schreiben\n");
      else
         counter++;
   }
}
int main(void) {
   int wahl;
   do {
      printf("\t1: Neuen Kunden eingeben\n\n");
      /* printf("\t2: Kunden ausgeben\n\n"); */
      printf("\t3: Programmende\n\n");
      printf("\tEingabe :> ");
      do {
         scanf("%d",&wahl);
      } while(getchar() != '\n');
      switch(wahl) {
         case 1  : neukunde(); break;
         /* case 2 : lese(); break; */
         case 3  : printf("bye\n"); break;
         default : printf("Falsche Eingabe!!!\n");
      }
   } while(wahl != 3);
   return EXIT_SUCCESS;
}

Zuerst werden in der Funktion neukunde() die Daten an die Struktur übergeben. Anschließend wird mit

if((fh=creat("kunden.dat",S_IREAD|S_IWRITE)) == –1)

eine Datei namens »kunden.dat« zum Lesen und Schreiben erzeugt. Jetzt kann mit

else if((write(fh,&k,sizeof(k))) == –1)

in diese Datei über den File-Deskriptor fh von der Adresse struct kunde k mit Größe der Struktur (sizeof(k)) geschrieben werden. Anschließend wird counter inkrementiert. Ein wenig verwirrend können die if-else if–else-Bedingungen sein. Aber bei Korrektheit werden alle drei ausgeführt, solange keine der Bedingungen –1 zurückliefert. Jetzt befindet sich im Verzeichnis, in dem das Programm ausgeführt wird, eine Datei namens »kunden.dat«. Wird diese Datei mit einem Texteditor geöffnet, könnte man meinen, das Schreiben mit write() hätte nicht geklappt. Aber wie bereits erwähnt, es wird unformatiert in eine Datei geschrieben. Und dies lässt sich nun mal nicht mit einem Texteditor lesen.


Anmerkung   Das Schreiben mit write() wird über einen Puffercache durchgeführt, bevor wirklich auf die Festplatte, Diskette usw. geschrieben wird. Dieses delayed write birgt bei einem Systemabsturz die Gefahr, dass im Cache befindliche Daten nicht physikalisch auf Festplatte oder Diskette geschrieben werden. In diesem Fall können Sie die Datei zum Schreiben im O_SYNC-Modus öffnen. Dieser Modus wartet bei jedem physikalischen Schreibvorgang bis dieser fertig ist und liest dann erst wieder Daten ein. Der Modus hat leider aber den Nachteil, schrecklich langsam zu sein. Für Linux gibt es hier zwei Funktionen, sync() und fsync(), die in diesem Buch allerdings nicht behandelt werden. Linux-User lesen bitte entsprechende man-Pages, falls Sie diese Funktionen benötigen.


Jetzt folgt das Gegenstück zur Funktion write(). Zuerst die Syntax:

int read(int fh, const void *puffer, site_t bytezahl);

Mit der Funktion read() werden bytezahl Bytes aus der Datei mit dem File-Deskriptor fh gelesen. Die Daten werden in die Adresse von puffer abgelegt. Zuvor muss natürlich die Datei mit open() geöffnet werden. Auch hier liefert die Funktion bei Fehler –1, ansonsten, wenn alles richtig verlief, die Anzahl gelesener Bytes zurück. Hierzu ein Listing, welches eine Datei kopiert:

/* read1.c */
#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
#define MAXBYTES 1024
int main(int argc, char **argv) {
   int in,out,count;
   char buffer[MAXBYTES];
   if(argc < 3) {
      printf("Aufruf: programmname quelldatei zieldatei\n");
      return EXIT_FAILURE;
   }
   if( (in=open(*++argv ,O_RDONLY)) == –1)
      printf("Fehler open %s\n", argv);
   if( (out=open(*++argv, O_WRONLY | O_TRUNC | O_CREAT)) == –1)
      printf("Fehler open %s\n", argv);
   while(count = read(in, buffer, MAXBYTES))
      write(out,buffer,count);
   close(in);
   close(out);
   return EXIT_SUCCESS;
}

Damit wird die Datei, die als zweites Argument in der Kommandozeile angegeben wird, in die Datei kopiert, welche als drittes Argument angegeben wird.

Jetzt soll das erste Programm aus dem vorigen Abschnitt zu write() mit der Funktion read() ergänzt werden:

/* read2.c */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#ifdef __unix__
      #include <unistd.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
      #include <io.h>
      #include <sys\stat.h>
#endif
int main(void) {
   int fh;
   char puffer[100];
   char pufferneu[100];
   strcpy(puffer,"Dieser Text steht in \"test.txt\"\n");
   if( (fh = open("test.txt",O_RDWR|O_CREAT|O_TRUNC)) == –1) {
      perror(NULL);
      return EXIT_FAILURE;
   }
   if((write(fh, &puffer, sizeof(puffer))) == –1) {
      perror("Fehler bei write");
      return EXIT_FAILURE;
   }
   close(fh);
   if( (fh = open("test.txt",O_RDONLY)) == –1) {
      perror(NULL);
      return EXIT_FAILURE;
   }
   if( (read(fh, &pufferneu, sizeof(pufferneu))) == –1) {
      perror("Fehler bei read");
      return EXIT_FAILURE;
   }
   printf("%s" ,pufferneu);
   close(fh);
   return EXIT_SUCCESS;
}

Bis zur Funktion write() soweit für Sie nichts Neues. Mit read() wird hier die Größe von sizeof(pufferneu) Bytes mit dem File-Deskriptor fh in die Adresse von pufferneu gelegt. Das Programm dürfte keinem mehr Kopfzerbrechen bereiten.

Daher soll auch das zweite obige Listing mit der Funktion read() bestückt werden. Schließlich wollen Sie die Daten, die geschrieben wurden, auch wieder lesen können:

/* read3.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.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
#define MAXADRESSEN 10
#define MAX 30
struct kunde  {
   char name[MAX];
   char vorname[MAX];
   int kundenummer;
   char ort[MAX];
   char strasse[MAX];
   int hausnummer;
   int vorwahl;
   int telefonnr;
};
struct kunde k[MAXADRESSEN];
static int counter=0;
void neukunde(void) {
   int fh;
   if(counter==MAXADRESSEN)
      printf("Kein Speicherplatz mehr frei!!!\n");
   else {
      printf("Name...................: ");
      fgets(k[counter].name, MAX, stdin);
      printf("Vorname................: ");
      fgets(k[counter].vorname, MAX, stdin);
      k[counter].kundenummer=counter;
      printf("Ort....................: ");
      fgets(k[counter].ort, MAX, stdin);
      printf("Strasse................: ");
      fgets(k[counter].strasse, MAX, stdin);
      printf("Hausnummer.............: ");
      do {
         scanf("%d",&k[counter].hausnummer);
      } while(getchar() != '\n');
      printf("Vorwahl................: ");
      do {
         scanf("%d",&k[counter].vorwahl);
      } while(getchar() != '\n');
      printf("Telefonnummer..........: ");
      do {
         scanf("%d",&k[counter].telefonnr);
      } while(getchar() != '\n');
      if((fh=open("kunden.dat",O_CREAT|O_RDWR)) == –1)
         printf("Konnte\"kunden.dat\" nicht öffnen\n");
      else if((write(fh,&k,sizeof(k))) == –1)
           printf("Konnte nicht in \"kunden.dat\" schreiben\n");
      else
         counter++;
   }
}
void lese(void) {
   int fh;
   int num;
   printf("Bitte geben Sie die Kundennummer ein : ");
   scanf("%d",&num);
   if( (fh = open("kunden.dat",O_RDONLY)) == –1) {
      perror("Kann Kundendatei nicht öffnen");
      exit(EXIT_FAILURE);
   }
   read(fh,&k,sizeof(k));
   printf("\n\n");
   printf("Name..........%s",k[num].name);
   printf("Vorname.......%s",k[num].vorname);
   printf("Kundennummer..%d\n",k[num].kundenummer);
   printf("Wohnort.......%s",k[num].ort);
   printf("Strasse.......%s",k[num].strasse);
   printf("Hausnummer....%d\n",k[num].hausnummer);
   printf("Vorwahl.......%ld\n",k[num].vorwahl);
   printf("Telefonnum....%ld\n",k[num].telefonnr);
}
int main(void) {
   int wahl;
   do {
      printf("\t1: Neuen Kunden eingeben\n\n");
      printf("\t2: Kunden ausgeben\n\n");
      printf("\t3: Programmende\n\n");
      printf("\tEingabe :> ");
      do {
         scanf("%d",&wahl);
      }while(getchar() != '\n');
      switch(wahl) {
         case 1 : neukunde(); break;
         case 2 : lese(); break;
         case 3 : printf("bye\n"); break;
         default: printf("Falsche Eingabe!!!\n");
      }
   } while(wahl != 3);
   return EXIT_SUCCESS;
}

Das Datenprogramm ist wieder um eine Funktion reicher geworden, nämlich um lese(). Bei dieser wird mit

if((fh=open("kunden.dat", O_RDONLY)) == –1)

die Kundendatei zum Lesen geöffnet und mit

read(fh,&k,sizeof(k));

ausgelesen sowie anschließend auf dem Bildschirm ausgegeben.

Ein paar Zeilen noch zum File-Deskriptor. Folgende Anwendung des File-Deskriptors ist bekannt:

write(fh, &puffer, sizeof(puffer));
read(fh, &puffer, sizeof(puffer));

Aber statt des File-Deskriptors fh können logischerweise auch Ganzzahlen verwendet werden. Folgende Ziffern sind allerdings fest belegt, da diese vordefinierte Deskriptoren sind:


Tabelle 18.17   Besetzte Werte für Deskriptoren

Dezimalzahl Bedeutung
0 Standardeingabe (stdin)
1 Standardausgabe (stdout)
2 Standardfehlerausgabe (stderr)

Ein Listing dazu:

/* deskriptor_nr1.c */
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
     #include <unistd.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
     #include <io.h>
#endif
int main(void) {
   char puffer[100];
   read(0, &puffer, sizeof(puffer));
   printf("%s",puffer);
   return EXIT_SUCCESS;
}

Mit read(0, &puffer, sizeof(puffer)) wird aus der Standardeingabe (stdin) in die Adresse des Puffers gelesen, also von der Tastatur. Anhand der Ausgabe können Sie auch die Eigenheiten der niedrigeren Ebene erkennen. Hier wird nicht automatisch ein Stringende-Zeichen angehängt, darum müssen Sie sich selbst kümmern. Dasselbe kann auch mit write() auf dem Bildschirm vorgenommen werden:

/* deskriptor_nr2.c */
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
     #include <unistd.h>
#elif __MSDOS__ || __WIN32__ || _MSC_VER
     #include <io.h>
#endif
int main(void) {
   char puffer[] = "Ich werde im Low-Level-I/O ausgegeben";
   write(1, &puffer, sizeof(puffer));
   return EXIT_SUCCESS;
}

Da der File-Deskriptor manchmal so verwendet wird, sollte dies hier nicht unerwähnt bleiben.

 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de