|
Programmname < Dateiname | Dieses Kommando bewirkt, daß das Programm von derangegebenen Datei liest. |
Programmname > Dateiname |
Dieses Kommando bewirkt, daß das Programm auf die
angegebenen Datei ausgibt. |
Die sogenannten Standard-Filepointer sind immer initialisiert,
die zugehörigen Dateien immer geöffnet:
stdin | Standardeingabe | (Tastatur) |
stdout | Standardausgabe | (Bildschirm) |
stderr | Fehlerausgabe | (Bildschirm!) |
Die Unterscheidung zwischen stdout und stderr ist beispielsweise
dann relevant, wenn man die Bildschirmausgabe (stdout) in irgendwelche Files
umleitet (z. B. mit '>' unter DOS und UNIX), aber verhindert werden soll,
daß auch etwaige Fehlermeldungen dorthin "verschwinden".
Ein einfaches Beispiel für Zugriff auf die Standarddateien stdin
und stdout:
/* Programm 'cat' */
#include <stdio.h>
int main(void)
{
int ch;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}
Solange Eingabedaten von der Standardeingabe kommen werden sie auf der Standardausgabe
wieder ausgegeben. Das Programm "echot" also die Standardeingabe. Mit der Ausgabeumleitung
kann man damit Tastatureingaben in eine Datei schreiben, z. B. durch den Programmaufruf:
cat > mein.text. Mittels Eingabeumleitung kann man Dateiinhalte auf den
Bildschirm bringen, z. B.: cat < mein.text.
Eine Unschönheit von C kann man an diesem Programm gut erkennen. Die Variable
ch ist nicht - wie man es erwarten sollte - als char definiert, sondern
als int. Das liegt einzig und alleine daran, daß die vordefinierte
Konstante EOF den Wert -1 besitzt und daher bei der Verwendung einer
char-Variablen das Datei- bzw. Eingabeende nicht erkannt würde.
Das Eingabeende unter UNIX wird durch das Zeichen Ctrl-D (bzw. auf der deutschen
Tastatur Strg-D) repräsentiert. Unter DOS/Windows ist es das Zeichen
Ctrl-Z (bzw. auf der deutschen Tastatur Strg-Z).
Beispiel: Kopieren der Standardeingabe zur Standardausgabe; Zählen der Byteanzahl.
#include <stdio.h>
int copy(void);
void main()
{
int bytes;
int blocks;
bytes = copy();
fprintf (stderr, "%d Bytes kopiert.\n", bytes);
}
int copy(void)
{
int c;
int bytes;
bytes = 0;
while ((c = getchar()) != EOF)
{
putchar(c);
bytes = bytes + 1;
}
return (bytes);
}
Beispiel: Kopieren der Standardeingabe zur Standardausgabe; Numerieren aller Zeilen.
#include <stdio.h>
#include <stdio.h>
#define YES 1
#define NO 0
main()
{
short nl_anz;
int c, zeile_nr;
zeile_nr = 1;
nl_anz = YES;
while ((c = getchar()) != EOF)
{
if(nl_anz)
{
printf("%6d ", zeile_nr);
zeile_nr = zeile_nr + 1;
}
if(c != '\n')
nl_anz = NO;
else
nl_anz = YES;
putchar(c);
}
}
Beispiel: Worte, Zeichen und Zeilen zählen.
#include <stdio.h>
#define IMWORT 1
#define AUSSEN 0
main()
{
int c,wo;
int nc, nw, nl; /* Zeichen, Worte, Zeilen */
wo = AUSSEN; /* Anfangswerte */
nc = nw = nl = 0;
while((c = getchar())!= EOF)
{
++nc;
if(c == '\n') ++nl;
if( (c == '\n') || (c == '\t') || (c == ' ') ) wo = AUSSEN;
else
if(wo == AUSSEN)
{
wo=IMWORT;
++nw;
}
}
printf("\nDas waren %d Zeichen, %d Worte und %d Zeilen.\n",nc,nw,nl);
}
Beispiel: Wortlängen im Eingabestrom ermitteln
Es wird die länge eines jeden Wortes bestimmt und die Längen zwischen
1 und 25 gezählt. Alle Wörter, die länger als 25 Zeichen sind,
werden in einem 26. Zähler registriert. Als Ausgabe erhält man die
Anzahlen der der Worte:
#include <stdio.h>
#define MAXWORDLEN 26 /* maximale Wort-Laenge + 1*/
int main(void)
{
int c; /* gelesenes Zeichen */
int wl,zl; /* Wortlaenge, Zeilenlaenge */
int i; /* Schleifenzaehler */
int words,lines; /* Wortzaehler, Zeilenzaehler */
int maxline,maxword; /* Maximallaengen */
int wordlen[MAXWORDLEN]; /* speichert die Wortlaengen */
zl = words = maxline = maxword = lines = 0;
for ( i=0; i < MAXWORDLEN; i++ )
wordlen[i] = 0;
c = getchar(); /* 1. Zeichen lesen */
while (c != EOF)
{
while (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))
&& (c != '\n') && (c != EOF))
{ /* Wortanfang suchen */
c = getchar(); /* Zeichen lesen */
zl++; /* Zeilenlaenge erhoehen */
}
wl = 0; /* Wortlaenge auf 0 setzen */
while ((c != EOF)
&& ((('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))
|| ('0' <= c && c <= '9') || (c == '_')))
{ /* naechstes Wort lesen */
c = getchar(); /* Zeichen lesen */
zl++; /* Zeilenlaenge erhoehen */
wl++; /* Wortlaenge erhoehen */
}
if (wl > 0) words++; /* Wortzaehler erhoehen */
if (wl > maxword ) maxword = wl; /* max. Wortlaenge */
if (wl >= MAXWORDLEN) /* groesser als MAXWORDLEN? */
wordlen[MAXWORDLEN-1]++; /* ja - ins letzte Element */
else
wordlen[wl-1]++; /* passenden Zaehler erhoehen */
if ((c == '\n') || (c == EOF)) /* neue Zeile? */
{
lines++; /* Zeilenzaehler erhoehen */
if (zl > maxline)
maxline = zl; /* max. Zeilenlaenge */
zl = 0;
c = getchar();
}
}
for ( i=0; i < MAXWORDLEN; i++ ) /* Maximalzahl der Worte */
printf("%10d | %10d\n", i, wordlen[i]);
return 0;
}
Sequentielle Dateien
Will man zusätzlich zu den Standard-Dateien noch weitere Dateien auf der Platte
benutzen, so muß man diese Dateien an das Programm anbinden.
Alle Befehle zur Dateibearbeitung sind nicht Teil von C. Die Standardbibliotheken
enthalten aber viele Funktionen zum Arbeiten mit Dateien.
Im folgenden Beispiel wird eine Datei sequentiell beschrieben und wieder gelesen.
An diesem Beispiel werden einige grundlegende Befehle erklärt, weshalb der
Quelltext mit Zeilennummern in eckugen Klammern versehen wurde. Die Zeilennummern
finden Sie in den Erklärungen unten wieder:
[ 1] #include <stdio.h>
[ 2]
[ 3] int main(void)
[ 4] {
[ 5] FILE *fp;
[ 6] int i,xi;
[ 7] static char dateiname[]="daten.datei";
[ 8] char text[80];
[ 9]
[10] /* Beschreiben der Datei */
[11] fp = fopen(dateiname,"w");
[12] if ( fp == NULL)
[13] {
[14] fprintf(stderr,"Datei %s kann nicht zum Schreiben\
[15] geoeffnet werden\n",dateiname);
[16] exit(1);
[17] }
[18] for ( i=0; i<10; i=i+2)
[19] fprintf(fp,"%d\n",i);
[20] fclose(fp);
[21]
[22] /* Lesen der Datei */
[23] fp = fopen("meine.dat","r");
[24] if (fp == NULL)
[25] {
[26] fprintf(stderr,"Datei %s kann nicht zum Lesen\
[27] geoeffnet werden\n",dateiname);
[28] exit(2);
[29] }
[30] while(feof(fp) == 0)
[31] {
[32] fscanf(fp,"%d",&xi);
[33] printf("%d",xi);
[34] }
[35] fclose(fp);
[36] exit(0);
[37] }
Im Programm finden wir folgende Dateianweisungen:
- [ 5] Definieren einer Datei:
FILE *<Dateizeiger>
FILE ist eine spezielle Stream-Struktur, der Datentyp für Dateien. Er
ist in der Standardbibliothek <stdio.h> als Struktur festgelegt,
die Informationen über die Datei enthält (z. B. Pufferadresse,
Zugriffsrechte u.s.w. ).
Mit obiger Anweisung wird ein Zeiger auf den Datentyp FILE definiert.
- [11], [23] Öffnen einer Datei:
<Dateizeiger> = fopen(<Dateiname>,<Zugriffsmodus>);
Die Funktion fopen verbindet den externen Namen der Datei
mit dem Programm und liefert als Ergebnis den Zeiger auf die Beschreibung
der Datei. Im Fehlerfall wird der NULL-Zeiger zurückgeliefert.
Die Funktion ist definiert als
FILE *fopen(const char *filename, const char *modus)
als Zugriffsmodus steht zur Verfügung eine Kombination von "a", "r", "w" und "+":
- 'r' (Lesen (read))
- 'w' (Schreiben (write))
- 'a' (Anhängen (append))
- 'r+' (Lesen und Schreiben)
- 'w+' (Schreiben und Lesen)
- 'a+' (Lesen an bel. Positeon, Schreiben am Dateiende)
Durch anhängen eines Zusatzes kann festgelegt werden, ob es sich bei der zu
bearbeitenden Datei um eine Binär- oder Textdatei handelt:
- 't' (für text)
- 'b' (für binary)
Die Funktion fopen reagiert folgendermaßen:
- Beim Öffnen einer existierenden Datei
- zum Lesen: keine Probleme
- zum Anhängen: keine Probleme
- zum Schreiben: Inhalt der Datei geht verloren
- Beim Öffnen einer nicht existierenden Datei
- zum Lesen: Fehler, Ergebnis ist NULL-Zeiger
- zum Anhängen: neue Datei wird angelegt
- zum Schreiben: neue Datei wird angelegt
Maximal FOPEN_MAX Dateien können gleichzeitig geöffnet
werden, maximale Dateinamenlänge: FILENAME_MAX.
Zwischen Lesen und Schreiben ist ein Aufruf von fflush() oder
ein Positeonierungsvorgang nötig.
- [14], [19], [26] formatierte Ausgabe auf Datei:
fprintf(<Dateizeiger>, "<Format>",<Werte>);
Entspricht der Funktion printf und schreibt die
angegebenen Werte im angegebenen Format auf die Datei.
Dateizeiger verweist auf die Datei, auf die geschrieben wird.
fprintf ist definiert als
int fprintf(FILE*, const char *format, ...)
- [32] formatierte Eingabe von Datei:
fscanf(<Dateizeiger>, "<Format>",<Werte>);
Entspricht der Funktion scanf und liest die angegebenen
Werte im vereinbarten Format der Datei.
fscanf ist definiert als
int fscanf(FILE*, const char *format, ...)
- [30] Dateiende abfragen:
feof(<dateizeiger>);
Die Funktion feof liefert den Integerwert 1, wenn das Dateiende
gelesen wurde, sonst 0.(int feof(FILE*))
- [20], [35] Schließen einer Datei:
fclose(<Dateizeiger>);
Die Datei wird geschlossen, vom Programm abgehängt
und der Platz für den Filebeschreibungsblock wieder freigegeben.
Beim Schreiben auf Dateien sollte die Datei geschlossen
werden, sobald alle Schreiboperationen abgschlossen sind, da erst
beim Schließen die Dateipuffer auf die Platte geschrieben und
die Informationen über die Datei in der Dateiverwaltung aktualisert
werden. fclose ist definiert als int fclose(FILE*).
Im 2. Programmbeispiel werden von der Datei "daten.dat" Datenzeilen gelesen.
Jede Zeile enthält einen Integerwert, einen Double-Wert und einen Textstring.
Diese Daten werden dann mit einer vorangestellten Zeilennummer in die Datei
"tabelle.txt" geschrieben.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char msg[100];
int n, i;
double d;
FILE *fpin, *fpout;
/* Dateien oeffnen */
fpin = fopen("daten.dat", "rt");
fpout = fopen("tabelle.txt", "wt");
if((fpin == NULL) || (fpout == NULL))
{ /* ... hat nicht funktioniert */
fprintf(stderr, "Fehler: I/O");
exit(1);
}
/* jetzt neue Datei mit anlegen, mit den Wertesaetzen zeilenweise
und mit Zeilennummern versehen */
i = 1;
for(;;) /* Endlosschleife */
{
fscanf(fpin, "%d%lf%s", &n, &d, msg);
if(feof(fpin)) break; /* Dateiende gelesen, sofort aufhoeren! */
fprintf(fpout, "%d> %s %d %f\n", i++, msg, n, d);
}
/* Dateien schliessen */
fclose(fpout);
fclose(fpin);
return 0;
}
Ein weiteres Beispiel: Programm zum Kopieren einer Datei. Das Programm hat zwei
Kommandozeilen-Parameter, die Namen von Quell- und Zieldatei. In diesem Beispiel
wird besonders auf die Fehlerbehandlung eingegangen. Wenn etwas schief geht,
interessiert uns die Ursache, die bei vielen Funktionen als Nummer in
errno und als englischer Text in strerror(errno)
hinterlegt sind. Deshalb wird die Headerdatei errno.h eingebunden.
Sämtliche Funktionen, deren Ergebniswerte nicht mehr interessieren, sind
mit einem Cast nach void versehen:
- Wenn fprintf(stderr,...) nicht mehr funktioniert,
hilft sowieso nichts mehr.
- Wenn es schon einen anderen Fehler gegeben hat,
interessiert es nicht mehr, ob die Dateien noch
geschlossen werden können.
Sie sehen, in C sollte man sehr, sehr sorgfältig alle Ergebniswerte
korrigieren, dies ist häufig viel aufwendiger als der Teil, der die
gewünschten Aktionen durchführt.
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
FILE *infile,*outfile;
int c,i;
if(argc != 3)
{
(void)fprintf(stderr,
"Aufruf: %s quelldatei zieldatei\n", argv[0]);
return(EXIT_FAILURE);
}
infile = fopen(argv[1],"rb");
if(infile == NULL)
{
(void)fprintf(stderr,
"Fehler beim Öffnen der Datei %s: %s\n",
argv[1],strerror(errno));
return(EXIT_FAILURE);
}
outfile = fopen(argv[2],"wb");
if(outfile == NULL)
{
(void)fprintf(stderr,
"Fehler beim Erzeugen der Datei %s: %s\n",
argv[2], strerror(errno));
(void)fclose(infile);
return(EXIT_FAILURE);
}
while((c = getc(infile)) != EOF)
if(putc(c,outfile) == EOF)
{
(void)fprintf(stderr,
"Fehler beim Schreiben der Datei %s: %s\n",
argv[2], strerror(errno));
(void)fclose(infile);
(void)fclose(outfile);
return(EXIT_FAILURE);
}
if(ferror(infile))
{
(void)fprintf(stderr,
"Fehler beim Lesen der Datei %s: %s\n",
argv[1], strerror(errno));
(void)fclose(infile);
(void)fclose(outfile);
return(EXIT_FAILURE);
}
if(fclose(infile) == EOF)
{
(void)fprintf(stderr,
"Fehler beim Schließen der Datei %s\n", argv[1]);
(void)fclose(outfile);
return(EXIT_FAILURE);
}
if(fclose(outfile) == EOF)
{
(void)fprintf(stderr,
"Fehler beim Schließen der Datei %s\n", argv[2]);
return(EXIT_FAILURE);
}
return(EXIT_SUCCESS);
}
|
|
|