18.29 Schreiben und Lesen – write und read
 
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.
|