getline() mit string als Delimiter



  • Ich versuche ein getline zu machen, das einen String statt einem Char als Delimiter entgegennimmt. Mein einziges Problem hierbei ist, dass istream fail() ist, wenn der letzte Part des Strings geparst wird.

    #include <istream>
    #include <string>
    
    std::istream& getline(std::istream& stream, std::string& line, const std::string& delimiter){
        line.clear();
    
        bool delim_found{false};
        char c;
    
        while(stream.get(c)){
            line += c;
    
            if(line.size() >= delimiter.size() && line.substr(line.size() - delimiter.size()) == delimiter){
                delim_found = true;
                break;
            }
        }
    
        if(delim_found)
            line.erase(line.end() - delimiter.size(), line.end());
    
        if(line.size())
            stream.setstate(std::ios::goodbit);
    
        return stream;
    }
    
    #include <sstream>
    
    int main(){
        std::stringstream stream{"abc;+foo;+bar;+baz"};
        std::string line;
    
        while(getline(stream, line, ";+"))
            std::cout << line << '\n';
    }
    

    Ich habe mit Zeile 22 bis 23 versucht, den Stream wieder "gut" zu machen, aber das klappt so nicht.

    Weiß jemand, wie ich das richtig mach?
    Wenn der letzte Part geparst wurde, soll der bool operator von istream immer noch true zurückgeben, wie beim normalen std::getline auch.

    :schland: :schland: :schland:



  • ich schlag dir 2 wege vor:

    (1)
    du verwendest std::getline
    wie in deinem beispiel:
    lass getline nach dem ersten vorkommen von ';' suchen und in einen string
    reinschreiben. wenn er ein ';' gefunden hat, checkst du ueber den
    istreambuf ob es sich tatsaechlich um den delimiter-string handelt.
    wenn ja, dann skippe den delimiter-string im istreambuf und beginne von
    vorn bis istream-ende ist.
    wenn nein dann lass std::getline die naechste pseudo-line suchen und schieb
    die line in den string zur vorhergefundenen line.
    das machst du solange bis du den delimiter-string oder das ende des streams
    gefunden hast.

    (2)
    du machst alles manuell ueber den istreambuf. kannst z.b. auch istreambuf-
    iteratoren verweden. dann geht funktioniert es als wenn du mit char-pointern
    oder std::string::iteratoren arbeiten wuerdest. dann koenntest du auch
    passende algos au der STL mitverwenden.

    Meep Meep



  • Meep Meep schrieb:

    ich schlag dir 2 wege vor:
    (2)
    du machst alles manuell ueber den istreambuf. kannst z.b. auch istreambuf-
    iteratoren verweden. dann geht funktioniert es als wenn du mit char-pointern
    oder std::string::iteratoren arbeiten wuerdest. dann koenntest du auch
    passende algos au der STL mitverwenden.

    Meep Meep

    Ich habe mal den Weg mit Iteratoren versucht, aber ganz so klappt das immer noch nicht. Ich vermute, wenn man beim istreambuf_iterator ++it macht, wird auch intern der Stream um eins inkrementiert. Dekrementieren ist nicht möglich! Selbst wenn ich ne Kopie des Iterators mache und meinem Iterator am Ende der Operation die Kopie erneut zuweise, klappt das so nicht. Hier mein neuer Lösungs-Essai:

    #include <iostream>
    
    std::string next_string(std::istreambuf_iterator<char> it, int n){
        std::istreambuf_iterator<char> end;
        std::string delimiter;
    
        while(it != end && n-- > 0)
            delimiter += *it++;
    
        return delimiter;
    }
    
    std::istream& getline(std::istream& stream, std::string& line, const std::string& delimiter){
        line.clear();
    
        char c;
    
        while(next_string(stream, delimiter.size()) != delimiter && stream.get(c))
            line += c;
    
        return stream;
    }
    
    #include <sstream>
    
    int main(){
        std::stringstream stream{"abc;+foo;+bar;+baz"};
        std::string line;
    
        while(getline(stream, line, ";+"))
            std::cout << line << '\n';
    }
    

    istream::seekg() wär ne Option, nur zu dumm, dass seekg() nichts mehr tut, wenn stream.fail() == true ist. Ist es irgendwie möglich einen Stream zu kopieren?



  • eigendlich brauchst du doch nicht mehr mit dem iterator zurueckgehen. beim ueberpruefen schreibst du die zeichen gleich in z.b. einen string. wenn es dann doch nicht der delimiter-string war dann kannst du die gelesenen zeichen an deinen string direkt anhaengen, und weiter nach nem delimiter-string suchen



  • Dann habe ich aber immer noch das gleiche Problem, dass beim Parsen des letzten Parts der Stream auf fail() gesetzt wird.



  • Ich hab das Problem mit istream::clear() gelöst.
    :schland: :schland: :schland:


Log in to reply