![]() |
Algorithmen & Datenstrukturen
|
Eine Datei besitzt also lauter Komponenten gleichen Typs. Zusammen mit der Dateivariable wird implizit auch ein Pufferbereich im Arbeitsspeicher definiert, der mindestens eine Dateikomponente (--> aktueller Wert) aufnehmen kann. Auf diesen Datenwert wird dann über die Dateivariable zugegriffen. Zusammen mit dem Typ "Datei" müssen einige Standardoperationen definiert sein, die den Zugriff auf die Datei erlauben:
| Programmname < Dateiname |
| Dieses Kommando bewirkt, daß das Programm von der angegebenen 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;
}
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:
FILE *fopen(const char *filename, const char *modus)
als Zugriffsmodus steht zur Verfügung eine Kombination von "a", "r", "w" und "+":
Die Funktion fopen reagiert folgendermaßen:
int fprintf(FILE*, const char *format, ...)
int fscanf(FILE*, const char *format, ...)
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:
#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);
}
Auch die Eingabe wird wie die Ausgabe gepuffert. Das heißt, es wird beim Aufruf von getchar() gewartet, bis nach dem Zeichen die Enter-Taste gedrückt wird. Um das zu vermeiden (wenn z. B. nur ein einziger Tastendruck erfolgen soll), muß das System in den ungepufferten Betrieb geschaltet werden. Sie erreichen dies unter UNIX durch Umschalten des Terminals in den ungepufferten (raw-)Modus. Das kann durch den Aufruf des Systemkommandos stty erreicht werden:
system ("stty raw");
Will man zusätzlich verhindern, daß das eingegebene Zeichen vom Betriebssystem geechot wird, kann man den Aufruf erweitern:
system ("stty raw -echo");
Danach wird das eingegebene Zeichen auch nicht mehr automatisch auf dem Bildschirm geechot, sondern erst durch die Ausgabe in Ihrem Programm. Auf diese Weise lassen sich beispielsweise auch Passworteingaben realisieren oder es kann verhindert werden, daß die Eingabe eine Bildschirmmaske verunstaltet. Der Funktionsaufruf fflush(stdout); nach jeder Ausgabe kann dann auch entfallen. Bevor das Programm beendet wird, müssen Sie jedoch wieder in den "Normalmodus" zurückschalten:
system ("stty -raw");
bzw.
system ("stty -raw echo");
Beispiel: Das folgende Programm macht in Textdateien solche Zeichen
sichtbar, die normalerweise nicht nicht sichtbar sind, nälich Steuerzeichen
mit den ASCII-Codes 0 bis 31. Diese Zeichen werden Hexadezimal ausgegeben und
als Markierung ein '\' davor, so würde das Formfeed-Zeichen als '\0C' ausgegeben.
Die Zeichen "Tabulator", "Newline" und das Leerzeichen bleiben unverändert,
die Zeilenstruktur der Datei bleibt also erhalten.
Auf der Kommandozeile können beliebig viele Dateinamen angegeben werden.
Bleibt die Kommandozeile leer, wird automatisch von der Standardeingabe gelesen.
#include <stdio.h>
#include <stdlib.h>
void vis(FILE *fp);
main(int argc, char *argv[])
{
int i; /* Zaehler fuer Parameter */
FILE *fp; /* Eingabedatei */
if (argc == 1) /* keine Datei angegeben */
vis(stdin);
else /* Dateiliste abarbeiten */
{
for (i = 1; i < argc; i++)
if ((fp=fopen(argv[i], "r")) == NULL)
{
fprintf(stderr, "Can't open %s\n", argv[1]);
exit(1);
}
else
{
vis(fp);
fclose(fp);
}
}
exit(0);
}
void vis(FILE *fp)
{
int c;
while ((c = getc(fp)) != EOF)
if (isascii(c) &&
(isprint(c) || c=='\n' || c=='\t' || c==' '))
putchar(c);
else
printf("\\%02x", c);
exit(0);
}
Als nächstes schreiben wir ein Programm, das den Mittelwert aus den eingelesenen Zahlen bildet:
#include <stdio.h>
#include <stdlib.h>
main(int argc, char *argv[])
{
double x; /* Eingabe-Werte */
int r; /* Rueckgabewert von fscanf */
int n; /* Anzahl der Werte */
double sx; /* in sx werden die eingelesenen Werte aufsummiert */
double mx; /* Mittelwert der x-Werte */
int i; /* Zaehler fuer Parameter */
FILE *fp; /* Eingabedatei */
if (argc == 1) /* keine Datei angegeben */
{
fprintf(stderr, "Keine Datei angegeben!\n");
exit(1);
}
else /* Dateiliste abarbeiten */
{
for (i = 1; i < argc; i++)
if ((fp=fopen(argv[i], "r")) == NULL)
{
fprintf(stderr, "Can't open %s\n", argv[i]);
exit(1);
}
else /* Datei bearbeiten */
{
n = 0; /* Zaehler und Summe auf Null setzen */
sx = 0;
for(;;)
{
r = fscanf (stdin,"%F",&x); /* Wert einlesen */
if (r == EOF) break; /* Dateiende -- fertig */
n = n + 1;
sx = sx + x;
}
mx = sx/n;
fprintf(stdout,"Datei: %s\n", argv[i]);
fprintf(stdout," Anzahl der verarbeiteten Werte: %d\n",n);
fprintf(stdout," Mittelwert: %f\n",mx);
fclose(fp);
}
}
exit(0);
}
Aufgabe: Erweitern Sie das Mittelwert-Programm so, daß gleichzeitig das Minimum und das Maximum der eingelesenen Zahlen ermittelt und ausgegeben wird.
Oft braucht man ein Programm, daß verschiedene Dateien nach einer Zeichenfolge durchsucht. In UNIX gibt es dazu das recht mächtige Tool grep und seine "Verwandten" egrep und fgrep. Hier soll eine einfache Variante vorgestellt werden, die mgrep heiße. Das Programm kann diverse Optionen verarbeiten:
mgrep <Optionen> <Suchbegriff> <Dateiname(n)>
wobei die Optionen auch fehlen dürfen, es werden dann Voreinstellungen verwendet. Der Teil des Programms, der die Kommandozeile auswertet, ist etwa genau so lang, wie der eigentliche Suchteil.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXLINE 1024 /* max. Zeilenlänge */
#define TRUE 1
#define FALSE 0
char upper = FALSE; /* Option i */
char count = FALSE; /* Option c */
char fnonly = FALSE; /* Option l */
char linen = FALSE; /* Option n */
char header = TRUE; /* Option h */
char nomatch = FALSE; /* Option v */
void usage(void)
{
printf("Aufruf: mgrep [<Optionen>] <Suchmuster> <Datei(en)> \n\n");
printf("Optionen: i Groß-/Kleinschreibung nicht beachten\n");
printf(" l nur Dateinamen ausgeben\n");
printf(" c Zeilen zählen\n");
printf(" h kein Dateinamen/Zeilennummer ausgeben\n");
printf(" n Zeilennummer ausgeben\n");
printf(" v alle Zeilen ausgeben, die nicht übereinstimmen\n\n");
exit(1);
}
void delete(char *str,int pos, int n)
/* n Zeichen aus str ab pos löschen */
{
int i = pos + n;
while ((str[pos++] = str[i++]) != '\0');
}
void del_spaces(char *line)
/* Führende Leerzeichen (Space und Tab) entfernen */
{
while (line[0]==' ' || line[0]==0x09) delete(line, 0, 1);
}
FILE *open_file(char *name)
/* Datei öffnen */
{
FILE *fp;
if ((fp=fopen(name,"r")) == 0)
{
fprintf(stderr,"Datei %s kann nicht geöffnet werden",name);
exit(1);
}
return fp;
}
char strstrc(char *str1, char *str2)
/* Feststellen, ob str2 in str1 enthalten ist */
/* In Abhängigkeit von upper Groß-/Kleinschreibung */
/* beachten bzw. nicht beachten */
{
if (upper)
{
int i, n, len;
n = strlen(str2);
len = strlen(str1) - n;
for (i = 0; i <= len; i++)
if (strnicmp(&str1[i], str2, n) == NULL) return TRUE;
}
return strstr(str1, str2) != NULL;
}
int main(int argc, char *argv[])
{
char line[MAXLINE], search[MAXLINE], name[120], match;
int i, lineno, cnt = 0;
FILE *fp;
if (argc < 3) usage(); /* falsche Parameteranzahl */
/* zuerst die Optionen bearbeiten */
while (argc > 2 && argv[1][0] == '-')
{
printf("Argument: %s\n",argv[1]);
switch (argv[1][1])
{
case 'i':
case 'I': upper = TRUE; break;
case 'n':
case 'N': linen = TRUE; break;
case 'c':
case 'C': count = TRUE; break;
case 'l':
case 'L': fnonly = TRUE; break;
case 'h':
case 'H': header = FALSE; break;
case 'v':
case 'V': nomatch = TRUE; break;
default: usage(); exit(1);
}
argc--;
argv++;
}
/* jetzt sollte noch Suchbegriff und Dateiname da sein */
if (argc < 2)
{
usage();
exit(1);
}
/* suchbegriff uebernehmen */
strcpy(search, argv[1]);
argc--;
argv++;
/* Text in allen Dateien suchen */
for (i = 1; i < argc; i++)
{
lineno=0;
strcpy(name, argv[i]);
printf("Durchsuchen: %s\n",name);
fp = open_file(name);
while (fgets(line, MAXLINE, fp) != NULL)
{
if (((match = strstrc(line, search)) == TRUE
&& !nomatch) || (!match && nomatch))
{
cnt++;
if (!count)
{
if (header)
{
printf("%s ", name);
if (fnonly)
{
putchar('\n');
break;
}
if (linen) printf("%04d", lineno);
putchar(':');
}
del_spaces(line);
printf("%s", line);
}
}
lineno++;
}
fclose(fp);
}
if (count) printf("%d Zeilen gefunden",cnt);
return 0;
}
Wenn ASCII-Zeichen auf eine Zeichenkette eingelesen werden, muß gegebenenfalls eine Umwandlung in eine Zahl erfolgen. Die folgende Funktion atof() leistet das Gewünschte:
double atof(char s[])
/* Zeichenkette s nach double wandeln */
{
double val, power;
int i, sign;
for (i = 0; s[i] == ' ' || s[i] == '\n' || s[i] == '\t'; i++)
; /* Zwischenraum uebergehen */
sign = 1;
if (s[i] == '+' || s[i] == '-') /* Vorzeichen */
if (s[i++] == '-') sign = -1;
for (val = 0; s[i] >= '0' && s[i] <= '9'; i++)
val = 10 * val + (s[i] - (int)'0');
if s[i] == '.') i++;
for (power = 1, s[i] >= '0' && s[i] <= '9'; i++)
{
val = 10*val + (s[i] - (int)'0');
power = power*10;
}
return (sign*val/power);
}
Die Funktion fwrite schreibt eine angegebene Anzahl von Datenelementen gleicher Größe in eine Datei. Übergeben werden muss:
size_t fwrite(const void *pt, size_t size, size_t n, FILE *f)
Die auszugebenden Daten müssen zusammenhängend im Speicher stehen. Dies ist bei Vektoren stets der Fall, ebenso wie bei Speicherplatz, der durch einen einzelnen malloc()-Aufruf (siehe Zeiger) zur Verfügung gestellt wurde.
Beispielprogramm für binäres Schreiben:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int Feld[] = {3, 91, 2134, 6, 33, 267, 9123, -5, 22, 0};
FILE *pf;
char dateiname[] = "daten.bin"; /* Dateiname */
pf = fopen(dateiname, "wb");
if (pf == NULL)
{
printf("Fehler beim Oeffnen der Datei\n");
exit(1);
}
fwrite( (void *)Feld, sizeof(int), 10, pf);
/* Das Feld wird komplett auf einmal geschrieben */
fclose(pf);
}
Die Größe der Datei daten.bin ist also 10 * sizeof(int).
Die Funktion fread ist die zu fwrite gehörige analoge Einlesefunktion. Ihr Rückgabewert ist die Anzahl der tatsächlich gelesenen Bytes. Diese Zahl kann kleiner sein, als die Zahl der zu lesenden Bytes, wenn das Dateiende vorzeitig erreicht wurde. SIe ist definiert als:
int fread(void *ptr, size_t size, size_t n, FILE *f)
Das folgende Programm kopiert eine Datei unter Verwendung der beiden Funktionen fread() und fwrite(). Dabei wird ein char-Array als Puffer verwendet. Diese Kopiermethode ist wesentlich schneller als zeichenweises Kopieren. Das Dateiende wird dadurch erkannt, daß fread() den Wert 0 zurückliefert. fwrite() schreibt genau soviele Bytes, wie von fread() gelesen wurden.
#include <stdio.h>
#include <stdlib.h>
#define MAXBUF 32768 /* Puffergroesse */
int main(void)
{
char buffer[MAXBUF]; /* Kopierpuffer */
FILE *rf, *wf; /* Lesedatei, Schreibdatei */
int numread; /* Anzahl gelesener Zeichen */
char readfrom[] = "daten.bin"; /* Dateinamen */
char writeto[] = "daten.bak";
rf = fopen(readfrom, "rb");
if (rf == NULL)
{
printf("Fehler beim Oeffnen der Datei\n");
exit(1);
}
wf = fopen(writeto, "wb");
if (wf == NULL)
{
printf("Fehler beim Oeffnen der Datei\n");
exit(1);
}
do
{
numread = fread(buffer, sizeof(char), MAXBUF, rf);
fwrite( buffer, sizeof(char), numread, wf);
} while (numread > 0);
fclose(wf);
fclose(rf);
return 0;
}
int fseek(FILE *f, long offset, int origin)
Mit dieser Funktion kann man einen Zeiger auf eine bestimmte
Position innerhalb einer Datei setzen
Die Funktion positioniert um einen offset, der in Bytes gezählt wird.
Der Wert origin legt fest, worauf sich offset bezieht:
Die Funktion long ftell(FILE *f) gibt die aktuelle Position in der Datei an, auf die der Dateizeiger weist. Im Fehlerfall liefert ftell den Wert -1.
Die Funktion void rewind(FILE *f) positioniert auf den Dateianfang und löscht den Fehlerstatus.
Beispiel: Programm mit wahlfreiem Zugriff auf eine Datei
#include <stdio.h>
int main(void)
{
long pos;
int count;
FILE *fp;
int mode = 0;
char c;
fp = fopen("daten.bin","w+");
if (fp == NULL)
{
printf("Fehler beim Oeffnen der Datei\n");
exit(1);
}
/* Datei beschreiben */
fputs("abcdefghijklmnopqrstuvwxyz",fp);
puts("abcdefghijklmnopqrstuvwxyz");
printf("\n");
/* Wahlfreier Zugriff auf Datei */
printf("Eingabe der Position im File (0 bis 25):\n");
scanf("%ld",&pos);
fseek(fp,pos,mode);
pos = ftell(fp);
printf("Dateiposition ist %ld\n",pos);
fread(&c,1,1,fp);
printf("\nBuchstabe an dieser Position: %c\n\n",c);
fclose(fp);
return 0;
}
Beispiel: Hexdump
Der Inhalt einer Datei soll Hexadezimal und in ASCII ausgegeben werden.
Für die Bildschirmausgabe soll das Programm die Datei blockweise anzeigen,
jeweils 16 Zeilen. Der folgende Bildschirmabzug zeigt die Ausgabe:
Adresse 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 : 2F 2A 20 48 65 78 64 75 6D 70 20 65 69 6E 65 72 /* Hexdump einer
00000010 : 20 44 61 74 65 69 20 2A 2F 0D 0A 0D 0A 23 69 6E Datei */....#in
00000020 : 63 6C 75 64 65 20 3C 73 74 64 69 6F 2E 68 3E 0D clude <stdio.h>.
00000030 : 0A 23 69 6E 63 6C 75 64 65 20 3C 73 74 64 6C 69 .#include <stdli
00000040 : 62 2E 68 3E 0D 0A 0D 0A 23 64 65 66 69 6E 65 20 b.h>....#define
00000050 : 52 45 54 20 31 33 20 20 20 20 20 20 20 20 20 20 RET 13
00000060 : 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 2F /
00000070 : 2A 20 52 65 74 75 72 6E 2D 54 61 73 74 65 20 2A * Return-Taste *
00000080 : 2F 0D 0A 23 64 65 66 69 6E 65 20 42 45 45 50 20 /..#define BEEP
00000090 : 70 75 74 63 68 61 72 28 27 5C 30 37 27 29 20 20 putchar('\07')
000000A0 : 20 20 20 20 20 20 20 20 20 20 2F 2A 20 53 69 67 /* Sig
000000B0 : 6E 61 6C 74 6F 6E 20 61 75 73 67 65 62 65 6E 20 nalton ausgeben
000000C0 : 2A 2F 0D 0A 0D 0A 0D 0A 2F 2A 20 67 6C 6F 62 61 */....../* globa
000000D0 : 6C 65 20 56 61 72 69 61 62 6C 65 20 2A 2F 0D 0A le Variable */..
000000E0 : 46 49 4C 45 20 2A 65 69 6E 3B 20 20 20 20 20 20 FILE *ein;
000000F0 : 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
Adresse 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
Anmerkungen zum Programm:
#include <stdio.h>
#include <stdlib.h>
#define RET 13 /* Return-Taste */
#define BEEP putchar('\07') /* Signalton ausgeben */
/* globale Variable */
FILE *ein; /* Eingabedatei */
char name[64]; /* Dateiname */
int ende = 0; /* Dateiende erreicht? */
long position; /* aktuelle Position in 'ein' */
int puffer[16][16]; /* Puffer fuer eine Seite */
/* Funktions-Deklarationen */
void seitelesen(long position); /* Puffer aus Datei lesen */
void ausgabe(long position); /* Puffer darstellen */
/* Hauptprogramm */
int main(int argc, char *argv[])
{
int command; /* Bediener-Kommando */
if (argc < 2) /* Datei ueber Kommandozeile? */
{
printf("Dateiname: "); /* Dann ueber Tastatur */
scanf("%s",&name);
}
else
strcpy(name,argv[1]);
ein = fopen(name,"rb"); /* Eingabe eroeffnen */
if (ein == NULL)
{
printf("Eingabedatei kann nicht eroeffnet werden\n");
exit(1);
}
position = 0; /* Dateianfang */
for(;;) /* endlose */
{ /* Hauptschleife */
seitelesen(position);
ausgabe(position);
command = getchar(); /* Befehlstaste lesen */
switch (command)
{
case '+' : if (feof(ein) == 0) /* naechster Block */
position = position + 256;
else BEEP;
break;
case 'b' : /* vorhergehender Block */
case 'B' :
case '-' : if (position >= 256)
position = position - 256;
else BEEP;
break;
case 'Q' : /* Beenden */
case 'q' : fclose(ein); exit(0);
break;
default : BEEP; /* alle anderen Tasten */
} /* end switch */
} /* end for */
return 0;
}
/* Funktionsdefinitionen */
void seitelesen(long position) /* Puffer aus Datei lesen */
{
int i, j; /* Feldindexe */
int zeichen; /* gelesendes Zeichen */
fseek(ein,position,SEEK_SET); /* Pos. ab Dateianfang */
i = j = 0;
do
{ /* zeichenweise einlesen */
zeichen = getc(ein);
puffer[i][j] = zeichen;
j++;
if (j == 16) (i++, j = 0);
} /* bis Puffer voll oder EOF */
while (i*16+j < 256 && zeichen != EOF);
while (i*16+j < 256) /* Rest-Puffer auffuellen */
{
puffer[i][j] = -1;
j++;
if (j == 16) (i++,j = 0);
}
}
void ausgabe(long position) /* Puffer darstellen */
{
int i, j; /* Feldindexe */
printf("Dump von *** %s *** \n",name);
printf("\n");
printf("Adresse 0 1 2 3 4 5 6 7 8 9 A B C D E F");
printf(" 0123456789ABCDEF\n");
printf("\n");
for (i = 0; i < 16; i++) /* 16 Zeilen */
{
printf("%08lX : ",position);
for (j = 0; j < 16; j++) /* Inhalt hexadezimal */
if (puffer[i][j] < 0)
printf(" ");
else
printf(" %02X",puffer[i][j]);
printf(" ");
for (j = 0; j < 16; j++) /* Inhalt als ASCII-Zeichen */
if (puffer[i][j] > 31 && puffer[i][j] != 127)
printf("%c",puffer[i][j]);
else
if (puffer[i][j] < 0) /* Datei zuende - Blanks */
printf(" ");
else
printf("."); /* nicht druckbares Zeichen */
printf("\n");
position = position + 16;
}
printf("\n");
printf("Adresse 0 1 2 3 4 5 6 7 8 9 A B C D E F");
printf(" 0123456789ABCDEF\n");
printf("\n");
printf("Weiter mit <+> Zurueck mit <B>/<->");
printf(" Abbruch mit <Q>\n");
fflush(stdout);
}
#include <sys/types.h> /* manchmal benoetigt */ #include <dirent.h> /* Headerfile fuer die Verzeichnis-Funktionen */ DIR *opendir(const char *name); struct dirent *readdir(DIR *dir); int closedir(DIR *dir);
struct dirent
{
long d_ino; /* Inode Nummer */
off_t d_off; /* Offset zum naechsten dirent */
unsigned short d_reclen; /* Laenge dieses Eintrags */
char d_name[NAME_MAX+1]; /* Dateiname */
};
Für das Anwenderprogramm ist meist nur der Name des Eintrags interessant.
Will man mehr über diesen Eintrag erfahren, beispielsweise, ob es wieder ein
Verzeichnis ist, verwendet man andere Systemaufrufe, z. B. stat().
#include <sys/types.h>
#include <dirent.h>
int main(void)
{
DIR *dirHandle;
struct dirent * dirEntry;
dirHandle = opendir("."); /* oeffne aktuelles Verzeichnis */
if (dirHandle != NULL)
{
while ((dirEntry = readdir(dirHandle)) != NULL)
{
puts(dirEntry->d_name);
}
closedir(dirHandle);
}
return(0);
}
void rewinddir(DIR *dir);
#include <unistd.h> char * getcwd(char *namebuffer, size_t size);In einigen Systemen ist es zulässig, NULL als Parameter für namebuffer zu übergeben. Dann alloziiert getcwd() selbst den benötigten Speicher und gibt den Zeiger darauf zurück. Die Anwendung muß dann durch einen Aufruf von free() den Speicher wieder freigeben.
#include <unistd.h> int chdir(char *Pfad);Bei Erfolg gibt die Funktion 0, sonst -1 zurück. Die Fehlernummer findet sich in der Variablen errno.
#include <fcntl.h> #include <unistd.h> int mkdir(char *Pfadname, mode_t mode); int rmdir(char *Pfadname);Bei Erfolg geben die Funktionen 0, ansonsten -1 zurück. Die Fehlernummer findet sich in der Variablen errno.
Zum Inhaltsverzeichnis |
Zum nächsten Abschnitt |