|
zwischen verschiedenen Rechner- bzw. Betriebssystemen zu konvertieren.
Gepufferte E/A vs. ungepufferte E/A
Wie bei den meisten Betriebssystemen wird auch bei DOS oder UNIX die Ausgabe
gepuffert. Das heißt, es wird erst etwas ausgegeben, wenn ein Zeilenende an das
Ausgabegerät gesendet wird, oder gar erst, wenn das Programm zuende ist.
Falls Sie das nicht möchten, müssen also dafür sorgen, daß nach
jedem Zeichen wirklich auch eine Bildschirmausgabe erfolgt. Sie erreichen dies durch
den Funktionsaufruf fflush(stdout); nach jeder Ausgabe.
Die Funktion fflush (FILE *datei) sorgt für sofortiges Wegschreiben
des internen Dateipuffers.
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");
Weitere Dateifunktionen für Textdateien
- int putc(int c, FILE *f) und int fputc(int c, FILE *f)
Schreiben ein Zeichen in die Datei. Sie arbeiten wie putchar().
putc() kann ein Makro sein.
- int getc(FILE *f) und int fgetc(FILE *f)
Lesen ein Zeichen aus der Datei. Sie arbeiten wie getchar().
(getc() kann Makro sein)
- int puts(const char *s) und int fputs(const char *s, FILE *f)
Schreiben eine Zeichenkette in die Datei. s ist ein Zeiger (Verweis) auf
die Zeichenkette. Die Funktionen liefern ok (0) oder EOF.
puts() ergänzt die Zeichenkette um ein Zeilenende-Zeichen (newline).
- char *gets(char *s)
Liest eine Zeichenkette aus der Datei. Die Funktion liefert entweder die Zeichenkette
s oder NULL, falls das Dateiende erreicht wurde. Bei einem Lesefehler
wird ebenfalls NULL zurückgegeben. gets() legt statt des
Newline-Zeichens (\n) ein Nullbyte am Stringende ab.
- char *fgets(char *s, int n, FILE *f)
Liest eine Zeichenkette aus der Datei. Die Funktion liefert entweder die Zeichenkette
s oder NULL, falls das Dateiende erreicht wurde. Es werden jedoch
maximal n-1 Zeichen gelesen. fgets() legt ein Nullbyte am Stringende ab.
- int ungetc(int c, FILE *f)
Diese Funktion stellt das Zeichen c in den Eingabepuffer zurück. Es kann
dann mit dem nächsten getchar(), getc() oder fgetc()
wieder gelesen werden. ungetc()) liefert entweder das Zeichen c oder EOF als
Status zurück.
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:
- i Groß-/Kleinschreibung nicht beachten
- l nur Dateinamen ausgeben
- c Zeilen zählen
- h kein Dateinamen/Zeilennummer ausgeben
- n Zeilennummer ausgeben
- v alle Zeilen ausgeben, die nicht übereinstimmen
Die Optionen werden durch ein '-'-Zeichen eingeleitet, um sie im Programm vom Suchbegriff
und Dateinamen unterscheiden zu können, also z. B. "-i" oder "-i -c".
Die Kommandozeile hat folgenden Aufbau:
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);
}
|
|
|