Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 18 Ein-/Ausgabe-Funktionen
  gp 18.1 Was ist eine Datei?
  gp 18.2 Formatierte und unformatierte Ein-/Ausgabe
  gp 18.3 Streams
  gp 18.4 Höhere Ein-/Ausgabe-Funktionen
  gp 18.5 Datei (Stream) öffnen – fopen
    gp 18.5.1 Modus für fopen()
    gp 18.5.2 Maximale Anzahl geöffneter Dateien – FOPEN_MAX
  gp 18.6 Zeichenweise Lesen und Schreiben – getchar und putchar
    gp 18.6.1 Ein etwas portableres getch()
  gp 18.7 Zeichenweise Lesen und Schreiben – putc/fputc und getc/fgetc
  gp 18.8 Datei (Stream) schließen – fclose
  gp 18.9 Formatiertes Einlesen/Ausgeben von Streams mit fprintf und fscanf
  gp 18.10 Standard-Streams in C
    gp 18.10.1 Standard-Streams umleiten
  gp 18.11 Fehlerbehandlung von Streams – feof, ferror und clearerr
  gp 18.12 Gelesenes Zeichen in die Eingabe zurück-schieben – ungetc
  gp 18.13 (Tastatur-)Puffer leeren – fflush
    gp 18.13.1 Pufferung
  gp 18.14 Stream positionieren – fseek, rewind und ftell
  gp 18.15 Stream positionieren – fsetpos, fgetpos
  gp 18.16 Zeilenweise Ein-/Ausgabe von Streams
    gp 18.16.1 Zeilenweise Lesen mit gets/fgets
    gp 18.16.2 Zeilenweise Schreiben mit puts/fputs
    gp 18.16.3 Zeilenweise Einlesen vom Stream mit getline() (nicht ANSI C)
    gp 18.16.4 Rezepte für zeilenweises Einlesen und Ausgeben
  gp 18.17 Blockweise Lesen und Schreiben – fread und fwrite
    gp 18.17.1 Blockweises Lesen – fread()
    gp 18.17.2 Blockweises Schreiben – fwrite()
    gp 18.17.3 Big-Endian und Little-Endian
  gp 18.18 Datei (Stream) erneut öffnen – freopen
  gp 18.19 Datei löschen oder umbenennen – remove und rename
    gp 18.19.1 remove()
    gp 18.19.2 rename()
  gp 18.20 Pufferung einstellen – setbuf und setvbuf
  gp 18.21 Temporäre Dateien erzeugen – tmpfile und tmpnam
    gp 18.21.1 mkstemp() – Sichere Alternative für Linux/UNIX (nicht ANSI C)
  gp 18.22 Fehlerausgabe mit strerror und perror
  gp 18.23 Formatiert in einem String schreiben und formatiert aus einem String lesen – sscanf und sprintf
  gp 18.24 Fortgeschrittenes Thema
  gp 18.25 Low-Level-Datei-I/O-Funktionen (nicht ANSI C)
  gp 18.26 Datei öffnen – open
  gp 18.27 Datei schließen – close
  gp 18.28 Datei erzeugen – creat
  gp 18.29 Schreiben und Lesen – write und read
  gp 18.30 File-Deskriptor positionieren – lseek
  gp 18.31 File-Deskriptor von einem Stream – fileno
  gp 18.32 Stream von File-Deskriptor – fdopen


Galileo Computing - Zum Seitenanfang

18.24 Fortgeschrittenes Thema  toptop

Mittlerweile haben Sie ja schon einiges gelernt in diesem Buch. Aber irgendwie waren alle Themen doch sehr theoretisch. Es wurde vorwiegend gezeigt, wie Sie eine Funktion einsetzen können und was dabei beachtet werden muss. Daher soll in diesem Abschnitt einmal etwas Praktisches gemacht werden, womit einige C-typische Stärken aufgezeigt werden können. Es muss auch erwähnt werden, dass dieses Programmbeispiel Ihnen einiges an Wissen abverlangt. Im Listing werden aber keine Konstrukte von C verwendet, mit denen Sie bisher noch nichts zu tun hatten. Wenn Sie so wollen, stellt dieses Programm eine Art Zwischenprüfung Ihrer Kenntnisse in C dar.

Es soll gezeigt werden, wie Sie einen Text einer Datei dynamisch in den Speicher lesen können; wie dies bei Textverarbeitungsprogrammen geschieht. Eine einfache Textdatei ist dabei wie folgt strukturiert:

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 18.11   Einfache Strukturierung einer Textdatei

Sicher ist dies nur eine einfache Strukturierung einer Textdatei. In der Regel fehlen hierbei z.B. noch die Absätze oder die Seitenzahlen. Aber für unseren Fall genügt dies.

Folgende Funktionen benötigen Sie für diese Aufgabe:

gp  Eine Funktion, welche eine Datei zum zeilenweisen Lesen öffnet
gp  Eine Funktion, die dynamischen Speicher für einen String anfordert
gp  Eine Funktion, die den dynamischen Speicher erweitert (eine Zeile hinzufügt)
gp  Eine Funktion, welche die Größe einer Zeile anpasst und überflüssigen Speicher wieder freigibt
gp  Eine Funktion, die den dynamischen Speicher verringert
gp  Eine Funktion, die allokierten Speicher freigibt

Hier jetzt das vollständige Listing, welches sehr ausführlich kommentiert ist:

/* dyn_text.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* symb. Konstanten */
#define LINE 255
#define ALLOC_LINE 10
enum { SUCCESS, ERROR };
/* Funktions-Prototypen */
int read_file(char ***, char *, int *);
char **alloc_string_array(int, int);
int zeilen_hinzu_fuegen(char ***, int, int, int);
int string_anpassen(char **, int);
int string_groesse_veraendern(char **, int, int);
void free_string_array(char **, int);
/*  ***array == Ein Zeiger auf einen Zeiger einer Zeile mit
 *  einem Zeiger auf die Länge der Zeile (*array[zeile][länge])
 *  *fname == Name der zu öffnenden Datei
 *  *zeile_n == Zeiger auf Anzahl Zeilen
 *  Rückgabewert: SUCCESS wenn kein Fehler, ansonsten ERROR
 */
int read_file(char ***array, char *fname, int *zeile_n) {
 char puffer[LINE] = {0}; /* Puffer zum zeilenweisen Einlesen  */
 char *newline = NULL;    /* Zeiger für neue Zeile             */
 FILE *f;                 /* Datei, welche geöffnet werden soll*/
 int error = SUCCESS;     /* Fehlerstatus                      */
 int zeile = 0;           /* aktuelle Zeile                    */
 int absatz_n;
 *zeile_n = 0;            /* Erste Zeile mit 0 initialisieren  */
 /* Speicher anfordern für ALLOC_LINE Zeilen a LINE Zeichen    */
 *array = alloc_string_array(ALLOC_LINE, LINE);
 if(NULL != *array) {
    f = fopen(fname, "r"); /* Datei fname zum Lesen öffnen */
    if(NULL != f) {
       *zeile_n = ALLOC_LINE;
       absatz_n = 0;
       /* Solange kein Fehler auftritt, zeilenweise einlesen */
       while(0 == error && NULL != fgets(puffer, LINE, f)) {
          newline = strchr(puffer, '\n');
          if(NULL != newline)
             /* Newline-Zeichen gegen
              * Terminierungszeichen austauschen */
               *newline = '\0';
             strcat( (*array)[zeile], puffer);
             if(NULL != newline) {
                absatz_n = 1;
                zeile++;
                /* Haben wir noch Platz im Speicher
                 * für weitere Zeilen */
                if(zeile >= *zeile_n) {
                   /* Nein, dann anfügen */
                   if(0 == zeilen_hinzu_fuegen(
                    array, *zeile_n, ALLOC_LINE, LINE) )
                      error = ERROR;
                   else
                      /* Anzahl der Zeilen + 10 */
                      *zeile_n += ALLOC_LINE;
                }
             }
             /* Kein Newline-Zeichen, dann Zeile länger als LINE
              * Zeichen, String der Länge anpassen  ->  Speicher
              * anfordern */
             else {
                absatz_n++;
                if(0 == string_groesse_veraendern(
                 *array, zeile, absatz_n * LINE))
                   error = ERROR;
             }
       }/*while*/
       fclose(f);
       /* Wir sind am Ende vom Einlesen oder ein Fehler trat auf.
        * Es müssen allerdings noch die übrige(n) Zeile(n) in
        * den Speicher eingelesen werden */
       if(0 == error && *zeile_n > zeile) {
          if(0 == zeilen_hinzu_fuegen(
           array, *zeile_n, zeile-*zeile_n, 0))
             error = ERROR;
          *zeile_n = zeile;
       }
    }
    else  /* Datei fname konnte nicht geöffnet werden */
       error = ERROR;
 }
 else   /* Es konnte kein Speicher allokiert werden */
     error = ERROR;
 if(error != 0) {
    /* Im Fall eines Fehlers Speicher wieder freigeben
     * und Anzahl Zeilen auf 0 */
    free_string_array(*array, *zeile_n);
    *zeile_n = 0;
 }
 else
     string_anpassen(*array, *zeile_n);
 return error;
}
/*   zeilen_n == Anzahl Zeilen, wieviele reserviert werden sollen
 *   laenge   == Speicherplatz für die Länge jeder Zeile,
 *               die angefordert wird
 *   Rückgabewert: Anfangsadresse des reservierten Speichers vom
 *                 String-Array  (array[zeile][laenge])
 */
char **alloc_string_array(int zeilen_n, int laenge) {
 char **array = NULL;
 int zeile;
 int ret = SUCCESS;
 if(zeilen_n > 0 && laenge > 0) {
    /* Speicher für zeilen_n Zeilen reservieren */
    array = (char **)malloc(zeilen_n * sizeof(*array));
    if(NULL != array) {
       for(zeile=0; zeile < zeilen_n; zeile++) {
          /* Für jede zeile_n Zeile laenge Bytes
           * Speicher reservieren */
          array[zeile] =
             (char *) malloc( laenge * sizeof(*array[zeile]) );
          if(NULL == array[zeile])
             ret = ERROR;
          else
             /*jede Zeile erstes Zeichen mit \0 initialisieren*/
             array[zeile][0] = '\0';
       }
       if(ERROR == ret) {  /* Bei Fehler Speicher freigeben */
          free_string_array(array, zeilen_n);
          array = NULL;
       }
    }
 }
 return array;
}
/*  ***array      == Ein Zeiger auf einen Zeiger einer Zeile mit
 * einem Zeiger auf die Länge der Zeile ( *array[zeile][länge])
 * alt_n_zeilen   == Anzahl akt. Zeilen im Speicher
 * n_zeilen_hinzu == Anzahl Zeilen, für die neuer Speicherplatz
 * reserviert werden soll. Bei negativen Wert werden n Zeilen
 * entfernt
 * init_laenge    == Speicherplatz für die Länge jeder Zeile, die
 *                   angefordert wird
 * Rückgabewert: 1 wenn Ok, ansonsten 0
 */
int zeilen_hinzu_fuegen(char ***array_ptr, int alt_n_zeilen,
                        int n_zeilen_hinzu, int init_laenge) {
 char **ptr;
 int ret = 1;
 int zeile;
 int anzahl_alte_zeilen = alt_n_zeilen;
 /* Ein negativer Wert bedeutet Zeilen entfernen */
 if(n_zeilen_hinzu < 0) {
    for(zeile=anzahl_alte_zeilen-1;
     zeile >= anzahl_alte_zeilen+n_zeilen_hinzu; zeile--)
       free( (*array_ptr)[zeile]);
 }
 /* Speicher für einzelne Zeilen reservieren */
 ptr=(char **)realloc( *array_ptr,
  (anzahl_alte_zeilen+n_zeilen_hinzu)*sizeof(**array_ptr));
 if(NULL != ptr) {
    *array_ptr = ptr;
    for(zeile=anzahl_alte_zeilen;
     ret && zeile < anzahl_alte_zeilen+n_zeilen_hinzu; zeile++) {
       /* Anzahl der Zeichen, welche jede Zeile
        * aufnehmen kann, reservieren */
       (*array_ptr)[zeile] = (char *) malloc(init_laenge);
       if( NULL != (*array_ptr)[zeile])
          /* jede Zeile erstes Zeichen mit \0 initialisieren */
          (*array_ptr)[zeile][0] = '\0';
       else
          ret = 0;
    }
 }
 else
    ret = 0;
 return ret;
}
/*  **array_ptr == Ein Zeiger auf das String-Array
 *                 array[zeile][laenge]
 *      zeile_n == Anzahl Zeilen, welche angepasst werden
 * Rückgabewert bei Erfolg 0, ansonsten größer als 0
 */
int string_anpassen(char **array_ptr, int zeile_n) {
 int zeile;
 int anzahl_zeichen;
 int fehlschlag = 0;
 for(zeile = 0; zeile < zeile_n; zeile++) {
    /* Funktion strlen liest das Terminierungszeichen
     * '\0' nicht mit -> daher +1 */
     anzahl_zeichen = strlen(array_ptr[zeile])+1;
     if(0 == string_groesse_veraendern(
      array_ptr, zeile, anzahl_zeichen))
        fehlschlag++;
 }
 return fehlschlag;
}
/*  **array_ptr == Ein Zeiger (Adresse) auf das String-Array
 *  array[zeile][laenge]
 *      zeile   == Zeile, welche verändert werden soll
 * neu_laenge   == Anzahl Zeichen, die für die Zeile verändert
 *                 werden soll
 * Rückgabewert bei Erfolg SUCCESS, ansonsten bei Fehler ERROR
 */
int string_groesse_veraendern(char **array, int zeile,
                              int neu_laenge) {
 char *ptr;
 int ret = SUCCESS;
 ptr = (char *) realloc(array[zeile], neu_laenge);
 if(ptr != NULL)
   array[zeile] = ptr;
 else
   ret = ERROR;
 return ret;
}
/*  **array_ptr == Ein Zeiger (Adresse) auf das String-Array
 *                 array[zeile][laenge]
 *    n_zeile   == Anzahl Zeilen, die freigegeben werden sollen*/
void free_string_array(char **array, int n_zeilen) {
 int zeile;
 if(array != NULL) {
    for(zeile= 0; zeile < n_zeilen; zeile++) {
       if(array[zeile] != NULL)
          free(array[zeile]);
    }
 }
 free(array);
}
int main(void) {
 char **array = NULL;
 char datei[255];
 int zeilen=0, i, auswahl, n, m;
 do {
    printf("Was wollen Sie tun?\n\n");
    printf("-1- Datei komplett in den Speicher einlesen\n");
    printf("-2- Inhalt der Datei im Speicher ausgeben\n");
    printf("-3- Datei im Speicher v. Zeile n bis m ausgeben\n");
    printf("-4- Den Speicher wieder freigeben\n");
    printf("-5- Ende\n\n");
    printf("Ihre Wahl : < >\b\b");
    scanf("%d",&auswahl);fflush(stdin);
    switch(auswahl) {
       case 1 : printf("Datei angeben( mit Pfadangabe ): ");
                scanf("%254s",datei);
                fflush(stdin);
                if( (read_file(&array, datei, &zeilen))==ERROR)
                    printf("Fehler beim Lesen in Speicher!!!\n");
                break;
       case 2 : if(zeilen == 0)
                   printf("Keine Daten vorhanden!\n");
                else
                   for(i=0; i<=zeilen-1; i++)
                      printf("%s\n",array[i]);
                break;
       case 3 : printf("Zeilenbeginn: ");
                scanf("%d",&n);
                fflush(stdin);
                if(n > 0 && n <= zeilen-1) {
                   printf("bis zur Zeile: ");
                   scanf("%d",&m);
                   fflush(stdin);
                   if(m >= n && m <= zeilen-1) {
                      for(i=n; i<=m-1; i++)
                         printf("%s\n",array[i]);
                   }
                   else
                      printf("??>>%d<<??\n",m);
                }
                else
                   printf("??>>%d<<??\n",n);
                break;
       case 4 : free_string_array(array, zeilen);
                zeilen=0;
                break;
       default: break;
    }
 } while(auswahl != 5);
 return EXIT_SUCCESS;
}
 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de