Inhalt • • • • • • • • Dateibehandlung Soll ein C-Programm eine Datei (lesend oder schreibend) nutzen, so werden verschiedene Funktionen der stdio.lib benötigt ➔ stdio.h mit include einbinden Einführung Elementare Datentypen und Anweisungen Felder und Zeiger Funktionen Strukturen Deklarationen, Speicherklassen Zunächst muss die gewünschte Datei geöffnet werden ➔ Funktion fopen aus der stdio.lib Beim Öffnen der Datei muß angegeben werden, ob sie zum Lesen oder zum Schreiben geöffnet werden soll Modus, der der Funktion fopen als Parameter übergeben wird ➔ Ein- und Ausgabe Sonstiges Zum Lesen und Schreiben stehen in der stdio.lib verschiedene Funktionen zur Verfügung ➔ Funktionen fgetc, fputc, fscanf, fprintf, ... Abschließend muss die Datei wieder geschlossen werden ➔ Funktion fclose aus der stdio.lib 209 210 fopen Mögliche Angaben zum Modus FILE *fopen(const char *filename, const char *mode) "r" "w" "a" "rb" "wb" "ab" Rückgabewert der Funktion ist FILE*, also ein Zeiger auf eine Struktur namens FILE, die in der stdio.lib definiert ist. Der Rückgabewert ist NULL, falls die Datei nicht geöffnet werden konnte. Vom Programm aus kann nur über diesen Pointer auf die Datei zugegriffen werden! ASCII-Datei zum Lesen öffnen ASCII-Datei zum Schreiben öffnen ASCII-Datei zum Erweitern (Schreiben) öffnen Binär-Datei zum Lesen öffnen Binär-Datei zum Schreiben öffnen Binär-Datei zum Erweitern (Schreiben) öffnen Achtung! Beim Schreiben mittels wird der alte Inhalt der Datei überschrieben! Beim Schreiben mittels wird an das alte Datei-Ende angehängt. filename ist der Name der zu öffnenden Datei, in der Notation des jeweiligen Systems (z.B. Unix: "/tmp/datei.dat", Windows: "C:\tmp\datei.dat") Beide Modi erzeugen eine neue Datei, wenn es keine Datei des angegebenen Namens gibt. mode ist String, der den Modus der Operation bestimmt. Es gibt weitere Modi. Wir werden im folgenden nur ASCII-Dateien (Text-files) benutzen. 211 212 Zeichenorientiertes Lesen und Schreiben fclose int fgetc(FILE *f); Liest nächstes Zeichen und liefert es als int-Wert (ASCII-Code!) zurück. Am Dateiende wird EOF zurückgeliefert. (EOF ist kein char, deshalb ist der Rückgabetyp der Funktion int) int fclose(FILE *f); int fputc(char c, FILE *f); Schreibt Zeichen. Liefert den ASCII-Code des Zeichens als Rückgabewert oder EOF bei Fehler. Der FILE-Pointer f hat anschließend einen undefinierten Wert. Schließt die Datei. Liefert 0, falls kein Fehler auftrat. char *fgets(char *s, int n, FILE *f); Höchstens n-1 Zeichen werden aus einer Zeile eingelesen und mit \0 abgeschlossen. Zurückgegeben werden diese Zeichen als String s oder NULL, wenn das Lesen nicht erfolgreich war. int fputs(const char *s, FILE *f); Schreibt s in Datei. Liefert EOF bei Fehler, sonst nichtnegativen Wert. 213 214 Beispiel Datei1.c Beispiel Datei2.c // Datei2.c // Datei1.c // liest aus der Datei dat1.txt // schreibt in die Datei dat1.txt #include <stdio.h> #include <stdio.h> int main (int argc, char ** argv) { int main (int argc, char ** argv) { FILE *f_in; int i; char c; FILE *f_out; f_out = fopen("dat1.txt", "w"); // Erfolg von fopen sollte immer abgefragt werden if (f_out != NULL) { fputc('a', f_out); fputs("bc", f_out); } f_in = fopen("dat1.txt", "r"); if (f_in != NULL) do { i = fgetc(f_in); c = (char) i; // Kontrollausgabe des gelesenen Zeichens printf("i: %d c: %c\n", i, c); }while(i != EOF); fclose(f_out); } fclose(f_in); } 215 216 Exkurs: Ausdrücke und Anweisungen Ausdrücke als Anweisung In C ist es aber auch erlaubt, einen Ausdruck als Anweisung zu formulieren. Bisher haben wir deutlich unterschieden zwischen Ausdrücken und Anweisungen. Beispiele: 3*(sin(y) + 7); printf(); Ausdrücke (logische oder arithmetische) werden ausgewertet und liefern ein Resultat. Logische Ausdrücke treten z.B. als Bedingungen in if-statements oder Schleifen auf, arithmetische Ausdrücke z.B. als rechte Seite von Wertzuweisungen: if (i < 0) { }; x = 3*(sin(y) + 7); Ein solcher Ausdruck wird ausgewertet und der Ergebniswert wird ignoriert. Anweisungen werden ausgeführt und haben eine Wirkung. Beispiele sind Wertzuweisung, if, Schleifen oder Funktionsaufrufe: if (i < 0) { }; x = 3*(sin(y) + 7); drucke(feld, n); Beide Aufrufe sind sowohl Ausdruck als auch Anweisung, genauer: Ausdrucksanweisung. Die von uns definierte Funktion drucke(int[], int) war als Funktion mit Rückgabetyp void definiert, printf() ist aber eine Funktion der stdio.lib, die dort mit Rückgabetyp int definiert ist. Diesen Rückgabewert der Funktion haben wir bisher immer ignoriert. Wieso ist printf(); ein Ausdruck? Wieso haben wir eben drucke(feld, n); als Anweisung bezeichnet? 217 218 Zuweisung als Ausdruck Anwendung Die Wertzuweisung hat nicht nur eine Wirkung (belegt einen Speicherplatz mit einem Wert), sondern liefert selbst einen Wert zurück (nämlich den Wert des Ausdrucks der rechten Seite). Dieser Wert kann dann in einem umfassenden Ausdruck verwendet werden: if ( (i = n/4) > 0 ) { if ( (i = n/4) > 0 ) { }; // statt Achtung! i = n/4; if ( i > 0 ) { oder: }; if( (iptr = (int*) malloc (8)) != NULL ) {*iptr = ...} // statt iptr = (int*) malloc (8); if (iptr!= NULL) {*iptr = ...} }; ist nicht identisch mit if ( i = (n/4 > 0) ) { Beispiel: }; Die Variable i wird mit dem Wert n/4 belegt, dieser Wert wird dann auf > 0 geprüft. if ( (i = n/4) > 0 ) { Durch diese doppelte Interpretation kann die Zuweisung eines Funktionswertes an eine Variable und die Abfrage dieses Wertes in einem Schritt zusammengefasst werden: }; 219 220 Ende des Exkurses Beispiel Datei1a.c // Datei1a.c Viele Funktionen in C liefern einen Fehlercode als int-Ergebnis zurück und verstecken die eigentliche Berechnung als „Nebeneffekt“. // schreibt in die Datei dat1.txt #include <stdio.h> Beispiele: printf, scanf, fclose, fgetc, fputc, ... Für solche Funktionen ist es üblich, den Aufruf mit der Fehlercodeabfrage zu verknüpfen. int main (int argc, char ** argv) { FILE *f_out; if ( (f_out = fopen("dat1.txt", "w")) != NULL) { fputc('a', f_out); fputs("bc", f_out); } -> folgende Beispiele fclose(f_out); } 221 Beispiel Datei2a.c 222 formatierte Ein- und Ausgabe int fprintf(FILE *f, char *format, ...); int fscanf(FILE *f, char *format, ...); // Datei2a.c // liest aus der Datei dat1.txt werden genau wie printf und scanf benutzt, mit dem Unterschied, dass der erste Parameter jeweils ein FILE-Pointer ist, der auf die Datei zeigt, in die geschrieben, bzw. von der gelesen werden soll. #include <stdio.h> int main (int argc, char ** argv) { FILE *f_in; int i; char c; Rückgabewert ist bei fscanf und scanf die Anzahl der gelesenen Werte (nicht Zeichen), bei fprintf und printf die Anzahl der geschriebenen Zeichen. if ( (f_in = fopen("dat1.txt", "r"))!= NULL ) while ( (i = fgetc(f_in)) != EOF ) { c = (char) i; // Kontrollausgabe des gelesenen Zeichens printf("i: %d c: %c\n", i, c); } Der zweite Parameter ist die Formatangabe. fclose(f_in); } 223 Formatbezeichner %d %o %x %ld %f %e %c %s %% 224 Übung Integer dezimal mit Vorzeichen Integer oktal ohne Vorzeichen Integer hexadezimal ohne Vorzeichen long int mit Vorzeichen float oder double ohne Benutzung von Zehnerpotenzen float oder double im Exponentionalformat einzelnes Zeichen String das %-Zeichen Aufgaben des dreizehnten Übungsblattes. Auch Breite und Genauigkeit der Ausgabe können angegeben werden, z.B.: %6d mindestens 6 Stellen, rechtsbündig %12.3f insgesamt mindestens 12 Stellen (incl. Vorzeichen und Dezimalpunkt), davon 3 Nachkommastellen 225 226