Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » FAQ C / C++ (ANSI-Standard) » Zeilenweise einlesen in C (Anfänger)

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
17.08.2004, 13:48 Uhr
virtual
Sexiest Bit alive
(Operator)


Zeilenweises Einlesen in C (ohne ++)

Wer in C++ eine Datei zeilenweise einlesen will, hat es vergleichsweise einfach: er/sie nimmt std::string Objekte, läßt dort den Speicher verwalten und fackelt das einlesen bequem mittels der Funktion std::getline ab.

gets
In der guten alten C Welt sieht die Sache etwas komplizierter aus: Zunächst gibt es aus ganz alten Zeiten noch eine antiquierte Funktion namens gets die leider ganz gerne eingesetzt wird, dadurch aber nicht ungefährlicher wird: gets. Zum einen eignet sich gets nur zum lesen von genau einer Datei, nämlich stdin, was meist einfach die Tastatur ist. Was gets aber nun wirklich gefährlich macht ist die Tatsache, daß man nicht weiß wie viel der Benutzer nun eingegeben hat. zB ist folgender Code ziemlich gefährlich:

C++:

void eingabe()
{
    char name[20+1];
    printf("Bitte Namen eingeben, max. 20 Zeichen: ");
    fflush(stdout);
    gets(name);
    ...

}


Wenn der Benutzer böse ist, kann er mehr als 20 Zeichen eingeben und das Programm läuft im Zweifel gegen die Wand - oder auch nicht: wenn der Benutzer nämlich wirklich dolle böse ist, kann er mit etwas Geduld und ggf. genau berechneten Eingaben den Stack des Programms derart überschreiben, daß es ganz woanders hinläuft als man eigentlich will. Fertig ist dann das Sicherheitsloch...
gets sollte nie nie nie nie nie in einem Programm auftauchen!

fgets
Nun ist man ja nicht doof gewesen bei der ANSI und hat die Fuktion fgets erfunden. Die ist zwar etwas komplizierter zu bedinen, weil sie drei an Stelle von einem Parameter hat, aber dafür beseitigt sie zum einen das Problem, daß nur von stdin gelesen wird, zum anderen räumt sie mit dem Problem auf, daß ein benutzer böse sein könnte (er kann es zwar noch immer sein, aber das hilft ihm nicht mehr so viel).
fgets erwartet die folgenden drei Parameter:
1. Einen zeiger, der angibt, wohin das eingelesen Zeug hinkommen soll.
2. Eine Längenangabe, die angibt, wie viel Speicher da frei ist.
3. Ein Zeiger auf ein FILE, das angibt, von wo aus gelesen werden soll.
Das folgende Beispiel zeigt nun wie das ganze in Aktion aussieht:

C++:
void eingabe()
{
    char name[20+1];
    printf("Bitte Namen eingeben, max. 20 Zeichen: ");
    fflush(stdout);
    fgets(name, sizeof(name), stdin);
    ...
}


Jetzt kann der Benutzer zwar auf die Idee kommen, mehr als 20 einzugeben, aber das hilt ihm nicht viel: es wird einfach abgeschnitten. Ziemliches Pech für Finnen, die traditionell recht lange Nachnamen haben können.
Ein weiteres Problem ist außerdem - übrigens auch bei gets - daß das Newline am Ende der Eingabe ebenfalls mit kopiert wird, jedenfalls wenn eins da ist. Grade bei interaktiven Eingaben wie oben will man das eigentlich nicht haben. Man ist daher gezwungen, zunächst zu prüfen, ob das Newline da ist und dieses dann ggf. zu entfernen.

Eigeninitiative gefragt
Nach meinem Kenntnisstand gibt der reine Standard derzeit nichts besseres als fgets her. Aber man muß sich damit ja dennoch nicht zufrieden geben, denn man kann ja auf basis von zB fgets erweiterungen selber basteln:

C++:
#include <stdio.h>

char* myfgets(FILE* in) {
    static char* buffer = NULL;
    static size_t buffer_size = 0;
    static size_t INCREMENT = 10;
    size_t len = 0;

    /*
     * Handle special call - in==NULL means release all memory
     */

    if (NULL == in) {
        if (NULL != buffer) free(buffer);
        buffer = NULL;
        buffer_size = 0;
        return NULL;
    }

    len = 0;
    for(;;) {
        /*
         * Resize buffer, if required (at least one character must fit).
         */

        if (buffer_size-len<2) {
            char* temp = (char*)realloc(buffer, buffer_size+INCREMENT);
            if (temp==NULL) {
                return NULL;
            }
            buffer = temp;
            buffer_size += INCREMENT;
        }

        /*
         * Read next portion of string
         */

        if (NULL == fgets(buffer+len, buffer_size-len, in)) {
            return NULL;
        }

        /*
         * Check for new line at end of buffer
         */

        len += strlen(buffer+len);
        if (len>0 && buffer[len-1]=='\n') {
            buffer[--len] = 0;
            break;
        }
    }

    return buffer;
}


Bei dieser Implementierung ist die Eingabelänge nicht beschränkt, intern arbeitet die Routine mit einem Buffer, der dynamisch immer erweitert wird, wenn bedarf besteht. Auch das oben angesprochene Problem mit dem Newline wird hier gehandelt: es wird jedenfalls abgeschnitten.
Generell erwartet die Routine ein FILE*, von dem gelesen werden soll und gibt einen Zeiger auf den Buffer zurück, in den gelesen wurde. Dieser Buffer enthält garantiert immer die ganze Zeile. Folgendes Programm demonstriert die Anwendung:

C++:
/* Liest zeilenweise ein und gibt zeilenweise aus. */
int main()
{
    char* buffer;
    while(NULL != (buffer=myfgets(stdin))) {
        puts(buffer);
    }

    myfgets(NULL); /* Releases any memory */
}


Natürlich hat auch diese Routine nachteile:
1. Sie ist wg. der Verwendung von static Attributes nicht multithreadingfähig. Aber das gilt eh für die gesamte ANSI Library, weil Threads nicht standardisiert sind.
2. ein erneuter Aufruf von myfgets überschreibt das alte Resultat. Ist aber in den meisten Situationen kein ernstes Problem: man kopiert den ganzen Rotz nach dem Einlesen einfach dahin, wo man es braucht.
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)

Dieser Post wurde am 17.08.2004 um 13:50 Uhr von virtual editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ FAQ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: