String in Hex-String wandeln - Komisch ...



  • Hallo !

    Ich bin dabei eine Funktion zu schreiben, die einen String in einen Hex-String konvertiert. Hierzu nutze ich bislang folgenden Code, mit dem ich aber nicht sonderlich glücklich bin.

    void strtohex(char *src, char *dest, int len) {
    
    	char tmp[2];
    
    	while(len--) {
    
    		sprintf(tmp, "%02X", *src++);
    		memcpy(dest, tmp, 2);
    		dest += 2;
    
    	}
    
    	*dest = '\0';
    }
    

    Dieser Code funktioniert soweit eigentlich, wäre aber sehr froh über Verbesserungsvorschläge und und vor allen Dingen Optimierungen.

    Nun wollte ich die Funktion aber ein wenig ändern und wundere mich, warum es nicht mehr funktion.
    Folgender Code klappt nicht.

    void strtohex(char *src, unsigned int len) {
    
    	unsigned int nLen = len * 2 + 1;
    	char *pBuf = new char[nLen];
    	char tmp[2];
    
    	while(len--) {
    		sprintf(tmp, "%02X", *src++);
    		memcpy(pBuf, tmp, 2);
    		pBuf += 2;
    	}
    
    	*pBuf = '\0';
    }
    

    In pBuf steht nichts. Wieso ?

    P.S.: Ich möchte/muss die Konvertierung "zu Fuß" erledigen. Kann also nicht std::string oder etwas dergleichen nutzen.



  • HaJo. schrieb:

    Kann also nicht std::string oder etwas dergleichen nutzen.

    memcpy, sprintf aber schon, oder wie? Wenn du eine optimierte Version willst, dann nimm std::stringstream.



  • Hmm ist zu fuss auch strtoul? wenn ja dann ist es subereinfach 😃



  • man nimmt size_t für längen Angaben und nicht int!

    Und wenn du richtig speed rausholen willst, dann ersetz die sprintf Funktion durch etwas selbst geschriebenes, weil sprintf ist nicht nur extrem langsam, sondern auch extrem groß!



  • Wie wär es mit:

    void strtohex(char * dst, char const * src, int len)
    {
       static char hexchar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    
       while(len--) {
          *dst  = hexchar[(*src & 0xF0) >> 4];
          ++dst;
          *dst  = hexchar[*src & 0x0F];
          ++dst;
          ++src;
       }
       *dst = 0;
    }
    


  • Shlo schrieb:

    memcpy, sprintf aber schon, oder wie?

    Ja, wieso. Der Code soll später in eine eigene Stringklasse. (Bis aus sprintf).

    kingruedi schrieb:

    Und wenn du richtig speed rausholen willst, dann ersetz die sprintf Funktion durch etwas selbst geschriebenes, weil sprintf ist nicht nur extrem langsam, sondern auch extrem groß!

    sprintf ist mittlerweile ersetzt durch itoa. Aber was eigenes wäre imho natürlich noch besser, nur ist das eher ein Problem. Ich weiß nicht genau wie ich die eine Dezimalzahl per Code in einer Hexadezimale Zahl unterbringe.

    @Ponto:
    Könntest Du mir erklären warum eine Bitverschiebung um 240 (0xF0) und eine um 15 (0x0F) stattfindet ?



  • HaJo. schrieb:

    @Ponto:
    Könntest Du mir erklären warum eine Bitverschiebung um 240 (0xF0) und eine um 15 (0x0F) stattfindet ?

    Das ist keine Verschiebung. Das ist eine Maskierung. Ich nehme an ein char ist 8 Bit. Dann wird aus den ersten vier Bits das erste Zeichen und aus den letzten vier das zweite Zeichen. Um die lezten Bits zu kriegen mache ich value & 0x0F. Um die ersten Bits zu kriegen mache ich 0xF0 und shifte dann um 4 stellen. Dieses Maskieren ist sogar überflüssig, da beim shiften die unwichtigen Bits verschwinden. Also würde auch value >> 4 reichen.



  • Ponto schrieb:

    Das ist keine Verschiebung. Das ist eine Maskierung. Ich nehme an ein char ist 8 Bit. Dann wird aus den ersten vier Bits das erste Zeichen und aus den letzten vier das zweite Zeichen. Um die lezten Bits zu kriegen mache ich value & 0x0F. Um die ersten Bits zu kriegen mache ich 0xF0 und shifte dann um 4 stellen. Dieses Maskieren ist sogar überflüssig, da beim shiften die unwichtigen Bits verschwinden. Also würde auch value >> 4 reichen.

    Es ist schon etwas klarer, aber die Bitarbeiten sind noch ein Buch mit 5 Siegeln. Deine Funktion ist übrigens unverschämt schnell. 🕶 👍
    Herzlichen Dank auch.

    Aber ein weiteres Problem besteht dennoch:
    Warum funktioniert folgender Source nicht ?

    void strtohex2(char *src, int len)
    {
       static char hexchar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
       char *dst = new char[len * 2 + 1];
    
       while(len--) {
          *dst  = hexchar[(*src & 0xF0) >> 4];
          ++dst;
          *dst  = hexchar[*src & 0x0F];
          ++dst;
          ++src;
       }
       *dst = '\0';
    
       cout << "Hex: " << dst << endl;
    }
    

    Es ist nichts "drin" in dst. Warum ? 😕
    Im Endeffekt soll nämlich der String src ind Hex gewandelt werden und somit soll src auch gleich dst sein.



  • Es wird nichts ausgegeben, da dst auf das Ende des Strings zeigt. Du mußt dir also den Anfang merken. Bei der anderen Version war das kein Problem, da die Funktion auf einer Kopie des Zeigers gearbeitet hat. Folgendes sollte klappen:

    void strtohex2(char *src, int len)
    {
       static char hexchar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
       char *dst = new char[len * 2 + 1];
       char * result = dst;
       while(len--) {
          *dst  = hexchar[*src >> 4];
          ++dst;
          *dst  = hexchar[*src & 0x0F];
          ++dst;
          ++src;
       }
       *dst = '\0';
    
       cout << "Hex: " << result << endl;
    }
    


  • Ponto schrieb:

    Es wird nichts ausgegeben, da dst auf das Ende des Strings zeigt. Du mußt dir also den Anfang merken. Bei der anderen Version war das kein Problem, da die Funktion auf einer Kopie des Zeigers gearbeitet hat. Folgendes sollte klappen:

    Argh, da sollte ich eigentlich schon selber drauf kommen. Vielen Dank jetzt klappt alles bestens. Einen schönen Abend noch. 🙂



  • Schau mal in die Java API. Die wenden in der Klasse Integer für die toString-Methoden wahnwitzige Tricks an, z.B. teilen sie nicht durch 16, sondern durch 16^2 und haben in einem array die ziffern für die 1er und die 16er des Rests abgelegt. 🙂



  • Optimizer schrieb:

    Schau mal in die Java API. Die wenden in der Klasse Integer für die toString-Methoden wahnwitzige Tricks an, z.B. teilen sie nicht durch 16, sondern durch 16^2 und haben in einem array die ziffern für die 1er und die 16er des Rests abgelegt. 🙂

    Wo kann ich denn den Source dazu einsehen ? Ich habe hier kein Java.



  • und beachtet doch den Hinweis mit dem size_t

    void strtohex2(char *src, /**/size_t/**/ len) //<--!!!
    


  • kingruedi schrieb:

    und beachtet doch den Hinweis mit dem size_t

    void strtohex2(char *src, /**/size_t/**/ len) //<--!!!
    

    Ist mittlerweile in meinem Code berücksichtigt. Und versuche auch in Zukunft daran zu denken. 😉
    Aber wieso soll man statt unsigned int size_t schreiben (oder bei int schon) ?



  • size_t solltest du immer nehmen, wenn du mit größen Angaben zu tun hast, weil es vom Standard her festgelegt ist, dass es die größte größe, die du im Speicher darstellen kannst aufnehmen können muss.

    Wenn du int nimmst, passt das zB. auf IA32 Systemen nicht und du baust dir leicht die Möglichkeit eines Integer-Overflow Bugs ein. (siehe zB. http://www.whitefang.com/sup/secure-faq.html#INPUT2)

    Aber leider sind die meisten Tutorial/Bücher Autoren zu unfähig das zu benutzen und so verbreitet sich diese "Seuche" weiter 🙂



  • kingruedi schrieb:

    size_t solltest du immer nehmen, wenn du mit größen Angaben zu tun hast, weil es vom Standard her festgelegt ist, dass es die größte größe, die du im Speicher darstellen kannst aufnehmen können muss.

    Gut, das ist verständlich bis auf größte Größe. Ist damit bei einem WinSystem 64 Bit gemeint oder bringe ich da jetzt total ewtas durcheinander und ein unsigned int == unigned long == unsigned __int32 ?

    P.S.: unsigned int == unsigned long ist wohl nur bei MS Compilern so.



  • std::size_t ist bei 32Bit Systemen idr 32Bit groß, bei 64Bit Systemen aber 64Bit etc.

    aus dem Grund gibt es eben den typedef



  • size_t ist zukunftsorientiert, heutige 32Bit compiler werden size_t wie schon erwähnt als unsigned int darstellen, 64bit compiler mit nem 64bit typ.

    ziel von size_t ist einfach, dass heutiger code auf 64 bit compiler übernommen werden kann, die size_t anders definieren, ich würds mir grausig vorstellen, in einem vector alle unsigned ints in size_t umzuändern^^



  • nicht nur Zukunftsorientiert. Es gibt ja mehr als 32Bit und 64Bit 🙂



  • kingruedi schrieb:

    nicht nur Zukunftsorientiert. Es gibt ja mehr als 32Bit und 64Bit 🙂

    Als noch einmal zusammengefasst. size_t immer bei größeren Integer Typen verwenden.
    Anfangs schriebst du

    kingruedi schrieb:

    man nimmt size_t für längen Angaben und nicht int!

    Meintest du da unsigned int oder ist es auch ratsam komplett auf Integer-Typen wie int/long zu "verzichten" und nur noch egal ob signed/unsigned size_t zu verwenden ?



  • size_t immer bei größeren Integer Typen verwenden.

    Nein, size_t ist wie ich geschrieben habe und wie der Name sagt, für größen Angaben da.

    size_t ist eben nur bei jeder Platform so, dass es die entsprechende größte größe aufnehmen kann. Auf 32Bit Platformen ist das idr. 32Bit, also auch nicht besonders groß.

    Also size_t für Größen und int fürs rechnen oder so


Anmelden zum Antworten