string einlesen + zeileninhalt trennen



  • hi leute,

    die sufu des Forums hat mir leider in meinem fall nicht helfen können.

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

    dabei soll aber gleich der zeileninhalt voneinander getrennt werden und in variablen abgelegt

    1.blablabla
    12.blablabla
    7.blablabla
    usw.
    

    zahl + trennzeichen + text

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



  • mit std::string.find und .substring kannst du sowas machen



  • die substring-before funktion sieht ganz brauchbar aus.

    wie tu ich dort aber angeben, dass er aus der variable "zeile", wo die zeilen abgelegt werden, ausliest

    substring-before(line,".")     ??
    

    und wie übergebe ich die zahlen vor dem trennzeichen an weitere variablen

    kennt das programm dann auch die zuordnungen zwischen zahl und text, sodass man nach dem sortieren die richtigen texte bekommt

    thx



  • das thema hatten wir doch erst

    http://www.c-plusplus.net/forum/viewtopic-var-t-is-190098.html

    in dem Beitrag von "Helfer" sollte eigentlich alles drin stehen.



  • gut, danke
    der beitrag war mir nicht bekannt



  • pair<int, string> split(const string& zeile)
    {
        string::size_type position = zeile.find('.', 0);
        make_pair(zeile.substr(0, position), zeile.substr(position+1));
    }
    

    irgendwie nicht gnz richtig, wa?

    mhh und wie soll man dann sortieren, wo sind die werte gespeichert



  • Fast richtig - jetzt mußt du nur noch den ersten Teil umwandeln in einen int-Wert (wie das geht, steht in der FAQ unter "Einmal String nach Zahl und zurück").



  • ... "Einmal Zahl nach String und zurück" wa?

    und was genau meinst du mit 1.teil? 🙂



  • 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.


Anmelden zum Antworten