Datei
Transcription
Datei
1 9 Dateiverarbeitung 2006 Pearson Education, Inc. All rights reserved. 2 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 9.10 9.11 Einführung Die Datenhierarchie Daten und Ströme Erzeugung einer sequenziellen Datei Daten von einer sequenziellen Datei lesen Eine sequenzielle Datei aktualisieren Dateien mit wahlfreiem Zugriff Erzeugung einer Datei mit wahlfreiem Zugriff Daten wahlfrei in eine Datei mit wahlfreiem Zugriff schreiben Sequenzielles Lesen aus einer Datei mit wahlfreiem Zugriff Fallstudie: Verarbeitung von Transaktionen 2006 Pearson Education, Inc. All rights reserved. 3 9.1 Einführung • Dateien – Werden verwendet, um Daten dauerhaft (‘persistent’) zu machen • Permanente Speicherung von großen Datenmengen – Speicherung auf sekundären Speichereinheiten • Magnetplatten • Halbleiterspeicher (Flash-Speicher) • Optische Speichermedien • Magnetbänder 2006 Pearson Education, Inc. All rights reserved. 4 9.2 Die Datenhierarchie • Bit – Darstellung von einem von zwei Zuständen, 0 oder 1 – Kleinste unterstützte Dateneinheit • Direkt in Hardwareschaltungen abgebildet • Zeichen – Ziffern, Buchstaben und spezielle Symbole • Aus Bits zusammengesetzt – Zeichensatz: Menge aller Zeichen, die auf einer bestimmten Plattform verwendet werden – chars werden in Bytes gespeichert (8 Bits) – wchar_ts werden in mindestens zwei Bytes gespeichert 2006 Pearson Education, Inc. All rights reserved. 5 9.2 Die Datenhierarchie • Feld – Aus Zeichen zusammengesetzt – Hat eine bestimmte Bedeutung – Beispiel • Groß- und Kleinbuchstaben können einen Namen darstellen • Datensatz (Record) – Aus mehreren Feldern zusammengesetzt – Wird in C als Struktur dargestellt – Beispiel • Datensatz für einen Angestellten könnte Personalnummer, Name, Adresse usw. enthalten – Ein Datensatz-Schlüssel ist ein Feld, das für alle Datensätze eindeutig ist. 2006 Pearson Education, Inc. All rights reserved. 6 9.2 Die Datenhierarchie • Datei – Aus einer Gruppe zusammengehöriger Datensätze zusammengesetzt – Beispiel: • Gehaltsdatei, die einen Datensatz für jeden Angestellten enthält – Unterschiedliche Varianten der Organisation von Datensätzen in einer Datei • Beispiel: Sequenzielle Datei – Datensätze werden nach einem Schlüssel sortiert nacheinander gespeichert • Datenbank – Aus einer Gruppe zusammengehöriger Dateien zusammengesetzt – Verwaltet durch eine Gruppe von Programmen, die zusammen Datenbank Management System (DBMS) genannt werden 2006 Pearson Education, Inc. All rights reserved. 7 Fig. 9.1 Datenhierarchie. 2006 Pearson Education, Inc. All rights reserved. 8 9.3 Dateien und Ströme • Datei – C betrachtet jede Datei als eine Folge von Bytes (Fig. 9.2). – Jede Datei endet entweder mit einem ‘end-of-file marker’ oder an einer speziellen Byte-Nummer, die in einer systeminternen, administrativen Datenstruktur steht. – Wenn eine Datei geöffnet wird, wird ein Strom (‘stream’) mit ihr verbunden. – Drei Dateien und die mit ihnen assoziierten Ströme werden automatisch geöffnet, wenn die Programmausführung beginnt: standard input, standard output und standard error. – Ströme stellen Kommunikationskanäle zwischen Dateien und Programmen zur Verfügung. 2006 Pearson Education, Inc. All rights reserved. 9 Fig. 9.2 | C-Sicht einer Datei mit n Bytes. 2006 Pearson Education, Inc. All rights reserved. 10 9.3 Dateien und Ströme • Der Standardeingabestrom ermöglicht es einem Programm Daten von der Tastatur zu lesen und der Standardausgabestrom ermöglicht es einem Programm Daten auf den Bildschirm zu schreiben. • Das Öffnen einer Datei gibt einen Zeiger auf eine FILE Struktur (definiert in <stdio.h>) zurück, die Informationen zur Bearbeitung der Datei enthält. • standard input, standard output und standard error werden über die Dateizeiger stdin, stdout und stderr angesprochen. 2006 Pearson Education, Inc. All rights reserved. 11 9.3 Dateien und Ströme • Die Standardbibliothek stellt viele Funktionen für das Lesen aus Dateien und das Schreiben in Dateien zur Verfügung. • Die Funktion fgetc liest ein einzelnes Zeichen aus einer Datei. Sie erwartet einen FILE Zeiger für die Datei, aus der das Zeichen gelesen werden soll, als Argument. • Der Aufruf fgetc( stdin ) liest ein Zeichen von stdin (der Standardeingabe). Dies ist äquivalent mit dem Aufruf getchar(). 2006 Pearson Education, Inc. All rights reserved. 12 9.3 Dateien und Ströme • Die Funktion fputc schreibt ein einzelnes Zeichen in eine Datei. Sie erwartet das zu schreibende Zeichen und einen FILE Zeiger für die Datei, in die das Zeichen geschrieben werden soll, als Argument. • Der Aufruf fputc( 'a', stdout ) schreibt das Zeichen 'a' nach stdout (der Standardeingabe). Dies ist äquivalent mit dem Aufruf putchar( 'a' ). 2006 Pearson Education, Inc. All rights reserved. 13 9.3 Dateien und Ströme • Viele andere Funktionen, die Daten von Ein- und Ausgabe lesen bzw. schreiben, haben gleichartig benannte Dateiverarbeitungsfunktionen. • So können beispielsweise die Funktionen fgets und fputs benutzt werden, um eine Zeile aus einer Datei zu lesen bzw. eine Zeile in eine Datei zu schreiben. • In den nächsten Abschnitten werden die dateiverarbeitenden Äquivalente der Funktionen scanf und printf verwendet: fscanf und fprintf. 2006 Pearson Education, Inc. All rights reserved. 14 9.4 Erzeugung einer sequenziellen Datei • Dateistruktur – Die Strukturierung von Dateien ist Aufgabe des Programmierers • C selbst strukturiert eine Datei in keiner Weise, sondern arbeitet mit einer reinen Folge von Bytes. 2006 Pearson Education, Inc. All rights reserved. 15 9.4 Erzeugung einer sequenziellen Datei • Öffnen einer Datei zur Ausgabe – Ein C Programm verwaltet jede Datei mit einer separaten FILE Struktur (deklariert in stdio.h). – Hierzu wird einer Variablen vom Typ FILE* durch die Funktion fopen ein Zeiger auf die FILE Struktur der zu öffnenden Datei zugewiesen. – fopen übernimmt zwei Argumente: Einen Dateinamen (einschließlich Pfadinformation zum Ort der Datei im Dateiverzeichnis) und einen Dateiöffnungs-Modus. – Der Dateiöffnungs-Modus "w" zeigt an, dass die Datei zum Schreiben geöffnet wird. – Falls die Datei nicht existiert und zum Schreiben geöffnet wird, erzeugt fopen die Datei. 2006 Pearson Education, Inc. All rights reserved. 16 Häufiger Programmierfehler Öffnet man eine existierende Datei zur Ausgabe (Dateiöffnungs-Modus "w"), wird der vorhandene Inhalt der Datei ohne Warnung verworfen. 2006 Pearson Education, Inc. All rights reserved. 17 Modus Beschreibung r w Öffnet eine vorhandene Datei zur Eingabe. Erzeugt eine Datei zur Ausgabe. Falls die Datei schon existiert, wird der vorhandene Inhalt verworfen. a Öffnet oder erzeugt eine Datei für die Ausgabe ans Ende der Datei. r+ w+ Öffnet eine vorhandene Datei zur Aktualisierung (Ein- und Ausgabe). Erzeugt eine Datei zur Aktualisierung. Falls die Datei schon existiert, wird der vorhandene Inhalt verworfen. Öffnet oder erzeugt eine Datei zur Aktualisierung. Geschrieben wird am Ende der Datei. a+ rb, wb, ab, rb+, wb+, ab+ Gleiche Funktionsweise wie oben beschrieben, jedoch nicht im Text-, sondern im Binärmodus. Fig. 9.5 | Dateiöffnungsarten. 2006 Pearson Education, Inc. All rights reserved. 18 9.4 Erzeugung einer sequenziellen Datei • Verwendung des FILE-Zeigers – In die Datei schreiben • Z.B. formatiertes Schreiben unter Verwendung der Funktion fprintf , die den FILE-Zeiger als erstes Argument übernimmt. – Datei schließen • Funktion fclose • Übernimmt den FILE-Zeiger als einziges Argument • Gibt die Datei wieder frei 2006 Pearson Education, Inc. All rights reserved. 19 Tipp zur Performanz Wenn ein Programm eine Datei nicht länger benötigt, sollte sie durch einen Aufruf von fclose explizit freigegeben werden. 2006 Pearson Education, Inc. All rights reserved. 20 9.4 Erzeugung einer sequenziellen Datei • Test auf Dateiende mit feof – Die Funktion feof testet, ob der end-of-file Indikator für die Datei, deren FILE-Zeiger sie übernimmt, gesetzt ist. – Der end-of-file Indikator informiert das Programm, dass keine Daten mehr zu verarbeiten sind. – Auch stdin kann auf end-of-file getestet werden; das ‘Dateiende’ wird dann durch eine spezielle Tastenkombination simuliert. – Fig. 9.6 zeigt die Tastenkombinationen um end-of-file einzulesen für verschiedene Betriebssysteme. 2006 Pearson Education, Inc. All rights reserved. 21 Plattform Tastenkombination UNIX/Linux/Mac OS X <Strg-d> (in einer eigenen Zeile) Microsoft Windows <Strg-z> (manchmal mit zusätzlichem Enter) VMS <Strg-z> Fig. 9.6 | Simulation der EOF-Markierung durch Tastatureingaben. 2006 Pearson Education, Inc. All rights reserved. 22 9.4 Erzeugung einer sequenziellen Datei • Formatiertes Schreiben mit fprintf – Zum formatierten Schreiben in eine Datei wird die Funktion fprintf verwendet. – fprintf ist äquivalent mit printf, bis auf die Tatsache, dass fprintf einen Zeiger auf die zu schreibende Datei als erstes Argument übernimmt. – Bei Erfolg gibt fprintf (wie auch printf) die Gesamtzahl der erfolgreich geschriebenen Zeichen zurück. – Im Fehlerfall wird der Fehlerindikator für den Dateistrom gesetzt und eine negative Zahl zurückgegeben. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 9.4: fig09_04.c 2 // Creating a sequential file. 3 #include <stdio.h> 4 #include <stdlib.h> // exit 5 #define NAME_SIZE 30 Outline 6 7 int main( void ) 8 { fig09_04.c (1 von 2) 9 unsigned int account; 10 char name[ NAME_SIZE ]; 11 12 double balance; 13 FILE* cfPtr = fopen( "clients.txt", "w" ); 14 if( cfPtr == NULL ) { Öffnen der Datei clients.txt zur Ausgabe fopen gibt NULL zurück, falls die Datei nicht erfolgreich geöffnet wurde 15 fputs( "File could not be opened", stderr ); 16 exit( 1 ); // exit program if unable to create file 17 23 } // end if 18 19 puts( "Enter the account, name, and balance." ); 20 printf( "%s", "Enter EOF to end input.\n? " ); 21 scanf( "%d%29s%lf", &account, name, &balance ); 22 feof gibt 1 zurück, wenn der Nutzer EOF eingibt 23 // write account, name and balance into file with fprintf 24 while( ! feof( stdin ) ) { 25 fprintf( cfPtr, "%d %s %.2f\n", account, name, balance ); 26 printf( "%s", "? " ); 27 scanf( "%d%29s%lf", &account, name, &balance ); 28 } // end while 29 30 Formatierte Ausgabe in clients.txt mit fprintf fclose schließt die Datei fclose( cfPtr ); // closes file 31 } // end main 2006 Pearson Education, Inc. All rights reserved. Enter Enter ? 100 ? 200 ? 300 ? 400 ? 500 ? ^Z the account, name, and balance. EOF to end input. Jones 24.98 Doe 345.67 White 0.00 Stone -42.16 Rich 224.62 Outline 24 fig09_04.c (2 von 2) 2006 Pearson Education, Inc. All rights reserved. 9.5 Daten von einer sequenziellen Datei lesen 25 • Öffnen einer Datei zum Einlesen – Hierzu wird einer Variablen vom Typ FILE* durch die Funktion fopen ein Zeiger auf die FILE Struktur der zu öffnenden Datei zugewiesen. – fopen übernimmt zwei Argumente: Einen Dateinamen (einschließlich Pfadinformation zum Ort der Datei im Dateiverzeichnis) und einen Dateiöffnungs-Modus. – Der Dateiöffnungs-Modus "r" zeigt an, dass die Datei zum Lesen geöffnet wird. – Falls die Datei nicht existiert und zum Lesen geöffnet werden soll, gibt fopen den Wert NULL zurück. 2006 Pearson Education, Inc. All rights reserved. 9.5 Daten von einer sequenziellen Datei lesen 26 • Formatiertes Einlesen mit fscanf – Zum formatierten Einlesen aus einer Datei wird die Funktion fscanf verwendet. – fscanf ist äquivalent mit scanf, bis auf die Tatsache, dass fscanf einen Zeiger auf die einzulesende Datei als erstes Argument übernimmt. – Bei Erfolg gibt fscanf (wie auch scanf) die Anzahl der erfolgreich gelesenen Argumente zurück. Dies kann der Anzahl der erwarteten Werte entsprechen oder kleiner sein aufgrund eines Lesefehlers oder des Erreichens von end-of-file (EOF). – Im Fehlerfall wird der passende Indikator (Fehler oder EOF) für den Dateistrom gesetzt und - falls dies vor dem erfolgreichen Einlesen von Daten passiert - EOF zurückgegeben. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 9.7: fig09_07.c 2 // Reading and printing a sequential file. 3 #include <stdio.h> 4 #include <stdlib.h> // exit function prototype 5 #define NAME_SIZE 30 Outline 6 7 int main( void ) 8 { fig09_07.c (1 von 2) 9 unsigned int account; 10 char name[ NAME_SIZE ]; 11 12 double balance; 13 FILE* cfPtr = fopen( "clients.txt", "r" ); Öffnen von clients.txt zum Einlesen 14 15 if( cfPtr == NULL ) { fopen gibt NULL zurück, falls clients.txt nicht erfolgreich geöffnet werden konnte 16 fputs( "File could not be opened", stderr ); 17 exit( 1 ); // exit program if file could not be opened 18 27 } // end if 19 20 printf( "%-10s%-13s%s\n", "Account", "Name", "Balance" ); 21 22 // display each record in file 23 24 while( fscanf( cfPtr, "%d%29s%lf", &account, name, &balance ) == 3 ) printf( "%-10d%-13s%7.2f\n", account, name, balance ); 25 26 fclose( cfPtr ); 27 } // end main fscanf gibt die Anzahl der erfolgreich eingelesenen Argumente zurück; wenn das Ende der Datei clients.txt erreicht wird, ist dieser Wert ungleich 3 und die Schleife wird beendet. 2006 Pearson Education, Inc. All rights reserved. Account 100 200 300 400 500 Name Jones Doe White Stone Rich Balance 24.98 345.67 0.00 -42.16 224.62 Outline 28 fig09_07.c (2 von 2) 2006 Pearson Education, Inc. All rights reserved. 9.5 Daten von einer sequenziellen Datei lesen 29 • Datei-Positionszeiger – Byte-Nummer des nächsten Byte zum Lesen oder Schreiben • Keine Variable eines Zeigertyps, sondern ein int-Wert – Die Anweisung rewind( cfPtr ); setzt den Datei-Positionszeiger an den Anfang (d.h. Byte 0) der Datei, der cfPtr zugeordnet ist. 2006 Pearson Education, Inc. All rights reserved. 9.5 Daten von einer sequenziellen Datei lesen 30 Kreditabfrage Programm • Das folgende Programm erlaubt es, Listen von – Kunden mit Kontostand Null, – Kunden mit Kreditstand (d.h. Kunden, denen das Unternehmen Geld schuldet) und – Kunden mit Debitstand (d.h. Kunden, die dem Unternehmen Geld für Waren und Dienstleistungen schulden) auszugeben. • Ein Kreditstand ist ein negativer Wert; ein Debitstand ist ein positiver Wert. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 9.8: fig09_08.c 2 // Credit inquiry program. 3 #include <stdio.h> 4 #include <stdlib.h> // exit function prototype 5 #define NAME_SIZE 30 6 7 enum RequestType { ZERO_BALANCE = 1, CREDIT_BALANCE, DEBIT_BALANCE, END }; 8 9 int main( void ) Outline 31 fig09_08.c (1 von 3) 10 { 11 unsigned int request; 12 unsigned int account; 13 char name[ NAME_SIZE ]; 14 double balance; 15 16 FILE* cfPtr = fopen( "clients.txt", "r" ); 17 18 if( cfPtr == NULL ) { 19 fputs( "File could not be opened", stderr ); 20 exit( 1 ); // exit program if file could not be opened 21 } // end if 22 23 24 // display request options printf( "%s", "\nEnter request\n" 25 " 1 - List accounts with zero balances\n" 26 " 2 - List accounts with credit balances\n" 27 " 3 - List accounts with debit balances\n" 28 " 4 - End of run\n? " ); 29 scanf( "%u", &request ); 30 2006 Pearson Education, Inc. All rights reserved. 31 // process user's request 32 while( request != END ) { 33 switch( request ) { 34 case ZERO_BALANCE: 35 puts( "\nAccounts with zero balances:" ); 36 // read and display file contents (until eof) 37 while( fscanf( cfPtr, "%d%29s%lf", &account, name, &balance ) == 3 ) 38 if( balance == 0 ) 39 40 41 printf( "%-10d%-13s%7.2f\n", account, name, balance ); 43 // read and display file contents (until eof) 44 while( fscanf( cfPtr, "%d%29s%lf", &account, name, &balance ) == 3 ) 45 if( balance < 0 ) 46 printf( "%-10d%-13s%7.2f\n", account, name, balance ); break; case DEBIT_BALANCE: 49 puts( "\nAccounts with debit balances:" ); 50 // read and display file contents (until eof) 51 while( fscanf( cfPtr, "%d%29s%lf", &account, name, &balance ) == 3 ) 52 53 54 (2 von 3) case CREDIT_BALANCE: puts( "\nAccounts with credit balances:" ); 48 fig09_08.c break; 42 47 Outline 32 if( balance > 0 ) printf( "%-10d%-13s%7.2f\n", account, name, balance ); break; 55 } // end switch 56 rewind( cfPtr ); // return cfPtr to beginning of file 57 printf( "%s", "\n? " ); 58 scanf( "%u", &request ); 59 } // end while 60 puts( "End of run." ); 61 fclose( cfPtr ); // close file 62 } // end main 2006 Pearson Education, Inc. All rights reserved. Outline Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run ? 1 fig09_08.c Accounts with zero balances: 400 Metzger 0.00 (3 von 3) 33 ? 2 Accounts with credit balances: 300 Muller -112.00 ? 3 Accounts with debit balances: 100 Becker 99.90 200 Schneider 199.90 500 Schmidt 22.20 ? 4 End of run. 2006 Pearson Education, Inc. All rights reserved. 34 9.6 Eine sequenzielle Datei aktualisieren • Aktualisierung eines Datensatzes in einer sequenziellen Datei – Der neue Datensatz könnte länger als der alte sein. • Falls dies so ist, werden Teile des nächsten sequenziellen Datensatzes überschrieben. • Um dies zu vermeiden, müssen alle Datensätze hinter dem neuen Datensatz kopiert werden. • Für große Dateien kann der entsprechende Aufwand unakzeptabel werden. 2006 Pearson Education, Inc. All rights reserved. 35 9.7 Dateien mit wahlfreiem Zugriff • Dateien mit wahlfreiem Zugriff – Notwendig für Anwendungen mit direktem Zugriff • Beispielsweise Systeme zur Transaktionsverarbeitung – Ein Datensatz kann eingefügt, gelöscht oder modifiziert werden, ohne andere Datensätze zu beeinflussen. – Verschiedene Techniken können eingesetzt werden. • Voraussetzung ist, dass alle Datensätze die gleiche Länge haben und in der Reihenfolge ihrer Schlüssel angeordnet sind. – Das Programm kann den genauen Ort jedes Datensatzes berechnen. • Basis hierfür sind Größe und Schlüssel des Datensatzes. 2006 Pearson Education, Inc. All rights reserved. 36 Fig. 9.9 | C-Sicht einer Datei mit wahlfreiem Zugriff. 2006 Pearson Education, Inc. All rights reserved. 9.8 Erzeugung einer Datei mit wahlfreiem Zugriff 37 • Die Funktion fwrite schreibt eine bestimmte Anzahl von Bytes, beginnend an einer bestimmten Speicheradresse, unformatiert in eine Datei. • Die Daten werden in der Datei ab der Stelle geschrieben, auf die der Dateipositions-Zeiger gerade zeigt. • Entsprechend liest die Funktion fread eine bestimmte Anzahl von Bytes ab der Stelle in der Datei, auf die der Dateipositions-Zeiger gerade zeigt, in einen Speicherbereich beginnend an einer angegebenen Speicheradresse. 2006 Pearson Education, Inc. All rights reserved. 9.8 Erzeugung einer Datei mit wahlfreiem Zugriff 38 •Funktion fwrite – Schreibt eine Anzahl von Bytes aus einem Speicherbereich in eine Datei an die Stelle, auf die der Positionszeiger zeigt. – Parameter: • Ein const void*, der auf das Anfangsbyte eines Arrays im Speicher zeigt • Ein size_t, der die Anzahl der Bytes angibt, die für ein Element des Arrays geschrieben werden sollen • Ein size_t, der die Anzahl der Elemente im Array angibt, die geschrieben werden sollen • Zeiger auf eine FILE-Struktur, die die Ausgabedatei angibt – Beispiel: fwrite( &number, sizeof( int ), 1, fPtr ); 2006 Pearson Education, Inc. All rights reserved. 39 Portabilitäts-Tipp Die Verwendung von fwrite ist systemabhängig und kann dazu führen, dass sich Programme auf verschiedenen Plattformen unterschiedlich verhalten. Die Daten werden nicht formatiert (wie im Fall von fprintf ), sondern in Form von ‘rohen Daten’ (also als Bytes) geschrieben. 2006 Pearson Education, Inc. All rights reserved. 40 Portabilitäts-Tipp Ein Programm, das unformatierte Daten liest (die mit fwrite geschrieben wurden), muss auf einem System kompiliert und ausgeführt werden, das mit dem Programm kompatibel ist, das die Daten geschrieben hat. Unterschiedliche Systeme können unterschiedliche interne Datendarstellungen verwenden (Beispiel: Big Endian – Little Endian). 2006 Pearson Education, Inc. All rights reserved. 9.8 Erzeugung einer Datei mit wahlfreiem Zugriff 41 • Headerdateien – Separate Dateien, die nur Typdefinitionen, Funktionsprototypen und Konstanten enthalten • Erlauben dem Compiler nutzerdefinierte Typen und Funktionen zu erkennen, wenn sie anderswo eingesetzt werden – Haben im allgemeinen .h Erweiterung für Dateinamen • Testanwendungsdateien (Driver files) – Programme zum Testen von Typen und Funktionen – Enthalten eine main Funktion und sind ausführbar – Haben im allgemeinen .c Erweiterung für Dateinamen •.h und .c Dateien werden als Quelldateien (source code files) bezeichnet . 2006 Pearson Education, Inc. All rights reserved. 9.8 Erzeugung einer Datei mit wahlfreiem Zugriff 42 • #include Präprozessor-Direktive – Wird benutzt, um Headerdateien einzufügen • Weist den C Präprozessor an, die Direktive durch eine Kopie des Inhalts der angegebenen Datei zu ersetzen – Anführungszeichen kennzeichnen eine nutzerdefinierte Headerdatei • Der Präprozessor sucht zuerst im aktuellen Verzeichnis – Falls die Datei nicht gefunden wird, sucht er im C Standard-include-Verzeichnis – Winkelklammern kennzeichnen die C Standardbibliothek • Präprozessor sucht nur im C Standard-include-Verzeichnis 2006 Pearson Education, Inc. All rights reserved. 9.8 Erzeugung einer Datei mit wahlfreiem Zugriff 43 • Präprozessordirektiven zur Vermeidung des mehrfachen Einbindens von Headerdateien – Verhindern, dass Code mehr als einmal eingebunden wird • #ifndef – “if not defined” – Überspringe diesen Code, falls schon definiert, d.h. eingebunden • #define – Definiere einen Namen zur Vermeidung des nochmaligen Einbindens • #endif – Falls der Header schon zuvor eingebunden wurde • Name ist schon definiert und Headerdatei wird nicht mehr eingebunden – Verhindert Fehlermeldungen durch Mehrfachdefinitionen – Beispiel: #ifndef CLIENTDATA_H #define CLIENTDATA_H … // code #endif 2006 Pearson Education, Inc. All rights reserved. 9.8 Erzeugung einer Datei mit wahlfreiem Zugriff 44 • Aufgabenstellung: – Es soll ein Kreditverarbeitungs-Programm geschrieben werden, das bis zu 100 Datensätze fester Länge in einer Datei speichern kann. Jeder Datensatz soll aus einer Kontonummer (die auch als Schlüssel für den Datensatz dient), Nachname, Vorname und Kontostand bestehen. – Das Programm soll • ein bestehendes Konto aktualisieren können, • einen Datensatz für ein neues Konto einfügen können, • ein Konto löschen und • die Datensätze aller vorhandenen Konten als formatierte Textdatei abspeichern oder auf die Konsole ausgeben können. – Es soll eine Datei mit wahlfreiem Zugriff verwendet werden. 2006 Pearson Education, Inc. All rights reserved. 9.8 Erzeugung einer Datei mit wahlfreiem Zugriff 45 – Das Kreditverarbeitungs-Programm wird in mehreren Schritten / Teilprogrammen entwickelt. – Das erste Teilprogramm dient nur dazu, die Grundstruktur der verwendeten Datei zu erzeugen, indem 100 leere Datensätze erzeugt und in eine Datei mit wahlfreiem Zugriff geschrieben werden. – Die Datensätze werden als Variablen vom Typ einer struct Clientdata erzeugt. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 9.11: ClientData.h 2 // Type ClientData definition used in Fig. 9.12 – Fig. 9.15. 3 #ifndef CLIENTDATA_H 4 #define CLIENTDATA_H 5 6 typedef struct { 7 unsigned int accountNumber; 8 char lastName[ 16 ]; 9 char firstName[ 12 ]; 10 double balance; Headerdatei, die die Definition des Typs ClientData enthält. Outline 46 ClientData.h (1 von 1) 11 } ClientData; // end definition of type ClientData 12 13 #endif 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 9.12: fig09_12.c 2 // Creating a random-access file sequentially. 3 #include <stdio.h> 4 #include <stdlib.h> // exit 5 Outline Einfügen der Headerdatei, die die Definition des Typs ClientData enthält. #include "ClientData.h" // Definition of type ClientData fig09_12.c 8 int main( void ) (1 von 1) 9 { 6 47 7 10 size_t i; // loop counter 11 ClientData blankClient = { 0, "", "", 0.0 }; // default record 12 13 FILE* cfPtr = fopen( "credit.dat", "wb" ); credit.dat im Binärmodus öffnen 14 15 if( cfPtr == NULL ) { 16 fputs( "File could not be opened.", stderr ); 17 exit( 1 ); // exit program if unable to open file 18 } // end if 19 20 // output 100 blank records to file 21 for( i = 0; i < 100; ++i ) 22 fwrite( &blankClient, sizeof( ClientData ), 1, cfPtr ); 23 24 fclose( cfPtr ); 25 fputs( "File written." ); 26 } // end main Daten in blankClient als Bytes in credit.dat abspeichern 2006 Pearson Education, Inc. All rights reserved. 9.9 Daten wahlfrei in eine Datei mit wahlfreiem Zugriff schreiben 48 • Daten an bestimmte Position schreiben – Für Ein- und Ausgabe im Binärmodus öffnen • Verwenden der Dateiöffnungsart "rb+" – Einsatz der Funktion fseek, um den Positionszeiger an die gewünschte Position zu setzen • Beispiel: ( n – 1 ) * sizeof( ClientData ) • Byteposition für nten ClientData Datensatz – Verwendung von fwrite, um Daten zu schreiben 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 9.13: fig09_13.c 2 // Writing data randomly to a random-access file. 3 #include <stdio.h> 4 #include <stdlib.h> // exit Outline 49 5 6 #include "ClientData.h" // ClientData type definition fig09_13.c 8 int main( void ) 9 { (1 von 3) 7 10 ClientData client = { 0, "", "", 0.0 }; // default record 11 12 FILE* cfPtr = fopen( "credit.dat", "rb+" ); 13 14 if( cfPtr == NULL ) { 15 fputs( "File could not be opened.", stderr ); 16 exit( 1 ); // exit program if unable to open file 17 Verwenden von rb+, um credit.dat zur Ein- und Ausgabe im Binärmodus zu öffnen } // end if 18 19 // require user to specify account number 20 printf( "%s", "Enter account number (1 to 100, 0 to end input)\n? " ); 21 scanf( "%d", &client.accountNumber ); 2006 Pearson Education, Inc. All rights reserved. 22 23 // user enters information, which is copied into file 24 while( client.accountNumber > 0 && client.accountNumber <= 100 ) { 25 // user enters last name, first name and balance 26 printf( "%s", "Enter lastname, firstname, balance\n? " ); 27 28 // set record lastName, firstName and balance values 29 fscanf( stdin, "%14s%9s%lf", client.lastName, 30 fig09_13.c (2 von 3) client.firstName, &client.balance ); 31 32 // seek position in file of user-specified record 33 fseek( cfPtr, ( client.accountNumber - 1 ) * 34 Outline 50 Positionszeiger wird auf gewünschte Byteposition gesetzt sizeof( ClientData ), SEEK_SET ); 35 36 // write user-specified information in file 37 fwrite( &client, sizeof( ClientData ), 1, cfPtr ); 38 39 // enable user to enter another account 40 printf( "%s", "Enter account number\n? " ); 41 scanf( "%d", &client.accountNumber ); 42 } // end while Der ClientData Datensatz wird an die gewünschte Position geschrieben 43 44 fclose( cfPtr ); 45 } // end main 2006 Pearson Education, Inc. All rights reserved. Enter account number (1 to ? 37 Enter lastname, firstname, ? Barker Doug 0.00 Enter account number ? 29 Enter lastname, firstname, ? Brown Nancy -24.54 Enter account number ? 96 Enter lastname, firstname, ? Stone Sam 34.98 Enter account number ? 88 Enter lastname, firstname, ? Smith Dave 258.34 Enter account number ? 33 Enter lastname, firstname, ? Dunn Stacey 314.33 Enter account number ? 0 100, 0 to end input) balance balance Outline 51 fig09_13.c (3 von 3) balance balance balance 2006 Pearson Education, Inc. All rights reserved. 9.9 Daten wahlfrei in eine Datei mit wahlfreiem Zugriff schreiben 52 • Daten an bestimmte Position schreiben – Der Funktionsprototyp für fseek ist int fseek( FILE* stream, long int offset, int whence ); – Hierbei ist offset die Anzahl der Bytes für das Setzen des Positionszeigers gerechnet ab whence in der Datei, auf die stream zeigt - ein positiver offset sucht vorwärts und ein negativer sucht in Rückwärtsrichtung. – Das Argument whence ist einer der Werte SEEK_SET, SEEK_CUR oder SEEK_END (alle definiert in <stdio.h>), die den Ort angeben, an dem die Suche beginnt. 2006 Pearson Education, Inc. All rights reserved. 9.9 Daten wahlfrei in eine Datei mit wahlfreiem Zugriff schreiben 53 • Daten an bestimmte Position schreiben – Die symbolische Konstante SEEK_SET gibt an, dass der Positionszeiger relativ zum Anfang der Datei um die Größe des Offset erhöht wird. – SEEK_CUR gibt an, dass die Suche an der aktuellen Position des Positionszeigers in der Datei beginnt und SEEK_END gibt an, dass die Suche am Ende der Datei beginnt. 2006 Pearson Education, Inc. All rights reserved. 9.9 Daten wahlfrei in eine Datei mit wahlfreiem Zugriff schreiben 54 • Um die Programme dieses Kapitels übersichtlich zu halten, wird nicht auf mögliche Fehlerfälle getestet. • Professionelle Programme sollten jedoch testen, ob Funktionen wie fscanf , fseek und fwrite korrekt arbeiten, indem ihre Rückgabewerte überprüft werden. • fscanf gibt die Anzahl der erfolgreich gelesenen Datenwerte zurück oder den Wert EOF falls beim Einlesen der Daten ein Problem auftritt. • fseek gibt einen Wert ungleich Null zurück, falls die Suchoperation nicht durchgeführt werden kann. • fwrite gibt die Anzahl der erfolgreich ausgegebenen Werte zurück . Falls diese Zahl kleiner als das dritte Argument des Funktionaufrufs ist, trat ein Fehler auf. 2006 Pearson Education, Inc. All rights reserved. 9.10 Sequenzielles Lesen aus einer Datei mit wahlfreiem Zugriff 55 • Funktion fread – Liest eine Anzahl von Bytes aus dem Eingabestrom (der mit der aktuellen Dateiposition verknüpft ist) in ein Objekt im Speicher – Parameter: • Ein const void*, der auf das Anfangsbyte eines Arrays im Speicher zeigt • Ein size_t, der die Anzahl der Bytes angibt, die in ein Element des Arrays eingelesen werden sollen • Ein size_t, der die Anzahl der Elemente des Arrays angibt, die eingelesen werden sollen • Zeiger auf eine FILE-Struktur, die die Eingabedatei angibt – Beispiel: fread( &client, sizeof( ClientData ), 1, cfPtr ); 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 9.14: fig09_14.c 2 // Reading a random-access file sequentially. 3 #include <stdio.h> 4 #include <stdlib.h> // exit Outline 56 5 6 #include "ClientData.h" // ClientData type definition fig09_14.c 8 int main( void ) 9 { (1 von 2) 7 10 ClientData client = { 0, "", "", 0.0 }; // default record 11 12 FILE* cfPtr = fopen( "credit.dat", "rb" ); 13 14 if( cfPtr == NULL ) { 15 fputs( "File could not be opened.", stderr ); 16 exit( 1 ); // exit program if unable to open file 17 } // end if 18 19 printf( "%-10s%-16s%-11s%10s\n", "Account", "Last Name", 20 "First Name", "Balance" ); 21 Daten aus credit.dat als Bytes in client abspeichern 22 // read all records from file 23 while( fread( &client, sizeof( ClientData ), 1, cfPtr ) == 1 ) { 24 // display record 25 if( client.accountNumber != 0 ) 26 printf( "%-10d%-16s%-11s%10.2f\n", client.accountNumber, 27 client.lastName, client.firstName, client.balance ); 28 } // end while 29 30 fclose( cfPtr ); 31 } // end main 2006 Pearson Education, Inc. All rights reserved. Account Last Name First Name 29 33 37 88 96 Brown Dunn Barker Smith Stone Nancy Stacey Doug Dave Sam Balance -24.54 314.33 0.00 258.34 34.98 Outline 57 fig09_14.c (2 von 2) 2006 Pearson Education, Inc. All rights reserved. 9.11 Fallstudie: Verarbeitung von Transaktionen 58 • Ausführliches Beispiel: Durchführung verschiedener Transaktionen von Kontendaten – Sequenzielles Lesen aus einer Binärdatei mit wahlfreiem Zugriff und Schreiben der gelesenen Informationen in eine Textdatei oder auf die Konsole – Aktualisieren eines zuvor in die Binärdatei geschriebenen Datensatzes – Schreiben eines neuen Datensatzes in die Binärdatei – Löschen eines vorhandenen Datensatzes aus der Binärdatei 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 9.15: fig09_15.c 2 // Bank-account program reads a random-access file sequentially, 3 // updates data previously written to the file, creates data to 4 // be placed in the file, and deletes data previously in the file. 5 #include <stdio.h> 6 #include <stdlib.h> // exit fig09_15.c #include "ClientData.h" // ClientData type definition (1 von 9) 7 8 9 Outline 59 10 // prototypes 11 unsigned int enterChoice( void ); 12 void createTextFileOrPrint( FILE* readPtr ); 13 void updateRecord( FILE* fPtr ); 14 void newRecord( FILE* fPtr ); 15 void deleteRecord( FILE* fPtr ); 16 17 enum Choices { PRINT = 1, UPDATE, NEW, DELETE, END }; 18 19 int main( void ) 20 { 21 unsigned int choice; // user's choice 22 FILE* cfPtr = fopen( "credit.dat", "rb+" ); 23 24 if( cfPtr == NULL ) { 25 fputs( "File could not be opened.", stderr ); 26 exit( 1 ); // exit program if unable to open file 27 } // end if 28 2006 Pearson Education, Inc. All rights reserved. 29 // enable user to specify action 30 while( ( choice = enterChoice() ) != END ) { 31 32 switch( choice ) { case PRINT: // create text file from record file or print to console 33 createTextFileOrPrint( cfPtr ); 34 break; 35 case UPDATE: // update record 36 updateRecord( cfPtr ); 37 break; 38 case NEW: // create record 39 newRecord( cfPtr ); 40 break; 41 deleteRecord( cfPtr ); 43 break; puts( "Incorrect choice\n" ); 46 break; 48 (2 von 9) default: // display message if user does not select valid choice 45 47 fig09_15.c case DELETE: // delete existing record 42 44 Outline 60 } // end switch } // end while 49 50 fclose( cfPtr ); 51 } // end main 52 2006 Pearson Education, Inc. All rights reserved. 53 // enable user to input menu choice 54 unsigned int enterChoice( void ) 55 { 56 unsigned int menuChoice; 57 // display available options 58 printf( "%s", "\nEnter your choice\n" 59 "1 - store text file or print to console\n" 60 "2 - update an account\n" 61 "3 - add a new account\n" 62 "4 - delete an account\n" 63 "5 - end program\n? " ); Outline 61 fig09_15.c (3 von 9) 64 65 scanf( "%u", &menuChoice ); // receive choice from user 66 return menuChoice; 67 } // end function enterChoice 68 2006 Pearson Education, Inc. All rights reserved. 69 // create text file or print to console 70 void createTextFileOrPrint( FILE* readPtr ) 71 { 72 ClientData client = { 0, "", "", 0.0 }; // default 73 74 unsigned int writeChoice; 75 // display available options 76 printf( "%s", "\nEnter your choice\n" 77 "1 - store a formatted text file of acounts" 78 79 80 FILE*-Zeiger als Parameter Outline für das Einlesen von Daten aus credit.dat record fig09_15.c (4 von 9) " called \"accounts.txt\"\n" "2 – write to console\n? " ); scanf( "%u", &writeChoice ); // receive choice from user 81 82 62 FILE* writePtr = ( writeChoice == 1 ? 83 fopen( "accounts.txt", "w" ) : stdout ); Verwendung von writeChoice, um writePtr mit Ausgabedatei oder stdout zu verknüpfen 84 85 // exit program if file cannot be opened 86 if( writePtr == NULL ) { 87 fputs( "File could not be opened.", stderr ); 88 exit( 1 ); // exit program if unable to open file 89 } // end if 90 2006 Pearson Education, Inc. All rights reserved. 91 92 fprintf( writePtr, "%-10s%-16s%-11s%10s\n", "Account", 93 "Last Name", "First Name", "Balance" ); 94 95 rewind( readPtr ); // set file-position pointer to beginning of file 96 97 // copy all records from record file into text file 98 while( fread( &client, sizeof( ClientData ), 1, readPtr ) == 1 ) { 99 100 // write single record to text file (5 von 9) if( client.accountNumber != 0 ) // skip empty records 101 fprintf( writePtr, "%-10d%-16s%-11s%10.2f\n", client.accountNumber, 102 103 Outline Verwendung von rewind, um sicherzustellen, dass der Positionszeiger am Anfang der Datei ist fig09_15.c 63 client.lastName, client.firstName, client.balance ); } // end while 104 105 if( writeChoice == 1 ) { // output file was written 106 printf( "File %s written.\n", "'accounts.txt'" ); 107 fclose( writePtr ); 108 } // 109 } // end function createTextFile 110 2006 Pearson Education, Inc. All rights reserved. 111 // update balance in record Outline 112 void updateRecord( FILE* fPtr ) 113 { 114 unsigned int account; // account number 115 double transaction; // transaction amount 116 ClientData client = { 0, "", "", 0.0 }; // default record fig09_15.c 118 printf( "%s", "Enter account to update ( 1 - 100 ): " ); 119 scanf( "%d", &account ); (6 von 9) 120 // move file-position pointer to correct record in file 121 fseek( fPtr, ( account - 1 ) * sizeof( ClientData ), SEEK_SET ); 117 64 122 123 // read record from file 124 fread( &client, sizeof( ClientData ), 1, fPtr ); 125 126 127 128 Feststellen, ob der Datensatz Informationen enthält if( client.accountNumber != 0 ) { // update record printf( "%-10d%-16s%-11s%10.2f\n\n", client.accountNumber, client.lastName, client.firstName, client.balance ); 129 // request transaction amount from user 130 printf( "%s", "\nEnter charge ( + ) or payment ( - ): " ); 131 scanf( "%lf", &transaction ); 132 client.balance += transaction; // update record balance 133 134 printf( "%-10d%-16s%-11s%10.2f\n\n", client.accountNumber, client.lastName, client.firstName, client.balance ); 135 // move file-position pointer to correct record in file 136 fseek( fPtr, ( account - 1 ) * sizeof( ClientData ), SEEK_SET ); 137 // write updated record over old record in file 138 fwrite( &client, sizeof( ClientData ), 1, fPtr ); 139 } // end if 140 else // display message if account does not exist 141 Daten aus dem Objekt client mit fwrite in die Binärdatei schreiben printf( "Account #%d has no information\n", account ); 142 } // end function updateRecord 2006 Pearson Education, Inc. All rights reserved. 143 Outline 144 // create and insert record 145 void newRecord( FILE* fPtr ) 65 146 { 147 unsigned int account; // account number 148 ClientData client = { 0, "", "", 0.0 }; // default record fig09_15.c 150 printf( "%s", "Enter new account number ( 1 - 100 ): " ); 151 scanf( "%d", &account ); (7 von 9) 152 // move file-position pointer to correct record in file 153 154 fseek( fPtr, ( account - 1 ) * sizeof( ClientData ), SEEK_SET ); 155 // read record from file 156 fread( &client, sizeof( ClientData ), 1, fPtr ); 149 157 158 Feststellen, ob der Datensatz keine Informationen enthält if( client.accountNumber == 0 ) { // create record 159 // user enters last name, first name and balance 160 printf( "%s", "Enter lastName, firstName, balance\n? " ); 161 scanf( "%14s%9s%lf", client.lastName, client.firstName, &client.balance ); 162 163 client.accountNumber = account; 164 165 // move file-position pointer to correct record in file 166 fseek( fPtr, ( account - 1 ) * sizeof( ClientData ), SEEK_SET ); 167 // insert record in file 168 fwrite( &client, sizeof( ClientData ), 1, fPtr ); 169 } // end if 170 else // display message if account does not exist Daten aus dem Objekt client mit fwrite in die Binärdatei schreiben 171 printf( "Account #%d already contains information\n", account ); 172 } // end function newRecord 2006 Pearson Education, Inc. All rights reserved. 173 Outline 174 // delete an existing record 175 void deleteRecord( FILE* fPtr ) 66 176{ 177 unsigned int account; // account number 178 ClientData client; // stores record read from file 179 ClientData blankClient = { 0, "", "", 0.0 }; // blank client 180 181 printf( "%s", "Enter account number to delete ( 1 - 100 ): " ); 182 scanf( "%d", &account ); 183 184 // move file-position pointer to correct record in file fseek( fPtr, ( account - 1 ) * sizeof( ClientData ), SEEK_SET ); fig09_15.c (8 von 9) 185 186 // read record from file 187 fread( &client, sizeof( ClientData ), 1, fPtr ); 188 189 Feststellen, ob der Datensatz Informationen enthält if( client.accountNumber != 0 ) { // delete record 190 // move file-position pointer to correct record in file 191 fseek( fPtr, ( account - 1 ) * sizeof( ClientData ), SEEK_SET ); 192 // replace existing record with blank record 193 fwrite( &blankClient, sizeof( ClientData ), 1, fPtr ); 194 printf( "Account #%d deleted.\n", account ); 195 } // end if 196 else // display message if account does not exist 197 printf( "Account #%d does not exist.\n", account ); Leeren Datensatz in die Datei kopieren, um Konto wieder auf Null zu setzen 198 } // end function deleteRecord 2006 Pearson Education, Inc. All rights reserved. Enter your choice 1 - store text file or print to console 2 - update an account 3 - add a new account 4 - delete an account 5 - end program ? 1 Enter your choice 1 - store a formatted text file of acounts called "accounts.txt" 2 - write to console ? 2 Account Last Name First Name Balance 29 Brown Nancy -24.54 33 Dunn Stacey 314.33 37 Barker Doug 0.00 88 Smith Dave 258.34 96 Stone Sam 34.98 Outline 67 fig09_15.c (9 von 9) Enter your choice 1 - store text file or print to console 2 - update an account 3 - add a new account 4 - delete an account 5 - end program ? 1 Enter your choice 1 - store a formatted text file of acounts called "accounts.txt" 2 - write to console ? 1 File 'accounts.txt' written. 2006 Pearson Education, Inc. All rights reserved.