Effektviste Möglichkeit vom Anfang eines Strings zu löschen



  • Hallo.
    Ich hab einen sehr langen std::string, der eine Zahl repräsentiert. Dieser hat eine fixe Länge und enthält deshalb führende Nullen (aber niemals Länge 0). Nun wollte ich eine Ausgabefunktion schreiben, die den String so verändert, dass die führenden Nullen wegfallen.
    Meine Funktion sieht so aus:

    void delete_leading_zeros(std::string& zahl) //mit erase
    {
        std::string::size_type pos = zahl.find_first_not_of('0');
        if(pos == std::string::npos) //string == empty oder besteht nur aus Nullen
            zahl = "0";
        else
            zahl.erase(0, pos);
    }
    

    Eine häufige Imeplentierung von std::string ist ja, dass std::string von std::vector erbt, weshalb erase wahrscheinlich nicht sehr effizient umgesetzt werden kann. Würdet ihr diese Version bevorzugen?

    void delete_leading_zeros(std::string& zahl) //mit swap
    {
        std::string::size_type pos = zahl.find_first_not_of('0');
        if(pos == std::string::npos)
            zahl = "0";
        else
        {
            std::string tmp( zahl.begin() + pos, zahl.end());
            zahl.swap(tmp);
        }
    
    }
    

    Oder vielleicht sollte man ja diese Version benutzen, da keine temporäre Variable anfällt:

    void delete_leading_zeros(std::string& zahl) //mit memmove / resize
    {
        std::string::size_type pos = zahl.find_first_not_of('0');
        if(pos == std::string::npos)
            zahl = "0";
        else
        {
            std::copy(zahl.begin() + pos, zahl.end(), zahl.begin());
            zahl.resize(zahl.size()-pos);
        }
    }
    

    Für welche Variante würdet ihr euch entscheiden?



  • 1. Du holst dir den C-Pointer auf den String
    2. Du kennst die Anzahl der führenden Nullen
    3. Du addierst zu diesem C-Pointer diese Anzahl hinzu
    4. Diese neue Pointer-Position gibst du aus

    ostream& operator << (ostream& stream, const MyBlubb& obj)
    {
        const char* abhier = obj.getString().c_str() + obj.getNullCount();
        stream << abhier;
        return sream;
    }
    

    oder so. Ungetestet und sicher falsch.

    MfG SideWinder



  • Daran hatte ich auch schon gedacht. Allerdings möchte ich den übergebenen String direkt verändern. Würde ich mir das interne cstring array holen, müsste ich dieses ja zwischenspeichern und zurück an den string übergeben:

    void delete_leading_zeros(std::string& zahl)
    {
        std::string tmp ( zahl.c_str() + getNullCount() );
        zahl.swap(tmp);
    }
    

    Was ja im Wesentlichen die 2. Variante ist.



  • In der Regel dürfte std::string::erase wohl so implementiert sein, wie du es von der dritten Variante erwartest. Allerdings bin ich grad nicht sicher, ob std::copy bei sich überschneidenden Speicherbereichen, wie sie hier ja durchaus zu erwarten sind, definiertes Verhalten liefert - ich habe ein bisschen die Vermutung, dass es für diesen speziellen Fall oft mit memcpy o.ä. implementiert sein dürfte. std::string::erase dagegen hält garantiert, was es verspricht.

    Ich bin in solchen Fällen geneigt, Scott Meyers zuzustimmen und std::string zu vertrauen; der kennt seine Implementierung im Zweifel besser als du. Deine Funktion, wie sie jetzt aussieht, scheint mir die sinnvollste Implementierung.

    Gibt es eigentlich einen besonderen Grund, warum du das ganze nicht einfach in eine Zahl parst?



  • seldon schrieb:

    Gibt es eigentlich einen besonderen Grund, warum du das ganze nicht einfach in eine Zahl parst?

    Wie meinst du das? Meine Klasse repräsentiert eine große Zahl (deutlich größer als 2^64, weshalb umwandeln in int oder long long wegfällt). Die Klasse besitzt auch eine .print() funktion, die die Zahl unformatiert ausgibt ( zB: "00000328743"). Die Klasse soll sich aber so verhalten, wie die POD's. Und wenn man diese über streams ausgibt, werden ja die führenden Nullen abgeschnitten. Daher überlade ich den << Operator, indem ich mir über .print() den entsprechenden string hole, von dem die führenden Nullen entferne und dann ausgebe.



  • das ist in meinen augen der falsche weg, den du da nimmst...
    wieso gibt print die führenden nullen mit aus?

    und auf ein if mehr oder weniger kommts dann auch nicht mehr an?!

    vermutlich würde ich ein remove_if nehmen - wäre aber nicht sonderlich effektiv... wenns doch schnell sein sollte, dann in etwa so:

    void erase_leading_zeros(std::string& value)
    {
      std::string::size_type index = value.find_first_not_of('0');
    
      if(index == 0)
        return;
    
      if(index == std::string::npos)
      {
        value = '0';
        return;
      }
    
      value.erase(value.begin(), value.begin()+index);
    }
    

    bb



  • Wenn dich das Entfernen der Nullen nur bei der Ausgabe interessiert, ist SideWinders Vorschlag wohl der beste. Du brauchst dann ja nicht den string selbst zu verändern.



  • repräsentiert eine große Zahl (deutlich größer als 2^64,

    GNU Multiple Precision Arithmetic Library, the fastest bignum library on the planet

    Und ansonsten: ich wuerde mit string::substr() arbeiten.



  • ich würde boost::algorithm::trim_left_if verwenden.



  • string erase schrieb:

    Hallo.
    Ich hab einen sehr langen std::string, der eine Zahl repräsentiert. Dieser hat eine fixe Länge und enthält deshalb führende Nullen (aber niemals Länge 0). Nun wollte ich eine Ausgabefunktion schreiben, die den String so verändert, dass die führenden Nullen wegfallen.

    Oder Du gehst das Problem tiefer an.
    a) (nicht men Freund) Du nimmst nicht string, sondern scheibst Dir eine Klasse, die schnell vorne löschen kann.
    b) (mag ich) Du speicherst im string die Zahlen alle andersrum, also statt 1024="00001024" lieber 1024="42010000". Den ganzen Berechnungsalgos macht das wenig aus, aber man kann immer schnell schnippeln.



  • Der Vorteil von b) ist, das gleichwertige Digits an gleicher Position in den Strings stehen, egal wie lang so ein String ist.



  • Versuche es einfach in ANSI-C mit strcpy(string,""); - dann ist der String leer. Mit der Standardklasse 'string' von C++ gibt es vergleichbares. Wo ist das Problem? 😮
    Müssen wir hier wirklich die Grundlagen der Programmierung diskutieren? :p



  • berniebutt schrieb:

    Versuche es einfach in ANSI-C mit strcpy(string,""); - dann ist der String leer.

    Oder besser mit *string='\0';

    berniebutt schrieb:

    Mit der Standardklasse 'string' von C++ gibt es vergleichbares.

    Ja.

    berniebutt schrieb:

    Wo ist das Problem? 😮

    Keiner wollte den string einfach nur leeren.

    berniebutt schrieb:

    Müssen wir hier wirklich die Grundlagen der Programmierung diskutieren? :p

    Diese Frage scheint mir mehrere Bedeutungen zu haben. Diskutieren müssen wir die Grundlagen schon. Immer wieder. Aber nicht in diesem Thread, fürchte ich. Zum Beschimpfen der Anfänger hat Marc++us mich engagiert. Diese Mühe kannst Du Dir sparen, wenn Du Lust hast. Natürlich bin ich über deine tatkräftige Hilfe auch dankbar. Aber auch vielleicht besser in anderen Threads. Hier http://www.c-plusplus.net/forum/viewtopic-var-t-is-262340-and-highlight-is-.html wäre toll.



  • volkard schrieb:

    string erase schrieb:

    Hallo.
    Ich hab einen sehr langen std::string, der eine Zahl repräsentiert. Dieser hat eine fixe Länge und enthält deshalb führende Nullen (aber niemals Länge 0). Nun wollte ich eine Ausgabefunktion schreiben, die den String so verändert, dass die führenden Nullen wegfallen.

    Oder Du gehst das Problem tiefer an.
    a) (nicht men Freund) Du nimmst nicht string, sondern scheibst Dir eine Klasse, die schnell vorne löschen kann.
    b) (mag ich) Du speicherst im string die Zahlen alle andersrum, also statt 1024="00001024" lieber 1024="42010000". Den ganzen Berechnungsalgos macht das wenig aus, aber man kann immer schnell schnippeln.

    Naja man kann sich auch einfach den Offset merken.

    struct my_string
    {
        std::string m_str;
        size_t m_offset;
        char const* c_str() const { return m_str.c_str() + m_offset; }
    };
    

    Ich meine nur WENN man schon selbst was programmieren will. Macht aber alles nicht wirklich Sinn.



  • volkard schrieb:

    berniebutt schrieb:

    Müssen wir hier wirklich die Grundlagen der Programmierung diskutieren? :p

    Diese Frage scheint mir mehrere Bedeutungen zu haben. Diskutieren müssen wir die Grundlagen schon. Immer wieder. Aber nicht in diesem Thread, fürchte ich. Zum Beschimpfen der Anfänger hat Marc++us mich engagiert. Diese Mühe kannst Du Dir sparen, wenn Du Lust hast. Natürlich bin ich über deine tatkräftige Hilfe auch dankbar. Aber auch vielleicht besser in anderen Threads. Hier http://www.c-plusplus.net/forum/viewtopic-var-t-is-262340-and-highlight-is-.html wäre toll.

    War nicht als Beschimpfung gemeint, der Rüffel ist berechtigt. Natürlich sollen und müssen wir Anfängern auch Grundlagen erklären, wenn sie an einer Stelle hängenbleiben. Shame on me - ich verspreche, mich zu bessern!


Anmelden zum Antworten