web-dev-qa-db-ja.com

16進値の文字列を文字列に変換する方法は?

次のような文字列があるとします。

_string hex = "48656c6c6f";
_

2文字ごとにASCII値の16進表現に対応します。例:

_0x48 0x65 0x6c 0x6c 0x6f = "Hello"
_

では、ルックアップASCIIテーブルを作成せずに_"hello"_から_"48656c6c6f"_を取得するにはどうすればよいですか?atoi()は明らかにここでは機能しません。

14
int len = hex.length();
std::string newString;
for(int i=0; i< len; i+=2)
{
    string byte = hex.substr(i,2);
    char chr = (char) (int)strtol(byte.c_str(), null, 16);
    newString.Push_back(chr);
}
17
James Curran

16進数は2進数に非常に簡単に変換できます。

// C++98 guarantees that '0', '1', ... '9' are consecutive.
// It only guarantees that 'a' ... 'f' and 'A' ... 'F' are
// in increasing order, but the only two alternative encodings
// of the basic source character set that are still used by
// anyone today (ASCII and EBCDIC) make them consecutive.
unsigned char hexval(unsigned char c)
{
    if ('0' <= c && c <= '9')
        return c - '0';
    else if ('a' <= c && c <= 'f')
        return c - 'a' + 10;
    else if ('A' <= c && c <= 'F')
        return c - 'A' + 10;
    else abort();
}

したがって、文字列全体を実行するには、次のようになります。

void hex2ascii(const string& in, string& out)
{
    out.clear();
    out.reserve(in.length() / 2);
    for (string::const_iterator p = in.begin(); p != in.end(); p++)
    {
       unsigned char c = hexval(*p);
       p++;
       if (p == in.end()) break; // incomplete last digit - should report error
       c = (c << 4) + hexval(*p); // + takes precedence over <<
       out.Push_back(c);
    }
}

strtolがあり、それを使用するとコードが大幅に少なくなるのに、なぜこのようにするのかと合理的に尋ねるかもしれません(James Curranの回答のように)。そうですね、そのアプローチは完全な10進数の桁数遅くなります。これは、各2バイトのチャンクをコピーし(ヒープメモリを割り当てる可能性があります)、一般的なテキストから数値への変換ルーチンを呼び出すためです。上記の特殊なコードほど効率的に書くことはできません。クリスチャンのアプローチ(istringstreamを使用)はthatより5倍遅いです。これがベンチマークプロットです。デコードするデータの小さなブロックでも違いがわかります。違いが大きくなると、それは露骨になります。 (両方の軸が対数スケールであることに注意してください。)

Benchmark comparison plot

これは時期尚早の最適化ですか?地獄はありません。これは、ライブラリルーチンに押し込まれ、忘れられて、1秒間に数千回呼び出される種類の操作です。悲鳴を上げる必要があります。私は数年前にSHA1チェックサムを内部で非常に多用するプロジェクトに取り組みました-それらを16進数ではなく生のバイトとして保存し、それらを表示する必要がある場合にのみ変換することで、一般的な操作を10〜20%高速化しましたユーザー-そしてそれはすでに死ぬように調整された変換関数でした。より大きなタスクが何であるかに応じて、ここでのパフォーマンスよりも簡潔さを正直に好むかもしれませんが、そうであれば、一体なぜC++でコーディングしているのですか?

また、教育学的な観点から、この種の問題の手書きの例を示すことは有用だと思います。それはコンピュータがしなければならないことについてもっと明らかにします。

18
zwol

コメントはできませんが、zwolのソリューションにはバグがあります。

c = c << 4 + hexval(*p);

正しくあります

c = (c << 4) + hexval(*p);

シフト演算子は追加よりも優先順位が低いため

6
std::string str("48656c6c6f");
std::string res;
res.reserve(str.size() / 2);
for (int i = 0; i < str.size(); i += 2)
{
    std::istringstream iss(str.substr(i, 2));
    int temp;
    iss >> std::hex >> temp;
    res += static_cast<char>(temp);
}
std::cout << res;
4
Christian Ammer

strtol 各16進数のペアに0xを追加すると、その仕事をするはずです。

0
schnaader