String to Integer



  • Moin ­čśë

    ich bin mal wieder am (ver)zweifeln. Wieso kommt da unten eine 99 raus!?

    // String to Integer
    uint32_t str2int(string &str, uint8_t base = 16 ){
        size_t n = 0;
        uint32_t result = 0;
        for( string::reverse_iterator i = str.rbegin(); i != str.rend(); i++, n++){
            uint8_t ord = int(*i);
            if( ord >= 48 && ord <= 57  )     ord -= 48; // Ziffern 
            else if( ord >= 97 && ord <= 102) ord -= 87; // a-f
            else                              ord -= 55; // A-F
            size_t f = std::pow(base, n);
            result += f*ord;    
        }
        return result;
    }
    
    int main(){
        cout << "Content-Type:text/plain\n\n";
        string str = "100";
        cout << "\n" << str2int(str, 10) << "\n";
        
        return 0;
    }
    

    Wo doch der Code mit Basis 16 einwandfrei funktioniert. Was habe ich hier wiedermal falsch gemacht?

    Mit der Bitte um Hilfe und Danke im Vorab, roro



  • @_ro_ro

    Oh, sorry, Datentyp mi├čachtet. Mein Fehler. Bitte um Entschuldigung.

    Sch├Ânes Wochenende!


  • Banned

    Nutze bitte immer spitze, geschweifte Klammern { }

    Pr├╝fe erst, ob kleiner 48, dann ob kleiner 57, dann ob kleiner 102.

    Verwende '9' 'F' und 'f' anstatt Zahlen

    Verwende sprechende Bezeichner, f ist nicht sprechend

    Verwende const bei Parametern

    Gib auf die richtigen Datentypen acht

    Ersetze int(*i) durch (uint8_t) *i um den Lesefluss zu erh├Âhen



  • Guck dir mal die Konvertierungsfunktionen aus der STL an, die braucht man nicht mehr selbst zu schreiben.



  • @DocShoe

    Ja schon aber darum geht es nicht. Eine Frage bleibt dennoch: In meinem Code lasse ich eine numerische Variable n mitlaufen damit ich den Index bekomme. Wie komme ich an den Index ohne diese zus├Ątzliche Variable? Der m├╝sste doch im Iterator stecken

    for( string::reverse_iterator i = str.rbegin(); i != str.rend(); i++, n++)
    

    also in i.

    Viele Gr├╝├če, roro.



  • @nameName sagte in String to Integer:

    Verwende '9' 'F' und 'f' anstatt Zahlen

    Coller Tipp!!!! Danke!!!!

    Machts lesbarer, viele Gr├╝├če!!


  • Banned

    @_ro_ro sagte in String to Integer:

    Coller Tipp!!!! Danke!!!!

    Das klingt jetzt so, als seien meine ├╝brigen Tipps/Review bl├Âd

    @_ro_ro sagte in String to Integer:

    Wie komme ich an den Index ohne diese zus├Ątzliche Variable?

    Nimm doch eine einfache, normale for-Schleife, anstatt das iterator-Geraffel... Bei std::string s ist das zul├Ąssig



  • @_ro_ro sagte in String to Integer:

    @DocShoe

    Ja schon aber darum geht es nicht. Eine Frage bleibt dennoch: In meinem Code lasse ich eine numerische Variable n mitlaufen damit ich den Index bekomme. Wie komme ich an den Index ohne diese zus├Ątzliche Variable? Der m├╝sste doch im Iterator stecken

    for( string::reverse_iterator i = str.rbegin(); i != str.rend(); i++, n++)
    

    also in i.

    Viele Gr├╝├če, roro.

    Worum denn, willst du das nur als ├ťbungsaufgabe umsetzen? Und wof├╝r brauchst du den Index? Vielleicht erkl├Ąrst du mal, was genau du machen m├Âchtest.

    ├ťber std::distance kannst du die Anzahl der Elemente zwischen zwei Iteratoren bestimmen. Bei reverse-iteratoren also die Anzahl der Elemente von hinten nach vorne. Vielleicht ist der bessere Ansatz, den string von vorne nach hinten zu durchlaufen, dann kommst du auch ohne das teure pow aus. Und das Iterieren ├╝ber den Inhalt geht mit range-based-for auch eleganter:

    uint32_t str2uint( std::string const& str, unsigned int base /*=10*/ )
    {
       uint32_t retval = 0;
       for( auto ch : str )
       {
          retval *= base;
          ...
       }
       return retval;
    }
    

    Mir fallen da noch zwei Dinge auf:

    1. Die Funktion hei├čt str2int, konvertiert aber zu uint32_t. Ist str2uint da nicht vielleicht sinnvoller?
    2. Du ├╝bergibst einen base Parameter, gehst aber davon aus, dass er immer 16 ist (jedenfalls wird jedes Zeichen auf den Bereich 0-15 gepr├╝ft). F├╝r alle anderen Bases funktioniert deine Konvertierungfunktion nicht richtig.


  • @DocShoe sagte in String to Integer:

    Du ├╝bergibst einen base Parameter, gehst aber davon aus, dass er immer 16 ist

    Nein. Die base wird ├╝bergeben. 16 ist nur der Default. Der numerische Index ist die Potenz f├╝r die base, wird also innerhalb der for()-Schleife gebraucht.

    float f = pow(base,n);
    

    ist der Koeffizient. So ergibt sich der numerische Wert zu

    result += f*ord;
    

    und das wird aufsummiert.

    MFG



  • @_ro_ro

    Ja, das sehe ich.

    • was passiert bei base > 16? Teste doch mal mit base 18 und dem Alphabet [0-9a-h] (Kleinbuchstaben!)
    • was passiert bei Base = 10 und dem string "deadbeef", was ist dann das Ergebnis?

    PS:
    Du hast immer noch nicht verraten, warum du die Funktionen aus der STL nicht benutzen kannst/m├Âchtest.



  • @DocShoe

    Mein Compiler kennt std::stoi noch nicht. Also bleibt nur der Eigenbau. Eine Pr├╝fung auf a-fA-F ├╝berlege ich mir noch. Die Perl-Funktion hex wirft eine Exception f├╝r den Fall da├č Buchstaben > F oder > f ankommen. In meiner Umgebung w├Ąre eine Exception nat├╝rlich auch ne L├Âsung.

    MFG



  • @DocShoe sagte in String to Integer:

    ├ťber std::distance kannst du die Anzahl der Elemente zwischen zwei Iteratoren bestimmen. Bei reverse-iteratoren also die Anzahl der Elemente von hinten nach vorne. Vielleicht ist der bessere Ansatz, den string von vorne nach hinten zu durchlaufen, dann kommst du auch ohne das teure pow aus. Und das Iterieren ├╝ber den Inhalt geht mit range-based-for auch eleganter:

    Nach meinem Verst├Ąndnis braucht man eher ein pow, wenn man den Zahlen-String eben von vorne nach hinten durchl├Ąuft. So wie ich den Code verstehe wird in f der Stellenwert berechnet,
    also der wert, mit dem der Ziffernwert multipliziert werden muss um den Gesamtwert der Zahl zu ermitteln (Der Stellenwert w├Ąre also 1, 10, 100, 1000, ... f├╝r Dezimalzahlen oder 1, 2, 4, 8, ... f├╝r Bin├Ąrzahlen).

    R├╝ckw├Ąrts ist also okay, aber man braucht den Stellenwert nicht jedes mal mit der Flie├čkomma-Funktion pow neu zu berechnen, sondern man kann ihn einfach mitf├╝hren. Dann kann man n├Ąmlich direkt mit Integer-Werten arbeiten und braucht auch das n nicht mehr:

    uint32_t place_value = 1; 
    for (...)
    {
        ...
        place_value = place_value * base;
    }
    
    1. Du ├╝bergibst einen base Parameter, gehst aber davon aus, dass er immer 16 ist (jedenfalls wird jedes Zeichen auf den Bereich 0-15 gepr├╝ft). F├╝r alle anderen Bases funktioniert deine Konvertierungfunktion nicht richtig.

    So wie ich das lese funktioniert das f├╝r alle Basen ÔëĄ\le 16. Mit ord wird lediglich der Ziffernwert des aktuellen Zeichens ermittelt und dieser ├Ąndert sich zwischen verscheidenen Basen nicht: 1 (hex) == 1 (dezimal) == 1 (bin├Ąr). Das sollte also okay sein.



  • @Finnegan

    Ne, das funktioniert auch f├╝r Base = 10 nicht, weil nirgendwo eine ├ťberpr├╝fung eines einzelnen Zeichens gegen base stattfindet. Es werden alle Werte von 0-15 richtig geparst, aber es findet keine Plausibilit├Ątspr├╝fung statt. Im Beispiel mit 'deadbeef' wird bei base10 fr├Âhlich mit Werten >9 gerechnet. Das Ganze funktioniert nur mit base16 richtig.

    @_ro_ro :
    Welchen Compiler benutzt du denn? Die STL Funktionen sind mit dem Standard C++11 eingef├╝hrt worden, das ist ├╝ber 10 Jahre her. Musst du f├╝r deinen Compiler vllt nur den Sprachstandard festlegen (per Kommandozeilenparameter, Projekteinstellungen, etc)?



  • @DocShoe

    Nat├╝rlich werde ich meinerseits einer angemessen Fehlerbehandlung nachkommen. Ich habe jedoch gerade eben festgestellt da├č es in der Server-Umgebung bereits eine Einrichtung gibt die malformed Hex-Strings erkennt. D.h., da├č meine Fehlerbehandlung gar nicht mehr greift weil der Request schon vorher von abgefangen wird.

    MFG



  • @DocShoe sagte in String to Integer:

    @Finnegan

    Ne, das funktioniert auch f├╝r Base = 10 nicht, weil nirgendwo eine ├ťberpr├╝fung eines einzelnen Zeichens gegen base stattfindet. Im Beispiel mit 'deadbeef' wird bei base10 fr├Âhlich mit Werten >9 gerechnet. Das Ganze funktioniert nur mit base16 richtig.

    Ja, das stimmt f├╝r ung├╝ltige Eingaben. Da bekommt base16 aber ebenfalls z.B. mit 1Z56 Probleme. F├╝r Zeichen die mit einem Wert > 102 codiert werden, wird schliesslich der Wert direkt in ord ├╝bernommen. Da habe ich jetzt erstmal nicht den Fokus drauf gelegt sondern nur auf den Grundalgrorithmus. Es muss nat├╝rlich sichergestellt werden, dass nur g├╝ltige Ziffern verarbeitet werden bzw. die gesamte Eingabe stimmt.



  • @_ro_ro sagte in String to Integer:

    @DocShoe

    Mein Compiler kennt std::stoi noch nicht. Also bleibt nur der Eigenbau. Eine Pr├╝fung auf a-fA-F ├╝berlege ich mir noch. Die Perl-Funktion hex wirft eine Exception f├╝r den Fall da├č Buchstaben > F oder > f ankommen. In meiner Umgebung w├Ąre eine Exception nat├╝rlich auch ne L├Âsung.

    MFG

    Wenn es der gleiche compiler ist wie aus diesem post https://www.c-plusplus.net/forum/topic/354463/fehlermeldung-wenn-instanz-ohne-new-erstellt-wird/2
    dann stellen sich 2 Fragen:

    1. Wieso kannst du nicht einen neuere Version des compilers nutzen? Denn der mingw build 5.3.0 stammt aus 2015. Aktuell ist 11.0.0 (mit gcc 13.1.0)
    2. Wieso aktivierst du nicht den c++11 mode des compilers? Wie ich es hier grob beschrieben habe? https://www.c-plusplus.net/forum/topic/354463/fehlermeldung-wenn-instanz-ohne-new-erstellt-wird/4

  • Banned

    #include <cstdint>
    #include <string>
    #include <set>
    #include <map>
    #include <stdexcept>
    #include <iostream>
    
    bool baseValid(const uint8_t base)
    {
        std::set<uint8_t> bases = {2, 4, 8, 10, 16, 32};
        return bases.find(base) != bases.end();
    }
    
    uint32_t str_to_uint32(const std::string &str, const uint8_t base = 10)
    {
        if (!baseValid(base))
        {
            throw std::invalid_argument("Invalid base");
        }
        std::map<char, uint8_t> charToNum = {{'0', 0}, {'1', 1}, {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5}, {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'a', 10}, {'b', 11}, {'c', 12}, {'d', 13}, {'e', 14}, {'f', 15}, {'g', 16}, {'h', 17}, {'i', 18}, {'j', 19}, {'k', 20}, {'l', 21}, {'m', 22}, {'n', 23}, {'o', 24}, {'p', 25}, {'q', 26}, {'r', 27}, {'s', 28}, {'t', 29}, {'u', 30}, {'v', 31}};
        uint32_t result = 0;
        for (auto c : str)
        {
            auto cl = std::tolower(c);
            if (charToNum.find(cl) == charToNum.end())
            {
                throw std::invalid_argument("Invalid character");
            }
            result = result * base + charToNum[cl];
        }
        return result;
    }
    
    int main(int argc, char const *argv[])
    {
        std::cout << str_to_uint32("deadbeef", 16) << "\n";
        std::cout << str_to_uint32("01237") << "\n";
        return 0;
    }
    

    Bei den includes bin ich mir unsicher, ob alle richtig sind. ­čśĽ

    Des Weiteren, bitte die Kompilierzeile mit angeben.



  • @nameName

    die Fehlerbehandlung geht auch so:

        uint32_t str2int(string &str, uint8_t base = 16 ){
            size_t n = 0;
            uint32_t result = 0;
            for( string::reverse_iterator i = str.rbegin(); i != str.rend(); i++, n++){
                uint8_t ord = int(*i);
                if( ord >= '0' && ord <= '9'  )    {ord -= 48;} // 0-9
                else if( ord >= 'a' && ord <= 'f') {ord -= 87;} // a-f
                else if( ord >= 'A' && ord <= 'F') {ord -= 55;} // A-F
                else{            
                    throw string("Wrong Character in Parameter-String");
                }            
                float f = pow(base,n);
                result += f*ord;    
            }
            return result;
        }
    

    Also gleich da wo es knallen k├Ânnte.

    Danke auch und Viele Gr├╝├če!!!


  • Banned

    Bitte Codeschnipsel immer formatiert posten... Gebot des Anstands.



  • @nameName sagte in String to Integer:

    Bitte Codeschnipsel immer formatiert posten... Gebot des Anstands.

    nun, ich bevorzuge in Sachen Einr├╝ckung den Style Java Sun. Und der ist absolut legitim ­čśë

    Viele Gr├╝├če.


Log in to reply