Die Datei hat den Namen hauptst.csv und enthält die Felder Staat, Hauptstadt, Einwohner, Stand und Quelle. Wir wollen daraus eine Datei erstellen, die zu jeder Hauptstadt die Anzahl ihrer Einwohner im Format <Hauptstadt>: <Anzahl Einwohner> ausgibt.
Dazu öffnen wir zunächst die Eingabe- und die Ausgabedatei. Dann lesen wir in einer Schleife Zeile für Zeile ein, verarbeiten die Zeile und schreiben das Ergebnis in die Ausgabedatei. Zuletzt schließen wir beide Dateien.
Beginnen wir mit der Funktion main(), wo wir zuerst die benötigten Variablen deklarieren:
Code: Alles auswählen
/* Main
*/
int main(void) {
char filename[13];
char filenameOut[13];
FILE *fp;
FILE *fpOut;
char buf[100];
t_line line;
Code: Alles auswählen
typedef struct {
char staat[60];
char hauptstadt[50];
char einwohner[12];
char stand[10];
char quelle[40];
} t_line;
Wir öffnen die Eingabedatei:
Code: Alles auswählen
strcpy(filename, "hauptst.csv");
fp = fopen(filename, "r");
if (fp == NULL) {
printf("Fehler: Eingabedatei %s nicht gefunden!\n", filename);
return -1;
}
Die Ausgabedatei wird ganz ähnlich geöffnet:
Code: Alles auswählen
strcpy(filenameOut, "extrakt.csv");
fpOut = fopen(filenameOut, "w");
if (fpOut == NULL) {
printf("Fehler: Konnte Ausgabedatei %s nicht oeffnen!\n", filenameOut);
fclose(fp);
return -1;
}
Die Schleife für das Lesen der Zeilen sieht folgendermaßen aus:
Code: Alles auswählen
// Zeilen lesen
while (fgets(buf, sizeof buf, fp) != NULL) {
printf(".");
// Zeile in Felder aufteilen
parseLine(buf, &line);
// Hauptstadt und Einwohner schreiben
fprintf(fpOut, "%s: %s\r", line.hauptstadt, line.einwohner);
}
putchar('\n');
Die Ausgabe eines Punkts pro Zeile soll nur etwas visuelles Feedback für das Lesen der Zeilen geben. Die Funktion parseLine soll dann die Zeile in die Felder Staat, Hauptstadt, Einwohner, Stand und Quelle aufteilen und das Ergebnis in die Struktur line schreiben. Der typedef für die Struktur sieht so aus:
Code: Alles auswählen
typedef struct {
char staat[60];
char hauptstadt[50];
char einwohner[12];
char stand[10];
char quelle[40];
} t_line;
Am Ende von main() müssen wir noch die Dateien schließen:
Code: Alles auswählen
fclose(fp);
fclose(fpOut);
return 0;
}
Code: Alles auswählen
/* Zerlegt eine Zeile in Felder.
*/
char *parseLine(char *buf, t_line *line) {
int i;
int len;
initLine(line);
buf = parseToken(buf, line->staat, ';');
buf = parseToken(buf, line->hauptstadt, ';');
buf = parseToken(buf, line->einwohner, ';');
buf = parseToken(buf, line->stand, ';');
buf = parseToken(buf, line->quelle, ';');
}
Code: Alles auswählen
void initLine(t_line *line) {
line->staat[0] = '\0';
line->hauptstadt[0] = '\0';
line->einwohner[0] = '\0';
line->stand[0] = '\0';
line->quelle[0] = '\0';
}
Code: Alles auswählen
/* Liest ein Feld bis zum Trennzeichen.
* Gibt einen Zeiger auf das naechste Feld zurueck.
*/
char *parseToken(char *start, char *field, char delimiter) {
while (*start != delimiter && *start != '\0') {
*field++ = *start++;
}
*field = '\0';
return ++start;
}
Einige Dinge hätte man auch anders machen können. So wäre es z.B. möglich ohne Kopieren der Strings auszukommen, indem man '\0'-Zeichen in den Puffer schreibt, wo die Trennzeichen sind. Oder man hätte mit strtok arbeiten können - eine String-Funktion, die Tokens anhand von Trennzeichen ermittelt. Diese hat allerdings den Nachteil, dass sie aufeinanderfolgende Trennzeichen wie eines behandelt. Das taugt nichts, wenn Felder auch leer sein können.
Auch die Struktur wäre nicht unbedingt nötig. Man könnte stattdessen mit separaten Array-Variablen arbeiten, die nicht zu einer Struktur zusammengefasst sind. Dann hätte man allerdings auch alle Felder einzeln als Parameter an die Unterfunktionen übermitteln müssen - oder man hätte die Arrays gleich global gemacht. Letzteres ist aber wegen des "information hiding"-Prinzips in der Software-Entwicklung nicht wünschenswert.
Hier ist noch das fertige C-Programm und die Eingabedatei zum Download:
Das ganze wird kompiliert mit
Code: Alles auswählen
zcc +cpm -subtype=pcw80 file2.c -o file2.com