string einlesen + zeileninhalt trennen



  • End0X schrieb:

    und was genau meinst du mit 1.teil? 🙂

    zeile.substr(0,position) ist immer noch ein String, der die "1" oder "12" in Textform enthält - den mußt du noch umwandeln in einen int-Wert, bevor du vernünftig damit rechnen kannst.



  • achso, ok

    beim verwenden dieses befehls, bekomm ich jedoch nen fehler

    int x = atoi(line.substr(0,position));
    
    1error C2065: 'position': nichtdeklarierter Bezeichner
    

    any ideas? 😃



  • An welcher Stelle hast du denn das in die obige Funktion eingebaut? (btw, atoi() erwartet keinen std::string, sondern einen char* - den mußt du dir erstmal per c_str() besorgen: int x = atoi(line.substr(0,position).c_str()); )



  • End0X schrieb:

    mein programm soll mit while(getline(datei,zeile)) mehrere zeilen einlesen, ok?

    Mmh! - nein nicht wirklich ok. Man sollte immer zunächst versuchen, das einzulesen, was man einlesen will. Und erst wenn das nicht mehr geht, kann man ganze Zeilen oder Dateien lesen, um sie dann mit irgendwelchen Parsern auseinander zu pflücken.

    End0X schrieb:

    zahl + trennzeichen + text

    ich will also die zahlen vor dem trennzeichen extra haben, damit ich die zeilen sortieren kann

    Also letztlich Zahl und Text - dann mache man sich eine Struktur

    struct entry
    {
        int m_nummer;
        std::string m_restText;
    
        // --   Vergleichsmethode für's sortieren
        bool operator<( const entry& b ) const
        {
            return m_nummer < b.m_nummer;
        }
    };
    

    Füge Funktionen zum Einlesen und vielleicht auch gleich welche zum Ausgeben hinzu

    std::istream& trenner( std::istream& in )
    {
        char t;
        if( in >> t && t != '.' )
            in.setstate( std::ios_base::failbit );
        return in;
    }
    // --   Entry lesen
    std::istream& operator>>( std::istream& in, entry& e )
    {
        return std::getline( in >> e.m_nummer >> trenner, e.m_restText );
    }
    // --   Entry schreiben
    std::ostream& operator<<( std::ostream& out, const entry& e )
    {
        return out << e.m_nummer << '.' << e.m_restText << std::endl;
    }
    

    .. und den Rest bekommt man dann schon fast geschenkt - jedenfalls ist es immer der gleiche Code

    int main()
    {
        using namespace std;
        std::ifstream quelle( "input.txt" );
        if( !quelle.is_open() )
        {
            std::cerr << "Fehler beim Oeffnen der Datei" << std::endl;
            return -1;
        }
        std::vector< entry > log( (std::istream_iterator< entry >( quelle )), (std::istream_iterator< entry >()) );
        std::sort( log.begin(), log.end() );
        std::copy( log.begin(), log.end(), std::ostream_iterator< entry >( std::cout ) );
        return 0;
    }
    

    Als Include-Dateien sind dafür notwendig:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <vector>
    #include <algorithm>    // sort
    #include <iterator>     // i/ostream_iterator<>
    

    siehe auch hier.

    Gruß
    Werner



  • Werner Salomon schrieb:

    ...

    std::istream& trenner( std::istream& in )
    {
        char t;
        if( in >> t && t != '.' )
            in.setstate( std::ios_base::failbit );
        return in;
    }
    

    ...

    Hmmm - ich wusste erst nicht, ob ich dem if-statement glauben kann, hab's dann aber begriffen: Wenn !(in >> t) , dann wird zwar das setstate() nicht mehr ausgeführt, aber das braucht's auch nicht, weil in dann sowieso schon den richtigen (="falschen" 😉 ) state hat.

    Schönes Idiom, werde ich mir merken (hofffentlich).

    Gruß,

    Simon2.



  • ich finds ja schon sehr beeindruckend, was Werner Salomon so hinbekommt.
    finds nur (für meine verhältnisse) etwas kompliziert und unverständlich 😃

    aber riesen lob und dank 👍



  • End0X schrieb:

    finds nur (für meine verhältnisse) etwas kompliziert und unverständlich 😃

    .. Ihr würdet in solchen Fällen mir - und damit letztlich auch Euch - helfen, wenn man mir genau sagen würde, was genau kompliziert aussieht und unverständlich ist. Klar ist das kein Anfänger Code, aber leider fällt es mir inzwischen schwer, zwischen Dingen die für Anfänger leicht zu verstehen sind und Dingen, die für mich leicht zu verstehen sind, zu unterscheiden. Deshalb bräuchte ich da etwas Support.

    Also: was genau erscheint kompliziert und unverständlich?

    Gruß
    Werner



  • Simon2 schrieb:

    Hmmm - ich wusste erst nicht, ob ich dem if-statement glauben kann, hab's dann aber begriffen: Wenn !(in >> t) , dann wird zwar das setstate() nicht mehr ausgeführt, aber das braucht's auch nicht, weil in dann sowieso schon den richtigen (="falschen" 😉 ) state hat.

    Also ich les' das so: erst wenn 'in >> t' gut gegangen ist - also true ist - erst dann ist das 't' gültig und darf auf Ungleichheit, oder was auch immer, geprüft werden.

    Gruß
    Werner



  • Werner Salomon

    naja, eigentlich kann man ihn schon verstehen, wenn man nachguckt,welche befehle was auslösen. 😉

    aber will nicht meinen ganzen code umschreiben müssen 😃 🙄

    ich hätte jetzt zbsp strstrok benutzt (alles andere "splitten" ging irgendwie nicht) um den teil vor dem trennzeichen in eine und den danach in eine andere variable zu packen.

    while(getline(infile,line))
    	vect.push_back(line);
    
    	char *token;
    
    	token = strtok(vect, ">");
        while( token != NULL )
        {
    	cout << "%s\n" << token;
    	token = strtok(NULL, ">");
        }
    

    so in der art. kommt aber dies 😃

    error C2664: 'strtok': Konvertierung des Parameters 1 von 'std::vector<_Ty>' in 'char *' nicht möglich
    

    ich merke schon, dass man vieles beachten muss,beim coden. sonst entstehen zu viele konflikte. naja, amateurstyle halt :p



  • aber wie ich das danach sortieren soll,wüsste ich ehrlich gesagt auhc nicht 🤡



  • @End0X:
    Überleg dir mal was du Schritt für Schritt machen willst. Was hat das für nen Sinn in ner while Schleife alles in nen vector zu packen und gleichzeitig zu teilen zu versuchen. Und bleib doch bei den Funktionen des std::strings und nimmt nicht wieder so strtok Zeugs.

    @Werner Salomons Code
    😮 Um sowas einfaches wie nen String bei nem Punkt zu teilen brauch ich doch nicht so n aufwendiges Zeug, dass man erst mal ne ganze Weile anschauen muss, um es zu verstehen. Findet ihr wirklich das sowas ne gute C++ Lösung ist?



  • blaurot schrieb:

    ...Findet ihr wirklich das sowas ne gute C++ Lösung ist?

    Japp - weil es einer allgemeingültigen und -bekannten Struktur folgt, anstatt nur eine "Punktsuchfunktion" zu sein:
    Den Datentyp entry kann man nun genauso über jeden Stream (cin/cout, i/ofstream, i/ostringstream oder jeden selbstgeschriebenen) einlesen und ausgeben wie wir das von bisherigen Typen (int, double, string, ...) kennen.

    Die Darstellung ist "dicht am Typen" und "dicht an der Ein/Ausgabe" implementiert: In "seinen" Streamoperatoren....
    OK, trenner() hätte man auch direkt in operator>>() implementieren können, aber das wär's auch schon - und so ist er "allgemeingültig" (sprich: Kann auch für andere Datentypen genutzt werden).

    Ich finde es jedenfalls ein schönes Idiom ... in schönem C++. 😃

    Gruß,

    Simon2.



  • Simon2 schrieb:

    blaurot schrieb:

    ...Findet ihr wirklich das sowas ne gute C++ Lösung ist?

    Japp - weil es einer allgemeingültigen und -bekannten Struktur folgt, anstatt nur eine "Punktsuchfunktion" zu sein:
    Den Datentyp entry kann man nun genauso über jeden Stream (cin/cout, i/ofstream, i/ostringstream oder jeden selbstgeschriebenen) einlesen und ausgeben wie wir das von bisherigen Typen (int, double, string, ...) kennen.

    Die Darstellung ist "dicht am Typen" und "dicht an der Ein/Ausgabe" implementiert: In "seinen" Streamoperatoren....
    OK, trenner() hätte man auch direkt in operator>>() implementieren können, aber das wär's auch schon - und so ist er "allgemeingültig" (sprich: Kann auch für andere Datentypen genutzt werden).

    Ich finde es jedenfalls ein schönes Idiom ... in schönem C++. 😃

    Also wenn du ne Funktion willst, die allgemain verwendbar ist, dann sollte aber das Trennzeichen nicht hart reincodiert sein oder mindestens ne Konstande sein. Und ob das so gut ist, dass sie streams verwendet, weiß ich auch nicht. Wenn du eine UI hast, die dir nur nen string gibt, dann musst du daraus erst wieder nen stream machen. Wieso nicht ne einfache split-Funktion die ein pair zurück gibt und nen string und das trennzeichen übernimmt? Sowas ist doch viel einfacher zu lesen als die nichtssagenden operatoren.



  • blaurot schrieb:

    Also wenn du ne Funktion willst, die allgemain verwendbar ist, dann sollte aber das Trennzeichen nicht hart reincodiert sein oder mindestens ne Konstande sein.

    Ja, das wäre noch eine Steigerung der Wiederverwendbarkeit - die Funktion 'trenner()' aufbohren zu einem Stream-Manipulator.

    Und ob das so gut ist, dass sie streams verwendet, weiß ich auch nicht. Wenn du eine UI hast, die dir nur nen string gibt, dann musst du daraus erst wieder nen stream machen. Wieso nicht ne einfache split-Funktion die ein pair zurück gibt und nen string und das trennzeichen übernimmt? Sowas ist doch viel einfacher zu lesen als die nichtssagenden operatoren.

    Es ist gut, weil es dem Operator egal ist, woher seine Daten kommen (und das Einlesen und Verteilen in einem Arbeitsgang erledigt werden kann). Davon abgesehen kannst du mit diesen Operatoren auch lexical_cast<> anwenden, um deine "einfache split-Funktion" zu bekommen 😉



  • Schau dir einfach mal die main funktion an. Wer kann da den sagen was hier gemacht wird. Ne einfache Schleife mit ner split Funktion wäre ein einfacher und lesbarer Code.

    int main()
    {
        using namespace std;
        std::ifstream quelle( "input.txt" );
        if( !quelle.is_open() )
        {
            std::cerr << "Fehler beim Oeffnen der Datei" << std::endl;
            return -1;
        }
        std::vector< entry > log( (std::istream_iterator< entry >( quelle )), (std::istream_iterator< entry >()) );
        std::sort( log.begin(), log.end() );
        std::copy( log.begin(), log.end(), std::ostream_iterator< entry >( std::cout ) );
        return 0;
    }
    


  • Ich kann's dir sagen - du legst einen vector als Kopie eines Iterator-Bereiches an (die Quelle besteht aus Stream-Iteratoren, also werden die Elemente per op>> aus der Datei gezogen), sotierst ihn und kopierst ihn anschließend in einen anderen Iterator-Bereich (wieder ein Stream-Iterator, der die Daten mit op<< nach cout schreibt). Das klappt für int, double und jeden anderen Typ, der op>> und op<< überladen hat - im Gegensatz zu deinem Ansatz, den du je nach Anforderungen umbauen müsstest.



  • blaurot schrieb:

    ...Wenn du eine UI hast, die dir nur nen string gibt, dann musst du daraus erst wieder nen stream machen....

    und wenn Du ein UI hast, das Dir die Daten in einem File gibt oder über Konsole oder über Netz oder ... müsstest Du mit Deinem Ansatz erst einen String draus machen. Du bist hier doch derjenige, der einen Umweg verlangt (fstream->string->vector, statt fstream -> vector). 😮
    Und spätestens, wenn da jemand einen kontinuierlichen Stream (z.B. weil die Daten byteweise anfallen) oder 7 Terabyte übergeben möchte, kommst Du mit dem string-Ansatz an Probleme...

    Was hier getan werden soll ist, Daten sequentiell durchzugehen und daraus ein Objekt zu erstellen - genau das, wofür ein Stream eben gemacht ist ... und nicht String.
    BTW: Aus string einen stringstream zu machen, ist nun wirklich nicht der Akt. Nicht mehr Arbeit als eine Datei zu öffnen ... (= "Aus einem String einen fstream machen").

    blaurot schrieb:

    Schau dir einfach mal die main funktion an. Wer kann da den sagen was hier gemacht wird. ...

    Mal abgesehen davon, dass ie Ausgabe des Vektors ein wenig "eye-candy" ist: Dann schreib mal Deinen Code daneben, der dieselbe Funktionalität (aus einem File in einen Vektor einlesen inkl. Fehlerbehandlung) bietet...
    Ich wette, dass das weder kürzer noch übersichtlicher ist, sondern unter die Kategorie fällt: Vorher: "Na, das habe ich schon 1000mal so gemacht; das wird schon nicht so schwer sein"; Nachher: "Hmmm, war dan im Detail doch ganz schön lang und umständlich." 😃

    Gruß,

    Simon2.



  • kann mich bitte mal jemand, der sich mit string splitting auskennt und etwas zeit hat im icq anschreiben? 278-126



  • Simon2 schrieb:

    Ich wette, dass das weder kürzer noch übersichtlicher ist, sondern unter die Kategorie fällt: Vorher: "Na, das habe ich schon 1000mal so gemacht; das wird schon nicht so schwer sein"; Nachher: "Hmmm, war dan im Detail doch ganz schön lang und umständlich." 😃

    Ich denke das ist auch derselbe Bereich, aus dem die Frage nach der Lesbarkeit kommt: "Ich habe das schon 1000 mal so gemacht, ich weiss was da passiert" vs. "Die Denkweise ist ungewohnt, das sieht furchtbar kompliziert aus."

    Ich kann bei obigem Abschnitt nämlich mit gutem Gewissen sagen:

    blaurot schrieb:

    Wer kann da den sagen was hier gemacht wird. Ne einfache Schleife mit ner split Funktion wäre ein einfacher und lesbarer Code.

    Ich kann sagen was hier gemacht wird, da ich es schon 1000 mal so gemacht habe (:D). Ne einfache Schleife mit ner Split-Funktion fände ich weniger lesbar.



  • CStoll schrieb:

    Ich kann's dir sagen - du legst einen vector als Kopie eines Iterator-Bereiches an (die Quelle besteht aus Stream-Iteratoren, also werden die Elemente per op>> aus der Datei gezogen), sotierst ihn und kopierst ihn anschließend in einen anderen Iterator-Bereich (wieder ein Stream-Iterator, der die Daten mit op<< nach cout schreibt). Das klappt für int, double und jeden anderen Typ, der op>> und op<< überladen hat - im Gegensatz zu deinem Ansatz, den du je nach Anforderungen umbauen müsstest.

    Hm, und wo sehe ich jetzt, das an einem bestimmten Zeichen getrennt wird? Es ist einfach unklar, man kann nix sehen. Es gibt keinen sprechenden Funktionsnamen. Bei nem einfachen split(line, separator) kann man doch auf den ersten Blick sehen was gemacht wird.

    template<typename A, typename T> const A lexicalCast(const T& source) {
        std::stringstream s;
        s << source;
    
        A destination;
        s >> destination;
    
        return (destination);
    } 
    
    template<typename F> std::pair<F, std::string> split(std::string& line, char sep) {
    	size_t pos = line.find(sep);
    	if(pos != std::string::npos) {
    		std::string num = line.substr(0, pos);
    		return std::make_pair(lexicalCast<F>(num),line.substr(pos + 1));
    	} else {
    		throw std::exception("invalid format");
    	}
    }
    
    int main() {
    	try {
    		std::vector<std::pair<double, std::string> > numLines;
    
    		std::ifstream file( "input.txt" );
    		if( !file.is_open() ) {
    			std::cerr << "Fehler beim Oeffnen der Datei" << std::endl;
    			return -1;
    		}
    
    		std::string line;
    		while(getline(file,line)){
    			numLines.push_back(split<double>(line,'@'));
    		}
    
    		std::sort(numLines.begin(), numLines.end() );
    		for(size_t i = 0; i<numLines.size(); i++) {
    			std::cout<< numLines[i].first << "\t" << numLines[i].second << std::endl;
    		}
    	} catch(std::exception e) {
    		std::cerr << e.what();
    	}
    }
    

    Sogar als template, damit ihr den Typ ändern könnt. Obwohl ich sowas so gut wie nie mache war es ganz einfach, damit es auch mit double geht. Das mit dem lexicalCast gefällt mir zwar nicht, aber ich kenn jetzt auch keine vernünftige Funktion in C++ die ne Exception wirft, wenn der Text garkeine Zahl ist. Aber ist schon total unleserlich so ne while schleife mit Funktionsaufruf.


Anmelden zum Antworten