[Inhalt][0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17]
Willkommen beim sechsten Teil des C-Kurses!

Bisher hatten wir, recht erfolgreich, schon kleinere Programme geschrieben, die schon recht ansehnlich waren. Doch sie hatten ein Manko: Die eingegebenen Daten waren nach dem Beenden der Programme im Nirwana gelandet. Dem wollen wir abhelfen, indem wir uns im 6. Teil mit dem Ein- und Ausgeben der Daten in Dateien befassen.
 
Standardkanäle in C

Kanäle ? Ich kenn nur Canale Grande.... C legt bei jedem Start eines Programms drei fest definierte Ein- & Ausgabekanäle an. Sie müssen nicht definiert werden! In der Regel werden diese Ein-, Ausgabekanäle benutzt, um Datenumlenkungen zu ermöglichen. bsp. wenn unter Dos die Ausgabe auf ein File umgelenkt wird, wird das File zur Standardausgabe, bzw. wenn ein File als Eingabe auf ein Programm gelenkt wird, wird dieses File zur Standardeingabe. Doch schauen wir uns diese Drei Kanäle einmal genauer an. Die verwendeten Befehle werden noch erläutert.
 
stdin

Der Standardeingabekanal, er ist in der Regel die Tastatur
 

scanf ( ... )

ist also im Normalfall identisch mit
 

fscanf ( stdin, ... )

 
stdout

Der Standardausgabekanal ist in der Regel der Bildschirm
 

printf ( ... )

ist also im Normalfall identisch mit
 

fprintf ( stdout , ... )

 
 
stderr

Der Standardfehlerkanal. Hier werden die Fehlermeldungen zu Diagnosezwecken ausgegeben. Dies geschieht in der Regel auch auf dem Bildschirm. Von praktischem Nutzen wird das ganze, wenn der Standardausgabekanal nicht mehr der Bildschirm ist und man trotzdem Feedback haben möchte.
 

fprintf ( stderr ,"Fehler aufgetreten in der Datei xyz.bat" )

 
Das Öffnen und Schließen einer Datei

Für C ist eine Datei nichts anderes als eine Ansammlung von Zeichen, unabhängig, was drin steht. Um mit einer Datei unter C zu arbeiten, muß man sie "öffnen" und danach natürlich wieder "schließen".
 

/*
    Mal schauen, was man mit Files alles anstellen kann 
    Beispiel zum schreiben eines Files
*/
#include <stdio.h>

void main ( void )
{

    /*
    neuer Variablentyp FILE ! Er enthält Informationen über das zu bearbeitende File! 
    */

    FILE *datei; 

    datei = fopen ( "test.dat" , "w+" );
    if (datei == NULL ) 
    {

      /*
        Da ist wohl ein Fehler aufgetreten! 
      */

      printf ("\nDie Datei konnte nicht geöffnet werden!\n");

    }
    else
    {
      /* 
        Alles lief wie geplant - es kann losgehen 
      */
      fprintf (datei, "Hallo, dies steht nun in der Datei !");

      printf ("\nAlles OK!\n");
      printf ("\t\t...und die Datei ist wieder zu\n");

      fclose (datei);

}

Nach dem Start erscheint folgendes:

Screenshot des ausgeführten Programms

Ziemlich unspektakulär, aber schauen wir uns jetzt das File an. Wie wir sehen haben wir ein File mit Namen test.dat erzeugt, das folgenden Inhalt hat.

Screenshot des ausgeführten Programms

Schauen wir uns nun die Befehle an, die wir angewendet haben.
 
 
fopen

Mittels fopen wird eine Datei geöffnet. Als Argument werden die zu bearbeitende Datei und der Arbeitsmodus übergeben.
 
 

r nur zum lesen öffnen, die Datei muß existieren
w nur zum schreiben öffnen, wenn eine Datei existiert wird sie vorher gelöscht
a nur zum anhängen öffnen, wenn keine Datei existiert, wird sie angelegt
( die Fileendmarkierung EOF wird nicht gelöscht )
r+ zum lesen und schreiben öffnen, die Datei muß existieren
w+ zum lesen und schreiben öffnen, wenn die Datei nicht existiert, wird sie erzeugt
a+ nur zum anhängen öffnen, wenn keine Datei existiert, wird sie angelegt
( vor dem anhängen wird EOF gelöscht und am Endgeschriebene )

Zusätzlich zu diesen Arbeitsmodi kann man noch weitere Angaben machen.
 

b Binärmodus
alle Daten werden ohne Umwandlung in die Datei geschrieben
t Textmodus
einige Daten werden umgewandelt, so z.B. \n oder \r\n

Wird keine Angabe gemacht, ob im Text- oder Binärmodus gearbeitet wird, so wird defaultmäßig im Textmodus gearbeitet. Das oben erwähnte EOF ( = End Of File ) dient dazu das Ende eines Files zu markieren. Will man nun ein File zum lesen und schreiben öffnen, welches im Binärmodus arbeteitet, so kann man dies mit
 

fopen (datei, "w+b");

 
fclose

Mit fclose können wir die Dateien, die wir mit fopen öffneten wieder schließen.
 

fclose (datei);

Hier werden die Schreibpuffer auf das Medium zurückgeschrieben und die Verzeichniseinträge aktualisiert. datei ist vom Typ *FILE und wurde mit fopen initialisiert!
 
 
Lesen und schreiben von Daten

In den folgenden Beispielen ist datei immer eine mittels fopen erzeugte Variable vom Typ *FILE! (Zeiger auf den Typ FILE) , daher fangen sie auch mit einem f an. Es können aber auch die Standardkanäle zur gezielten Ein- und Ausgabe benutzt werden. Beispiele für den Sinn und Nutzen der Kanäle wird es bei putc() und getc() geben.
 
fscanf

Der Befehl verhält sich genau wie scanf - mit nur einem Unterschied, hier wird angegeben woher die Information kommen soll. Wo in der allg. Form Punkte stehen, sind die Variablenadressen aufzulisten.
 

fscanf ( FILE*  , char* , ... );
fscanf ( datei , "%c",  &zeichen);

Wenn wir also folgendes schreiben, so wäre dies equivalent zu einem scanf (...)
 

fscanf (stdin , "%c",  &zeichen );

Der einzige Unterschied: Hier wird von einer Datei gelesen. Die Variable zeichen ist vom Typ char.Analog kann man hier auch andere Zeichen einlesen. Wie an
 
 
fprintf

Und hier kann man ahnen was folgt. Analog zu printf dient dieser Befel zur Datenausgabe.Hier müssen zwei Argumente Übergeben werden, einmal die Datei oder der Ausgabekanal in der Form
 

fprintf ( FILE * datei, char *zeichen, ...);

Sei wie in unserem vorangegangenem Beispiel datei eine geöffnete Datei, so kann man in sie mit dem folgenden Befehlen beispielsweise schreiben
 

fprintf ( datei , "\nHallo - Das File ist geöffnet :-)\n\n");
fprintf ( datei , "\nZahl = %d" , zahl);

Wie sieht, sind die Ausgaberegeln bis auf die zusätzliche Angabe des Ausgabemediums mit printf identisch. Wenn wir die Standardkanäle benutzen kann man statt der Bildschirmausgabe auch folgendes schreiben, was equivalent zu printf("...")ist.
 

fprintf (stdout, "\nHallo - Das File ist geöffnet :-)\n\n" );

 
getc und putc

Vielleicht erinnert sich noch jemand an getchar und putchar ? Mit diesen Funktionen hatte man die Möglichkeit Zeichen einzeln einzulesen und auszugeben.Wie wir schon sahen, gibt es analoge Befehle zu printf und scanf, die auf Files, bzw. Kanäle bezogen sind. Folglich sollte es auch analoge Befehle zu putchar und getchar geben Voila, hier sind sie.
 

char putc (char zeichen, FILE *datei )

Analog zu putchar liefert als Rückgabe entweder das übergebene Zeichen oder ein EOF zurück und haben die gleiche Bedeutung. Bei einem Fehler beim Ausgeben des Zeichen wird EOF zurückgeliefert, ansonsten das übergebene Zeichen selbst.
 

char getc (FILE * datei )

Analog zu getchar wird hier das Zeichen aus dem File gelesen. Auch hier gilt, das bei einem aufgetretenen Fehler der Übergabewert EOF ist. Ein Beispiel, wie man putc und getc anwendet wird nach den
 
Beispielprogramm zu fprintf

So, jetzt können wir schon eine ganze Menge anfangen. Das untere Programm einfach mal ausprobieren und sich das erzeugte File anschauen. Testen Sie den Unterschied zwischen Text- und Binärmodus!
 

/*
Beispiel zum schreiben eines Files
*/
#include <stdio.h>

void main ( void
{

    FILE *datei;

    datei = fopen ( "test.dat" , "w+" );

    if (datei == NULL ) 
    {

      printf ("\nDie Datei konnte nicht geöffnet werden!\n");
    }
    else
    {
      printf ("\nAlles OK!\nIch schreibe in die Datei\n");

      fprintf (datei, "\nUnsere erste Datei :-)\n\n"); 
      fprintf (datei, "Wenn das so weiter geht ist unser Freund Bill arbeitslos\n");

      printf ("\n...und die Datei ist wieder zu\n"); 

      fclose (datei); 

    }
}

Beispielsweise erscheint nach dem Start, wenn alles glatt lief

Screenshot des ausgeführten Programms

und in der Datei test.dat sollte folgendes stehen

Screenshot des ausgeführten Programms

Schauen sie sich nun die Datei einmal an, wenn sie zum öffnen
 

fopen (datei, "w+b");

benutzt haben, dann sieht die Datei gleich anders aus.

Screenshot des ausgeführten Programms


Textdatei erstellen mit putchar und getchar

Wie wir die Daten in einem File abspeichern, haben wir nun geübt. Wenden wir uns nun einem Minimaleditor zu. Am Anfang werden wir abfragen, wie die Datei denn überhaupt heißen soll, die wir erstellen wollen. Der Dateiname darf dabei maximal 24 Buchstaben groß sein. Das 25te Zeichen ist dabei das \0 , wie wir schon im Kapitel über Textfelder lernten.
 

/*
Beispiel eines Minimaleditors
Abbruch des schreibens mit #
*/
#include <stdio.h>

void main ( void
{

    FILE *datei;
    char dateiname[25];
    char zeichen;
    char abbruch = '#';

    printf ("\nBitte Dateinamen angeben : ");
    scanf ("%s",dateiname);

    datei = fopen ( dateiname , "w+" );

    if (datei == NULL ) 
    {

      printf ("\nDie Datei konnte nicht geöffnet werden!\n");
    }
    else
    {
      printf ("\nText eingeben, beenden mit # \n\n");
      while (( zeichen = getchar()) != abbruch ) putc (datei,zeichen);
      printf ("\n\nDanke, das sie mich benutzten\n\n");
      fclose (datei); 
    }
}

Screenshot des ausgeführten Programms

Nachdem wir unser Programm starteten, wählen wir als Beispielnamen beispiel.dat und tippen ein bischen darauf los. Nachdem wir die Eingabe mit # beendeten sind wir natürlich auch neugierig, ob alles abgespeichert wurde, wie wir es wollten. Schauen wir nach.

Screenshot des ausgeführten Programms

Jetzt benötigen wir noch ein Programm, womit wir Textdateien auf dem Bildschirm ausgeben können. Dies geht recht fix, wie wir an folgendem Beispiel sehen werden. Wie zuvor werden wir den Dateinamen der Datei abfragen, die wir darstellen wollen. Schauen wir uns doch gleich mal das File an, welches wir mit dem vorigen Programm geschrieben haben.
 

/*
Beispiel von lesen eines Files und dem Ausgebem
auf dem Bildschirm über stdout
*/
#include <stdio.h>

void main ( void
{

    FILE *datei;
    char dateiname[25];
    char zeichen;
    char abbruch = '#';

    printf ("\nBitte Dateinamen angeben : ");
    scanf ("%s",dateiname);

    datei = fopen ( dateiname , "r" );

    if (datei == NULL ) 
    {

      printf ("\nDie Datei konnte nicht geöffnet werden!\n");
    }
    else
    {
      while (( zeichen = getc(datei)) != abbruch ) putc (zeichen, stdout);
      fclose (datei); 
      printf ("\n\nDanke, das sie mich benutzten\n\n");
    }
}

Screenshot des ausgeführten Programms

Mit diesem Programm können wir, da wir putc benutzen, die Ausgabe einfach in ein File umleiten.
 
Textdatei erstellen mit Standardkanälen

 

/*
Beispiel der Ein- und Ausgabe mittels Standardkanälen

Es wird solange von stdin gelesen und auf stdout ausgegeben,
bis # eingelesen wird.

*/
#include <stdio.h>

void main ( void
{

    char zeichen;
    char abbruch = '#';

    while (( zeichen = getc(stdin)) != abbruch ) putc (zeichen, stdout);

}

Starten wir das Programm und probieren aus, auf diesem Weg alles, was wir auf der Tastaur eingeben, in ein File zu schreiben, bis wir # eingeben. Auf diese weise sollte wir, ähnlich den vorigen Programmen, recht einfach eine Textdatei erstellen können. Das Programm wurde in diesem Beispiel kanal.c benannt und der Name der ausführbaren Datei ist kanal.exe . Das zu erzeugende File wurde stdout.txt genannt.

Screenshot des ausgeführten Programms

Nachdem ein bischen Text eingegeben wurde, sollte nun der Text in der Datei stdout.txt stehen, da die Ausgabe dorthin umgeleitet wurde. Schauen wir es uns mit einem Editor an. Wie zu sehen ist, machte das Programm was wir uns vorher überlegten. Da das # unsere Abbruchbedingung ist, wurde es nicht mehr abgespeichert.

Screenshot des ausgeführten Programms

Üben wir ein bischen, den Umgang mit den Ein- und Ausgabekanälen ein bischen. In der Einleitung zu den Kanälen wurde erwähnt, das z.B. durch Umlenkung Files als Ein- und Ausgabekanal gesetzt werden können. Doch wie sieht dies nun im Detail aus ? Im Prinzip haben wir das gerade gemacht.
 

kanal.exe > stdout.txt

Hier wird beispielsweise das File stdout.txt zur Standardausgabe. Schreiben wir nun ein Programm, welches die Standardeingabe stdin nimmt, und sie in Großbuchstaben umwandelt und anschließend auf der Standardausgabe stdout ausgibt. Wie wir sehen, zeigt stdin auf stdout.txt. Die Funktionen toupper zum umwandeln der Zeichen in Großbuchstaben finden wir in ctype.h .Zum testen nehmen wir das soeben erzeugte File stdout.txt.
 

/*
Beispiel der Ein- und Ausgabe mittels Standardkanälen
mit Umwandlung in Großbuchstaben
ACHTUNG! Abbruchbedingung ist EOF, damit nur bis
zum Textende die Zeichen ausgegeben werden. Sonst kann
es bei Files zu Problemen kommen
*/
#include <stdio.h>
#include <ctype.h>

void main ( void
{

    char zeichen;
    char abbruch = EOF;

    while (( zeichen = getc(stdin)) != abbruch ) putc (toupper(zeichen), stdout);

}

 
...das Obligatorische

Autor: Sebastian Cyris \ PCD Bascht

Dieser C-Kurs dient nur zu Lehrzwecken! Eine Vervielfältigung ist ohne vorherige Absprache mit dem Autor verboten! Die verwendete Software unterliegt der GPL und unterliegt der Software beiliegenden Bestimmungen zu deren Nutzung! Jede weitere Lizenzbestimmung die der benutzten Software beiliegt, ist zu beachten!