Liste aus Text-Datei einlesen und als Variable speichern



  • Hallo,

    ich habe folgendes Problem : Ich habe eine Textdatei (.txt) mit Vor- und Nachnamen, und habe nachher vor sie über C zu ordnen.

    Vor- und Nachname sind jeweils in einer Zeile mit einem Leerzeichen getrennt.

    Dazu hatte ich die Idee ein Strukt für Vor- und Nachname zu erstellen, darüber ein Array zu erstellen und mit einer for-Schleife nach und nach die Vor- und Nachnamen jeweils in ein weiteres Struct-Element zu packen. Diese füge ich dann sortiert in eine zunächst leere Liste ein.

    Meine Frage : Wie bekomme ich die Daten aus der Textdatei in die Struct-Elemente hinein?

    Als Rahmen habe ich bisher folgendes :

    #include <stdio.h>
    
    typedef struct name {
    	char vorname[100];
    	char nachname[100];
    }name;
    
    name Person[100];			// Anzahl Zeilen der Datei
    
    int main() {
    	int i;
    	for(i=1;i<10;i++){
    		Person[i].vorname[] = {" ... "};  // Vorname aus i+1. Zeile aus Datei
    		Person[i].nachname[] = {" ... "}; // Nachname aus i+1. Zeile aus Datei
    	}	
    }
    

    Und lässt sich die Anzahl der Zeilen der Textdatei auch ermitteln?

    Gruß



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum Rund um die Programmierung in das Forum C (alle ISO-Standards) verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • struct-Listen sind der Klassiker in C.
    Globale Variablen sind Müll.
    Du brauchst Dateifunktionen (fopen,fgets,fclose) zum zeilenweisen Einlesen in einen temporären String, aus dem du dann mit Stringfunktionen (sscanf) in ein zuvor mit realloc bereitgestelltes struct-Element einer Liste einliest.
    Direkte Arrayzuweisungen Person[i].nachname = ; gibt es in C nicht.
    Wenn die Datei am Ende ist, kannst du mit deiner struct-Liste dann Sortieren, z.B. mit qsort.



  • Ach, das kommt alles so darauf an.
    Wenn du bereit bist, nicht-standardisierte Funktionen zu verwenden, kannst du die Funktionen zum Einladen von Dateiinhalten deines Betriebssystems verwenden. Ich hatte immer immense Schwierigkeiten mit fopen und fgets und dem ganzen Kack - wenn du Windows verwendest, kannst du auf die Windows-API zurückgreifen und mit CreateFileA/W und CloseHandle und ReadFile arbeiten, einfach mal googlen.
    Unter POSIX-kompartiblen Systemen hast du Syscalls wie open , read und close - meine Erahrung zeigt, dass mit diesen Funktionen wesentlich leichter und schneller zu Programmieren ist. Ist dann aber nicht mehr Standard-C, heißt, wenn dein OS dir diese Schnittstellen nicht bieten, bist du aufgeschmissen. Wrapper musst du so oder so schreiben, wenn du willst, dass das auf mehreren Systemen läuft. Als Anfänger (von dem ich ausgehe, dass du einer bist) interessiert dich aber eh nur, das erst mal lauffähig zu bekommen.

    Unter POSIX-systemen hast du den Befehl "man", mit dem du dir Informationen zu einem bestimmten Call holen kannst:

    man 2 open schrieb:

    open versucht eine Datei zu öffnen, und gibt als “Returncode” eine Dateibeschreibung der zu öffnen versuchten Datei zurück.

    Unter Windows hast du MSDN, du kannst dir auch eine lokale Offline-Version der Bibliothek für die Listen der Syscalls holen.

    Das zum Ersten, jetzt zum zweiten (Anzahl der Zeilen herausfinden).

    Das ist ein bisschen böse. Ich habe seinerzeit eine Implementierung für sowas geschrieben, den Code könnte ich hier posten - der bringt dir aber nichts, weil ich darunter auch andere Programmteile von mir verwende. memmem zum Beispiel - damit man nach dem Zeilenende suchen kann, ohne über das Ende des Puffers zu lesen, ein immer sehr geschätzter Angriffsvektor. 🙂

    Ach, was soll's, ich mach das jetzt einfach:

    /*Beinhaltet den eigentlichen Code.*/
    int get_line_count_generic
    (
            const char*     input_string,
            const size_t    input_string_length,
            const char*     part_string,
            const size_t    part_string_length,
            size_t*         number_of_lines
    )
    {
            /*Selbst ne leere Zeile ist eine Zeile.*/
            register size_t pnumber_of_lines=1;
            register size_t pinput_string=(size_t)input_string,
                    input_string_end=pinput_string+input_string_length;
    
            /*Parametercheck*/
            if(!pinput_string || !input_string_length
            || !part_string   || !part_string_length
            || !number_of_lines)
            {
                    return EINVAL;
            }
    
            while(pinput_string<input_string_end
            && (pinput_string=(size_t)my_memmem
            (
                    (char*)pinput_string,input_string_end-pinput_string,
                    part_string,part_string_length
    
            )))
            {
                    /*Sicherstellen, dass wir das gleiche Muster nicht zweimal
                    **einlesen.*/
                    pinput_string+=part_string_length;
                    ++pnumber_of_lines;
            }
    
            *number_of_lines=pnumber_of_lines;
            return 0;
    }
    
    /*Wrapper für Mac-Systeme, diese kennen nur "\r" als Zeilenumbruchszeichen.*/
    int get_line_count_mac
    (
            const char*     input_string,
            const size_t    input_string_length,
            size_t*         number_of_lines
    )
    {
            /*Wieder Paramcheck*/
            if(!input_string  || !input_string_length
            || !number_of_lines)
            {
                    return EFAULT;
            }
    
            /*Hier den Aufruf, mit "\r" als Linelimiter.*/
            return get_line_count_generic
            (
                    input_string,input_string_length,
                    "\r",1,
                    number_of_lines
            );
    }
    
    /*Und dann das gleiche noch mal für Linux ("\n") und Windows
    **("\r\n"). Nur diese Symbole werden dann exportiert, die _generic-
    **Funktion bleibt versteckt, die soll man nicht aufrufen.*/
    

    Hat aber wie gesagt ein Problem, du brauchst ein funktionierendes (und am Besten schnelles) memmem . Gibt es nicht. Zumindest nicht unter Windows. Und unter Linux auch nur, wenn du ein krebsiges Präprozessormakro vor dem Inkludieren des Headers machst. strstr geht hier nicht, weil das bei jedem Nullbyte anhält ... OK, gut, in deinem Fall könnte man tatsächlich strstr verwenden. Alles nach dem Nullbyte würde dann halt ignoriert werden.

    Also, nimm meinen Code, packt da strstr statt my_memmem rein, und dann sollte das klappen. Kannst du natürlich nur dann machen, wenn du gelernt hast, dir die Bytes von der Festplatte zu picken. 🙂



  • So ein Müll habe ich lange nicht gesehen.

    Wrapper musst du so oder so schreiben, wenn du willst, dass das auf mehreren Systemen läuft.

    Wrapper musst du gar nicht schreiben, wenn du ausschließlich Funktionen des C-Sprachstandards benutzt.

    Als Anfänger (von dem ich ausgehe, dass du einer bist) interessiert dich aber eh nur, das erst mal lauffähig zu bekommen.

    Ahnungslose Anfänger gibt es immer wieder, die meinen, andere Anfänger mit ihren 'Erfahrungen' beglücken zu müssen.



  • Vielleicht hätte ich deutlicher machen sollen, dass das erste "so" sich auf Systeme bezog, die tatsächlich keine Laufzeitbibliothek besitzen, sondern nur ein rudimentäres API - selten, aber ich habe bereits schon so etwas gesehen - und das zweite "so" sich auf den unheimlichen Murks, den die C-Laufzeitbibliothek darstellt.
    scanf hängt sich in einer Endlossschleife auf? Ach, das erklären wir zum Feature, nicht zum Bug. Du willst die Länge einer Datei herausfinden? fopen , dann fseek , ftell und dann wieder fseek . Das ist nicht deren Ernst, oder?

    Wrapper sollte man dann schreiben, wenn man über diese kaputten Schnittstellen nicht arbeiten will - also fast immer.

    Wutz schrieb:

    So ein Müll habe ich lange nicht gesehen.

    Du meinst, die gleiche Kategorie Müll wie "verkettete Listen sind scheiße"? Aber dazu willst du dann lieber doch nichts sagen, hm? 🙂



  • Wutz schrieb:

    So ein Müll habe ich lange nicht gesehen.

    Das sehe ich ganz genauso.
    Und erst dieser scheiß Code mit zig Verrenkungen.
    Das geht einfacher und eleganter.

    @TS
    bitte lerne von dachschaden nichts.

    re342 schrieb:

    Dazu hatte ich die Idee ein Strukt für Vor- und Nachname zu erstellen, darüber ein Array zu erstellen

    so weit so gut.

    und mit einer for-Schleife nach und nach die Vor- und Nachnamen jeweils in ein weiteres Struct-Element zu packen. Diese füge ich dann sortiert in eine zunächst leere Liste ein.

    Würde ich nicht machen. Wozu die vielen Kopierschritte in neue Structs?

    Stattdessen machst du besser ein Array von Zeigern, dass auf deine Structobjekte zeigt und tauschst dann nur die Zeiger im Array aus.
    Das geht schneller.

    Meine Frage : Wie bekomme ich die Daten aus der Textdatei in die Struct-Elemente hinein?

    Zeilenweise einlesen, parsen und dann entsprechend reinkopieren.
    Guck dir dazu die Standard C Dateifunktionen an.

    Als Rahmen habe ich bisher folgendes :
    [code="c"]
    name Person[100]; // Anzahl Zeilen der Datei
    [/QUOTE]
    Globale Variablen sind pfui.
    Versuch es ohne, es geht immer ohne globale Variablen, man muss nur wollen.

    Und lässt sich die Anzahl der Zeilen der Textdatei auch ermitteln?

    Gruß

    Du könntest die Anzahl der Zeilen natürlich vorher erst einmal durchzählen, eleganter wäre es aber, wenn deine Arraygröße dynamisch angelegt wird und bei Bedarf einfach mehr Speicher reserviert wird.
    In so einem Fall empfiehlt es sich auch eine Liste zu nehmen, die dynamisch wachsen kann.



  • Rambermann schrieb:

    Du könntest die Anzahl der Zeilen natürlich vorher erst einmal durchzählen, eleganter wäre es aber, wenn deine Arraygröße dynamisch angelegt wird und bei Bedarf einfach mehr Speicher reserviert wird.
    In so einem Fall empfiehlt es sich auch eine Liste zu nehmen, die dynamisch wachsen kann.

    Den relevanten Hinweis habe ich vergessen.

    Mit einer dynamischen Liste kannst du gleich mit dem Einlesen der Daten anfangen und wenn deine Liste zu klein ist, machst du sie einfach größer.
    D.h. vorher Zeilen lesen musst du nicht.

    Damit das aber einigermaßen performant läuft, solltest du den Speicher Blockweise anfordern.
    Also gleich mal für mehrere mögliche Zeilen und nicht nur bei jeder einzelne Zeile.



  • Rambermann schrieb:

    Du könntest die Anzahl der Zeilen natürlich vorher erst einmal durchzählen, eleganter wäre es aber, wenn deine Arraygröße dynamisch angelegt wird und bei Bedarf einfach mehr Speicher reserviert wird.
    In so einem Fall empfiehlt es sich auch eine Liste zu nehmen, die dynamisch wachsen kann.

    Rambermann schrieb:

    Das sehe ich ganz genauso.
    Und erst dieser scheiß Code mit zig Verrenkungen.
    Das geht einfacher und eleganter.

    Es ist also intelligenter, einfach ein Array dynamisch wachsen zu lassen? Hast du mal gemessen, wie teuer so ein realloc / malloc ist? Und das soll elegant sein? Ich lach mich tot.

    Ich halte mich da raus. Weniger Konkurrenzdruck für mich. 🙂



  • dachschaden schrieb:

    Es ist also intelligenter, einfach ein Array dynamisch wachsen zu lassen? Hast du mal gemessen, wie teuer so ein realloc / malloc ist? Und das soll elegant sein? Ich lach mich tot.

    Deswegen macht man das Blockweise, wie ich bereits schrieb.

    Tot lachen kannst du dich gerne, dann haben wir zukünftig einen weniger hier, der Stuss redet.



  • Rambermann schrieb:

    dachschaden schrieb:

    Es ist also intelligenter, einfach ein Array dynamisch wachsen zu lassen? Hast du mal gemessen, wie teuer so ein realloc / malloc ist? Und das soll elegant sein? Ich lach mich tot.

    Deswegen macht man das Blockweise, wie ich bereits schrieb.

    Tot lachen kannst du dich gerne, dann haben wir zukünftig einen weniger hier, der Stuss redet.

    Da ist er auch unterwegs und mir schwant Fürchterliches:
    http://www.c-plusplus.net/forum/328628

    🙄



  • @re342
    das Einlesen kannst zB einfach mit fscanf machen
    und zum speichern: http://www.c-plusplus.net/forum/325376



  • Hallo!

    Vielen Dank für die Hinweise. Ich habe es nun hinbekommen :

    * fscanf zum Einlesen der Datei
    * Format der Zeilen vorgegeben, in lokalen Variablen gespeichert und diese an die Listen-Funktion übergeben
    * Auf Anzahl der Zeilen der Datei verzichtet und stattdessen !=EOF benutzt.

    Gruß



  • Toll.
    Du hast es 'hinbekommen'.
    Die Daten bestimmen deinen Code.
    Dein idealisierter Test läuft, d.h. dein Programm 'funktioniert'.
    Deswegen ist dein Programm Praxismüll, d.h. in der Praxis (unter nicht idealisierten Bedingungen) Müll.


Anmelden zum Antworten