Probleme bei Stringbearbeitung



  • Ja, aber dazu muss man mal kurz denken, und die Zeit hab ich grad nicht. 😃



  • Sorry Leute! Ich habe Blödsinn geredet. Aus

    23:24:28:10:29:18:12:24:11:27:10:22:10:20:28

    soll

    28:20:10:22:...

    werden. Daher kann nicht einfach ein reverse machen. Deswegen ist mein Code etwas komplizierter. Sieht jemand das Problem?

    Danke


  • Mod

    Wieso musst du dann von hinten lesen? Pack das Ding in einen Stringstream, parse alle Zahlen (Doppelpunkte ignorieren) und kehr die Reihenfolge der Zahlen um. 1000x einfacher als alles was hier im Thread vorkam.



  • Ich glaube, ich verstehe dich nicht richtig. Ich mach mal vor, wie ich dich verstehe.

    1.) 23:24:28:10:29:18:12:24:11:27:10:22:10:20:28

    2.) 232428102918122411271022102028 //Doppelpunkte raus

    3.) 82029122... //umdrehen.

    Jetzt ist erstens verloren gegangen, wo die Doppelpunkte standen (die Zahlenblöcke können beliebig groß sein) und außerdem drehe ich so auch die Blöcke um.

    Ziel ist es die Blockreihenfolge umzudrehen, nicht die Blöcke selbst.

    Danke 😃


  • Mod

    Nein, du verstehst mich nicht richtig. Ich habe doch absichtlich **ignor(e)**ieren gesagt, nicht rausmachen.

    Hier ist übrigens eine ganz nette Lösung für dein ursprüngliches Problem, beziehungsweise eine Lösung im Stil von Skym0sh0 aber mit

    Skym0sh0 schrieb:

    kurz denken

    Ist auf den ersten Blick unnötig umständlich, aber so ein Iterator-basierter Streambuf kam mir sehr nützlich vor und ich konnte im Netz keinen fertigen finden. Daher veröffentliche ich ihn hier mal. ist natürlich noch leicht ausbaufähig, zum Beispiel könnte man den Iteratortyp erkennen und gegebenenfalls noch die ganzen Funktionen zum Zurücksetzen oder Positionieren anbieten, je nach dem, was der Iterator so kann. Für dieses einfachste Beispiel reicht ein Inputiterator:

    #include <streambuf>
    #include <ios>    
    
    template <typename Iterator> class iterator_streambuf : public std::streambuf
    {
    public:
      iterator_streambuf(Iterator begin, Iterator end): current(begin), end(end) { }
    protected:
      virtual int_type underflow()
      {
        if (current != end)
          return *current;
        else 
          return traits_type::eof();
      }
      virtual int_type uflow()  
      {
        if (current != end)
          return *current++;
        else 
          return traits_type::eof();
      }
    private:
      iterator_streambuf( const iterator_streambuf& );    
      iterator_streambuf& operator=( const iterator_streambuf& );
    
      Iterator current;
      Iterator end;
    };
    
    #include <string>    
    #include <iostream>  
    #include <forward_list> 
    
    int main()
    {
      {
        std::string foo = "!nebeirhcseg streawkceur remmi nedrew netfahcstoB ehcsinataS";
    
        iterator_streambuf<decltype(foo.rbegin())> buf(foo.rbegin(), foo.rend());
        std::istream in(&buf);
    
        for(std::string str; in >> str; std::cout << str << '\n');  
      }
      // edit: Oder etwas exotischer:
      {
        std::forward_list<int> foo = {52, 32, 56, 32, 49, 53, 32, 49, 54, 32, 50, 51, 32, 52, 50};
        iterator_streambuf<decltype(foo.begin())> buf(foo.begin(), foo.end());
        std::istream in(&buf);
    
        for(int i; in >> i; std::cout << i << ' ');  
        std::cout << '\n';
      }
    }
    


  • SeppJ schrieb:

    Ist auf den ersten Blick unnötig umständlich, aber so ein Iterator-basierter Streambuf kam mir sehr nützlich vor und ich konnte im Netz keinen fertigen finden.

    Wenn du schon im Himmel der Iteratoren bist, wieso möchtest du dann wieder zurück in die Höllen der Streams?

    Rate mal, wie GCCs stdlibc++ den Input parst: Mit Stream-Iteratoren.

    Das heisst in diesem Falle:

    std::string s = "23:24:28:10:29:18:12:24:11:27:10:22:10:20:28:";
    for (auto curr=s.rbegin(),last=curr;(last=curr++,curr=std::find(curr,s.rend(),':'))!=s.rend();)
      std::copy(curr.base(), last.base(), std::ostreambuf_iterator<char>(std::cout));
    std::cout << '\n';
    

    Und du? Dein Ansatz kann genau Wörter bis zu nächsten Whitespace einlesen. Punkt. Nicht mehr. Was wenn an ":" getrennt werden soll (wie die Aufgabe *wirklich* lautete)? Dann halt von operator>> zu getline wechseln. Was wenn an ":" oder ";" getrennt werden soll? Ups. Das artet zu Locale-Rumgepfriemle aus. Will niemand.



  • Äh, natürlich

    for (auto curr=s.rbegin(),last=curr;last=curr++,curr=std::find(curr,s.rend(),':'),last!=s.rend();)
    

  • Mod

    eSsThehelL schrieb:

    SeppJ schrieb:

    Ist auf den ersten Blick unnötig umständlich, aber so ein Iterator-basierter Streambuf kam mir sehr nützlich vor und ich konnte im Netz keinen fertigen finden.

    Wenn du schon im Himmel der Iteratoren bist, wieso möchtest du dann wieder zurück in die Höllen der Streams?

    Rate mal, wie GCCs stdlibc++ den Input parst: Mit Stream-Iteratoren.

    Ich glaube, du hast nicht kapiert, was mein Code macht. Iterator->Stream, nicht Stream->Iterator.



  • SeppJ schrieb:

    eSsThehelL schrieb:

    SeppJ schrieb:

    Ist auf den ersten Blick unnötig umständlich, aber so ein Iterator-basierter Streambuf kam mir sehr nützlich vor und ich konnte im Netz keinen fertigen finden.

    Wenn du schon im Himmel der Iteratoren bist, wieso möchtest du dann wieder zurück in die Höllen der Streams?

    Rate mal, wie GCCs stdlibc++ den Input parst: Mit Stream-Iteratoren.

    Ich glaube, du hast nicht kapiert, was mein Code macht. Iterator->Stream, nicht Stream->Iterator.

    Ich glaube, du hast nicht kapiert:
    Iterator: 👍
    Stream: 👎

    Folglich
    Stream -> Iterator 👍
    Iterator -> Stream 👎


  • Mod

    Sicher. 🙄

    edit: Mach doch mal das zweite Beispiel aus meinem Code mit deiner Methode nach.



  • Der Beweis meiner Behauptung sind die Umstände, die nötig sind, deinen Ansatz auf das richtige Problem anzupassen (an ":" splitten und den erhaltenen String zu reversen).

    Und, dass dein Ansatz am verallgemeinerten Problem scheitert: "12:34;56" => "56;34:12" (also an ":" und ";" splitten und den Separator beibehalten).

    Ich versuch mich solange an deinem exotischen Problem.


  • Mod

    Häh? Wovon redest du überhaupt? Dem Iterator->Stream oder der schnellen Lösung für das vereinfachte Problem?

    Egal, beide haben überhaupt kein Problem mit irgendwelchen Separatoren. Ich bin immer mehr überzeugt, dass du gar nicht kapierst, was der Code macht.



  • Dein "exotisches" Problem:

    std::forward_list<char> s = {53, 32, 56, 32, 49, 53, 32, 49, 54, 32, 50, 51, 32, 52, 50}; 
    bool last_was_digit = true;
    for (char c : s)
      if (!std::isdigit(c))
        last_was_digit = false;
      else
        last_was_digit = std::cout << (last_was_digit?"":" ") << c;
    

    Löse du mal das Problem von freakC++ mit deiner Streamlösung: "12:34:56" soll werden zu "56:34:12". Und das möglichst ohne (a) Integer-Overflows (b) alles zu kopieren bei der Eingabe "allesohnedoppelpunkte".


  • Mod

    ??? Aufmerksam lesen: Meine Streamlösung ist für den Fall, dass 12:34:56 zu 65, 43, 21 werden soll. So wie vom TE ursprünglich verlangt und als Demonstration für Skym0sh0, wie es ohne drei Umwege und trotzdem universell gehen könnte. Damit ist das eine ganz wunderbare Lösung, die kaum einen Prozessortakt zu viel verschwendet, dabei aber universell auf viele Probleme angewandt werden kann.

    Zu deiner Lösung meines exotischen Problems: Ähh, du hast es nicht gelöst. Es geht nicht darum, die Zeichen auszugeben. Ich will die Zahlen haben! Wir wollen damit rechnen.

    Wenn du nun beginnst, num_get nachzuprogrammieren: Holzweg. Dann fang ich an, Fließkommazahlen hexadezimal in Scientificschreibweise mit Vorzeichen in die Liste zu packen und es soll immer noch funktionieren. Bitte eine universelle Lösung, die meiner ebenbürtig ist.



  • Ziel ist es die Blockreihenfolge umzudrehen, nicht die Blöcke selbst.

    Und wenn du nun 'Block' durch 'Woerter' ersetzt, erkennst du, dass das Internet viele bekannte Loesungen in vielen bekannten und unbekannten Sprachen bereithaelt. Denken muss man wirklich nicht. Oder der MAtrixstil: Nicht denken, wissen!

    Ach, ich bin da eher faul: http://www.geeksforgeeks.org/reverse-words-in-a-given-string/

    Einfach ' ' durch ':' ersetzen, http://ideone.com/Xc1Sz5 . Mann muss zwei Mal drueberiterieren, ist aber inplace. Trivial parallelisierbar. Um Sonderfaelle zu beachten, brauche ich eine bessere Problembeschreibung.



  • Genau das wollte ich machen. Mein Algorithmus ist dem doch dann gar nicht so fern....

    Danke!

    LG, freakC++



  • verallgemeinerten Problem

    Soll ich mal anfangen zu verallgemeinern?
    1.) Variabler Separator
    2.) Separator variabler Laenge
    3.) Separator escapen um als Teilwort betrachtet zu werden.
    4.) Escapezeichen escapen
    5.) Keine Strings sondern Streams mit verschiedenen Fehlerzustaenden
    6.) Keine Strings sonder Bitstreams deren Wortgroesse kein Vielfaches von 8 Bit sind
    7.) Muahahahaha 🙂
    8.) Werner darf nicht mitmachen
    9.) Mehr Muahahahaha ... 🙂



  • Dank knivils Link habe ich meine Methode nun wie folgt umgebaut. So funktioniert der Minialgorithmus und ich meiner Ansicht auch recht performant.

    Hat jemand was dagegen? 😃

    void myclass::format_output(std::string& raw_result) {
    
      std::string::iterator num_begin = raw_result.begin();
    
      for(std::string::iterator it = raw_result.begin(); it != raw_result.end(); ++it) {   
        if(*it == delimiter){
          	std::reverse(num_begin, it);
    	num_begin = it+1;
        }
      }
      std::reverse(raw_result.begin(), raw_result.end());
      raw_result.erase(0,1);
    }
    

    Viele Grüße
    freakC++



  • Hat jemand was dagegen?

    Warum sollte es eine Methode und nicht eher eine freie Funktion sein?



  • weil ich die Funktion nur in meiner Klasse brauche. Das würde dem Prinzip der Kapselung wiedersprechen.

    Später brauche ich die Klasse nur als Hilfsmittel. Diese Formatierungsfunktion ist privat und muss nicht nach außen sichtbar sein.


Anmelden zum Antworten