Txt-Datei einlesen mit "Begrenzer" ->in seperate Variablen, mit STL



  • ich hab eine Txt-Datei:
    bsp.

    John, Marschall; 110:10
    Reginald, Bull; 112:11
    Atlan, da Gonozal; 55:15
    Perry, Rhodan; 123:11
    Tako, Kakuta; 111:44
    

    jetzt muss ich die Datei auseinanderfriemeln und in eine Liste speichern..
    nur wie bekomm ich die einzelnen Textsequenzen auseinander

    hab bereits eine Class geschrieben:

    class Personen {
    public:
    	Personen::Personen( const string &first, const string &last, const string &pkt, const string &gpkt )
    	{
    	 setVorname(first);
    	 setNachname(last);
    	 setPunkte(pkt);
    	 setGegenPunkte(gpkt);
    	}
    	void setVorname( const string & first) { Vorname = first; }
    	string getVorname() const { return Vorname; }
    	void setNachname( const string & last) { Nachname = last; }
    	string getNachname() const { return Nachname; }
    	void setPunkte( const string & pkt) { punkte = pkt; }
    	string getPunkte() const { return punkte; }
    	void setGegenPunkte( const string & gpkt) { gegenpunkte = gpkt; }
    	string getGegenPunkte() const { return gegenpunkte; }
    private:
    	string Vorname;
    	string Nachname;
    	string punkte;       //sollte int sein
    	string gegenpunkte;  //sollte auch int sein
    };
    

    und habe da eine möglichkeit die ich sehr unbefriedigend finde, da nur strings
    auszug aus main...

    [cpp]int main()
    {
    	std::list<Person*> myList;  //weiss noch net ganz genau wie das 
    	ifstream infile("Daten.txt", ios::in);
    	string vorn, nam;
    	string pkt, gegenpkt;
    
    	if(!infile)
    	{
    		cerr<<"File could not be opened"<<endl;
    		exit(1);
    	}
    
    	while(infile)
    	{
    		getline(infile, nam, ',');
    		getline(infile, vorn, ';');
    		getline(infile, pkt, ':');   
    		getline(infile, gegenpkt, '\n');
    		// hier irgendwie in die Class abspeichern
    
    		..
    			..//und irgendwie in stl- liste gefriemelt --
    			mylist.push_front(Person); 
    	}
    
    	return 0;
    }[/cpp]
    

    hatte mir auch schon sowas gedacht
    while(infile>>name>>vorname>>punkte>>gegenpunkte)
    nur das problem sind die begrenzer (, ; : und \n), compiler weiss ja net..was zu name und was zu vorname gehört.. usw brauch da wohl nee extra
    "Auseinanderfriemel-funktion"
    hoffe mein beispiel iss ausreichend beschrieben.

    hat jemand nee ahnung wie man das macht? mit stl und dem einlesen?



  • Normalerweise überlädt man die Operatoren für's Streaming. Ziemlich hilfreich bei der einfachen Verarbeitung von Strings sind die Boost String Algorithmen. Aber du wolltest ja nur STL, also hier ein Beispiel, dass nur STL nutzt:

    #include <iostream>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    class Person
    {
    public:
        Person::Person(const string &first = "", const string &last = "", int pkt = 0, int gpkt = 0)
        {
            setVorname(first);
            setNachname(last);
            setPunkte(pkt);
            setGegenPunkte(gpkt);
        }
        void setVorname(const string & first)
        {
            Vorname = first;
        }
        string getVorname() const
        {
            return Vorname;
        }
        void setNachname(const string & last)
        {
            Nachname = last;
        }
        string getNachname() const
        {
            return Nachname;
        }
        void setPunkte(int pkt)
        {
            punkte = pkt;
        }
        int getPunkte() const
        {
            return punkte;
        }
        void setGegenPunkte(int gpkt)
        {
            gegenpunkte = gpkt;
        }
        int getGegenPunkte() const
        {
            return gegenpunkte;
        }
    private:
        string Vorname;
        string Nachname;
        int punkte;       //ist jetzt int
        int gegenpunkte;  //ist jetzt auch int
    
        //Dem Eingabestrom-Operator erlauben wir mal den Zugriff auf private Member,
        //dann kann direkt in ein Objekt gestreamt werden.
        friend istream &operator>>(istream &input, Person &p);
    };
    
    istream &operator>>(istream &input, Person &p)
    {
        char zeichen;
    
        p.Vorname = "";
        p.Nachname = "";
    
        while(input.peek() != ',')
        {
            input.read(&zeichen, 1);
            p.Vorname += zeichen;
        }
        //Das Komma konsumieren
        input.read(&zeichen, 1);
    
        //Leerzeichen brauchen wir nicht!
        while(input.peek() == ' ')
            input.read(&zeichen, 1);
    
        while(input.peek() != ';')
        {
            input.read(&zeichen, 1);
            p.Nachname += zeichen;
        }
        //Das Semikolon konsumieren
        input.read(&zeichen, 1);
    
        //Leerzeichen brauchen wir nicht!
        while(input.peek() == ' ')
            input.read(&zeichen, 1);
    
        input >> p.punkte;
        //Den Doppelpunkt konsumieren
        input.read(&zeichen, 1);
        input >> p.gegenpunkte;
    
        //Das "Neue Zeile"-Zeichen konsumieren
        input.read(&zeichen, 1);
    
        return input;
    }
    
    ostream &operator<<(ostream &output, const Person &p)
    {
        output << "Vorname:\t" << p.getVorname() << "\n";
        output << "Nachname:\t" << p.getNachname() << "\n";
        output << "Punkte:\t\t" << p.getPunkte() << "\n";
        return output << "GegenPunkte:\t" << p.getGegenPunkte() << "\n";
    }
    
    int main()
    {
        Person p;
        ifstream test("test.txt");
        while(!test.eof())
        {
            test >> p;
            cout << p;
        }
    }
    

    Zeichenweise einlesen ist allerdings nicht sehr performant; besser wäre es die ganze Zeile zu lesen und diese mit boost::split zu zerschneiden. Die Punkte-Strings kann man dann zum Schluss mit boost::lexical_cast nach int konvertieren.

    EDIT:
    Beim operator<< fehlte das return.



  • Oh mein Gott, tonnenweise C++ Ramsch, wo eine Zeile fscanf genügt 😃
    🙂



  • so krass wie nillable hätt ichs auch nicht gemacht 🙂

    Ich hätt es in etwa so gemacht:

    - Ganze zeile einlesen (bis zum \n)
    - Mit boost::tokenizer oder boost::split in teile splitten, die zeichen zum spliten wären dann eben ", ;:"
    - jedes einzelne feld enstspricht dann einem wert.
    - punkte udn gpkte mit atoi oder boost::lexcial_cast oder std::stringstream umwandeln.

    Aber da ohne boost... Schreib dir eine eigene splitfunkiont, die deine zeile bei ", " und und "; " und ":" splittet und als vector<string> zurück gibt.



  • proggingmania schrieb:

    Oh mein Gott, tonnenweise C++ Ramsch, wo eine Zeile fscanf genügt 😃
    🙂

    Jo! Wo sind nur die guten alten Zeiten hin? *träum*

    @Maxi:
    Kommst du jemals ohne Boost zurecht?



  • fscanfler schrieb:

    proggingmania schrieb:

    Oh mein Gott, tonnenweise C++ Ramsch, wo eine Zeile fscanf genügt 😃
    🙂

    Jo! Wo sind nur die guten alten Zeiten hin? *träum*

    Kannste ja immer noch benutzen; aber schön aufpassen, dass du Riesenbuffer nimmst, damit die Programmierung des Buffer-Overflows ein Easter Egg bleibt, gell 😉

    fscanfler schrieb:

    @Maxi:
    Kommst du jemals ohne Boost zurecht?

    Boost ist wunderbar! Mit boost::xpressive, könnte man den Eingabestrom-Operator zum Beispiel so lösen:

    istream &operator>>(istream &input, Person &p)
    {
        mark_tag vorname(1), nachname(2), punkte(3), gpunkte(4);
        string zeile;    
        sregex rxPerson = (vorname= -+(_w|_s)) >> ',' >> *_s >>
                          (nachname= -+(_w|_s)) >> ';' >> *_s >>
                          (punkte= +_d) >> *_s >> ':' >> *_s >>
                          (gpunkte= +_d);
        smatch personenDaten;
    
        getline(input, zeile);
        if(regex_search(zeile, personenDaten, rxPerson))
        {
            p.setVorname(personenDaten[vorname]);
            p.setNachname(personenDaten[nachname]);
            p.setPunkte(lexical_cast<int>(personenDaten[punkte]));
            p.setGegenPunkte(lexical_cast<int>(personenDaten[gpunkte]));
        }
    
        return input;
    }
    

    Klar, ist mit Kanonen auf Spatzen geschossen; hier würde auch split reichen, aber es soll nur ein kleines Beispiel sein, wie man sich mit Boost ausdrücken kann.



  • wegen fscanf... es ist ja auch ein c++-forum 😉

    @fscanfler: öhm... naja, früher schon. Jetzt hab ich boost echt lieb gewonnen. Sehe auch keinen Grund, warum man auf boost verzeichten sollte. Gibts welche? Ich benutz auch lieber boost::format als std::stringstream bzw. sprintf oder boost::assign und so. Macht alles leichter.



  • Maxi schrieb:

    wegen fscanf... es ist ja auch ein c++-forum 😉

    @fscanfler: öhm... naja, früher schon. Jetzt hab ich boost echt lieb gewonnen. Sehe auch keinen Grund, warum man auf boost verzeichten sollte. Gibts welche? Ich benutz auch lieber boost::format als std::stringstream bzw. sprintf oder boost::assign und so. Macht alles leichter.

    naja wenn das ziel bzw. vorgabe ist, möglichst kleine programme zu schreiben, kann man boost knicken, da zuviel unnützer kram mit reingeworfen wird. mehr als nur bei der STL selbst.



  • ein schlauer linker/compiler gibt nur das dazu, was gebraucht wird. Kann mir nciht vorstellen, dass boost::tokenizer oder so jetzt grad so fett sind.



  • Maxi schrieb:

    ein schlauer linker/compiler gibt nur das dazu, was gebraucht wird. Kann mir nciht vorstellen, dass boost::tokenizer oder so jetzt grad so fett sind.

    erzähl das mal dem aktuellen GCC.



  • ich sagte ein schlauer compiler/linker 😉
    Ne, kA warum der gcc so blöd ist und nicht checkt, dass er nicht alles linekn muss, was man ihm angibt. Und warum er nicht checkt, dass funktionen die die eine lib braucht vielleicht in einer in der kommandozeile folgenden lib drin sind. Naja, kA... komisch eben.


Anmelden zum Antworten