Text Datei einlesen, Zeilenweise, variable Länge



  • Hallo an Alle.

    Bin frischer Anfänger in C und komme mit meinem Problem nicht weiter. Wie im Titel schon, will ich eine Datei zeilenweise einlesen, wobei ich variable Längen von Zeilen habe.

    Nach recherchieren im Netz weiss ich, dass ich also falls der allocated Speicher nicht reicht, ich ihn vergrößern muss.

    Soviel hab ich bis jetzt gemacht:

    void open(char *link)
    {
    
       FILE *file = fopen(link, "r");
    
       if (file == NULL) 
        {
            fprintf(stderr, "Cannot open file\n");
            exit(1);
        }
    
    int max_size = 1000;
        int line_num = 0;
    
        char *line = (char *) malloc(sizeof(char)*max_size);
        line[0] = '\0';
    
        while (fgets(&line[strlen(line)],max_size,file)!= NULL) 
        {
    
            if (line[strlen(line)-1] == '\n') //dann wurde die ganze Zeile gespeichert
            {
    
            } else {
    
                printf("reallocate to %d\n", max_size);
            max_size*=2;
            line = (char *) realloc(line, max_size);
    
            }
        }
    
        free(line);
        fclose(file);
    }
    

    Ist das soweit richtig?
    1.
    Ein Problem habe ich aber, durch die Ausgabe von line ist mir aufgefallen, dass die neuen Zeilen drangehangen werden, wobei ich eher möchte, dass eine neue Zeile den Eintrag von line ersetzt, wo ist mein Fehler hier :S



  • Du hängst ja auch immer munter an den bisherigen Text an. (durch das &line[strlen(line)])

    Somit ist dein Aufruf von fgets auch falsch, da du ja nicht den kompletten Puffer (line) zur Verfügung stellst.
    (wenn du schon 100 Zeichen gelesen hast, bleiben nur noch 900 übrig)

    Wenn du festgestellt hast, dass du eine ganze Zeile (mit '\n') gelesen hast musst du die Länge wieder zurücksetzen (wie in Zeile 16)

    Ein leerer if-Zweig sieht etwas merkwürdig aus. Da kannst du auf ungleich testen.



  • Es muss

    while (fgets(&line[strlen(line)],max_size-strlen(line),file)!= NULL)
    

    heißen, weil du sonst über die Arraybounds hinausschreibst. Wenn z.B. max_len 4 ist, alle belegt sind, dann verdoppelst du die size und liest 8 weitere Bytes ein. dann hättest du aber 12 Bytes verbraucht.

    Im Übrigen musst du mit der letzten Zeile aufpassen. fgets ließt bis EOF, dann ist dort allerdings kein "\n" am Ende.



  • DarkShadow44 schrieb:

    Im Übrigen musst du mit der letzten Zeile aufpassen. fgets ließt bis EOF, dann ist dort allerdings kein "\n" am Ende.

    Heisst dass, ich sollte bei der while schleife (!= EOF) setzen?

    DirkB schrieb:

    Du hängst ja auch immer munter an den bisherigen Text an. (durch das &line[strlen(line)])

    Also muss ich einfach das line[0] = '\0' setzen, damit ich das Ergebnis habe?
    Oder wie wird das sonst immer gelöst?



  • tinchi schrieb:

    Heisst dass, ich sollte bei der while schleife (!= EOF) setzen?

    Nein, denn fgets gibt kein EOF zurück.
    Kannst du aber auch nachlesen: http://www.cplusplus.com/reference/cstdio/fgets/

    tinchi schrieb:

    Also muss ich einfach das line[0] = '\0' setzen, damit ich das Ergebnis habe?
    Oder wie wird das sonst immer gelöst?

    Ja, so wird die Länge eines C-Strings auf Null gesetzt.
    Du musst schon aufpassen, an welcher Stelle du das line[0] = '\0'; machst. Sonst löscht du dir die Daten, bevor du sie benutzt hast.



  • Also ich habe den String jetzt so wieder auf leer gesetzt, und dabei kriege ich ein Segmentation fault Fehlermeldung :S. Hab ich hier iwie was falsch gemacht?
    Er ließt die erste Zeile und bei der zweiten kommt dann die Fehlermeldung

    void open(char *link)
    {
    
       FILE *file = fopen(link, "r");
    
       if (file == NULL) 
        {
            fprintf(stderr, "Cannot open given link\n");
            exit(1);
        }
    
    int max_size = 1000;
        int line_num = 0;
    
        char *line = (char *) malloc(sizeof(char)*max_size);
        line[0] = '\0';
    
        while (fgets(&line[strlen(line)],max_size,file)!= NULL) 
        {
            printf("print line %s \n", line);
            if (line[strlen(line)-1] == '\n')
            {
    
                line = '\0';
                printf("long enough\n");
            } else {
    
                printf("reallocate to %d\n", max_size);
            max_size*=2;
            line = (char *) realloc(line, max_size);
    
            }
        }
    
        free(line);
        fclose(file);
    
    }
    


  • Ne hab den Fehler jetzt doch!!

    Sollte ja : line[0] und nicht line heißen!

    Aber ein Problem habe ich noch, wie fängt man denn die letzte Zeile einer Datei ab? Da die ja nicht mit "\n" endet denkt jetzt mein Programm, er müsste mehr Speicher allocaten.



  • Die letzte Zeile hat ein \0 am Ende. Textdateien beinhalten grundsätzlich mitten drin kein \0. Man kann sie als Datenstrom ansehen und die werden nur durch \n halt am Bildschirm zeilenweise angezeigt.Das ist nicht so wie auf ner Z/Os wo man mit "echten" Datensätzen arbeitet. Hat bei mir auch ne Weile gedauert, um das so zu sehen. Nicht umsonst heissen die Funktionen auch immer irgendwas mit stream.
    War für mich, wo ich aus der IBM-Großrechnerwelt komme, erst mal ein Umdenken.



  • Ok, also hier nochmal mein End-code

    void open(char *link)
    {
    
       FILE *file = fopen(link, "r");
    
       if (file == NULL) 
        {
            fprintf(stderr, "Cannot open given link\n");
            exit(1);
        }
    
    int max_size = 100;
        int line_num = 0;
    
        char *line = (char *) malloc(sizeof(char)*max_size);
        line[0] = '\0';
    
        while (fgets(&line[strlen(line)],max_size,file)!= NULL) 
        {
            if (line[strlen(line)-1] == '\n')
            {
    
                line[0] = '\0';
    //do something with string
                printf("long enough\n");
            } else {
    
                printf("reallocate to %d\n", max_size);
            max_size*=2;
            line = (char *) realloc(line, max_size);
    
            if (line[strlen(line)] == '\0') {
                printf("jetzt ist aber ende erreicht \n");
            }
    
            }
        }
    
        free(line);
        fclose(file);
    
    }
    

    Passt das jetzt so?
    Was ich mir aber noch gerade frage, wenn mein Programm in dem -else- teil landet, woher weiss es, dass es nach dem reallocate wieder in die while schleife soll, um beim letzten durchlauf nicht mehr?

    Das fgets() liefert doch den gespeicherten String wieder oder? Und null halt wenn alle Zeilen durchgelaufen sind?



  • Gelich mal vorweg:
    In Zeile 25 kannst du nicht mehr viel mit dem Inhalt von line machen, da du vorher dessen Länge auf Null gesetzt hast.

    Zur letzten Zeile:

    fgets schrieb:

    Return Value
    ...
    If the end-of-file is encountered while attempting to read a character, the eof indicator is set (feof). If this happens before any characters could be read, the pointer returned is a null pointer (and the contents of str remain unchanged). ...

    Wenn die letzte Zeile kein '\n' enthält, wird der Inhalt eingelesen und EOF wird gesetzt. Dies kannst du mit der Funktion feof abfragen.

    Die '\0' kennzeichnet das Ende vom String. Diese wird automatisch von (fast*) allen Funktionen (der Standard-Library) angehängt, die mit C-Strings arbeiten.
    Daher ist dein Test (line[strlen(line)] == '\0') immer wahr.

    Die '\0' steht nicht in der Textdatei. (Und wenn ja, ist es schwierig diese mit Stringfunktionen zu bearbeiten)

    Das &line[strlen(line)] ist kurz line+strlen(line)

    open ist kein guter Name für eine eigene Funktion, da es diesen schon im Posix-Standard gibt.

    *strncpy ist so eine Ausnahme, wenn die Maximalzahl der Zeichen erreicht wird.



  • DirkB schrieb:

    Gelich mal vorweg:
    In Zeile 25 kannst du nicht mehr viel mit dem Inhalt von line machen, da du vorher dessen Länge auf Null gesetzt hast.

    Ja ist mir auch aufgefallen, hatte das für das Beispiel schnell reingeschrieben..

    DirkB schrieb:

    If the end-of-file is encountered while attempting to read a character, the eof indicator is set (feof). If this happens before any characters could be read, the pointer returned is a null pointer (and the contents of str remain unchanged).

    Gibt es denn eine Seite/Bibliothek wo alle Methoden stehen, die man in C verwenden kann, oder muss ich immer alles googeln?

    DirkB schrieb:

    Wenn die letzte Zeile kein '\n' enthält, wird der Inhalt eingelesen und EOF wird gesetzt. Dies kannst du mit der Funktion feof abfragen.

    Ja habe ich jetzt mit der Funktion gemacht, das klappt super. Vielen Dank!

    DirkB schrieb:

    Daher ist dein Test (line[strlen(line)] == '\0') immer wahr.

    ... Ah klar, ja jetzt verstehe ich es... Danke.

    DirkB schrieb:

    Das &line[strlen(line)] ist kurz line+strlen(line)

    Danke für den Tipp, ist noch alles ein wenig verwirrend mit den Pointern und Arrays, die iwie "gleich" manchmal erscheinen.



  • tinchi schrieb:

    Gibt es denn eine Seite/Bibliothek wo alle Methoden stehen, die man in C verwenden kann, oder muss ich immer alles googeln?

    Du kannst auf der Seite, die ich dir für fgets und feof schon genannt habe weiter schmökern. (http://www.cplusplus.com/reference/clibrary/)

    Es gibt auch noch http://www.cppreference.com/

    Diese Seiten sind zwar fü C++, aber die Unterschiede werden genannt.

    Und auf der ersten Seite von diesem Unterforum, gibt es einen als wichtig markierten Thread: Linkliste für Neulinge
    Dort gibt es Links zu den verschiedenen C-Standars. (Wenn du wirklich das genaue Verhalten wissen willst)


Anmelden zum Antworten