【问题标题】:How to convert a string of hex values to a string?如何将一串十六进制值转换为字符串?
【发布时间】:2011-04-16 23:28:54
【问题描述】:

假设我有一个类似的字符串:

string hex = "48656c6c6f";

其中每两个字符对应其ASCII的十六进制表示,值,例如:

0x48 0x65 0x6c 0x6c 0x6f = "Hello"

那么我怎样才能从"48656c6c6f" 获得"hello" 而无需创建查找ASCII 表? atoi() 显然不会在这里工作。

【问题讨论】:

标签: c++ string stl ascii


【解决方案1】:

如果您将0x 添加到每个十六进制数字对,strtol 应该可以完成这项工作。

【讨论】:

  • 你不需要 0x,你只需要传递 16 作为第三个参数。但是 strtol 对这项工作来说太过分了......
  • 啊,我明白了。是的,只是想快速响应,这当然不是最好的解决方案(尤其是对于 C++)。
【解决方案2】:
int len = hex.length();
std::string newString;
for(int i=0; i< len; i+=2)
{
    std::string byte = hex.substr(i,2);
    char chr = (char) (int)strtol(byte.c_str(), null, 16);
    newString.push_back(chr);
}

【讨论】:

  • 我会选择这个答案,因为它不依赖于整数长度
  • int 中存储一个长度。现在你为什么要这样做?
  • @sbi:如果我不这样做,它会在每次循环中调用 string::length() 。因为我知道它会保持不变,所以不需要进行额外的工作。 (除非你质疑我选择 int 而不是说 long ——因为我认为这对于比 int 长度更长的字符串更实用)
  • @James 是 string::length() O(1)?
  • 我同意 sbi:为此使用 size_t。无论如何,您的 IDE/编译器都应该将此标记为警告。
【解决方案3】:

十六进制数字很容易转换为二进制:

// 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 的回答)。嗯,这种方法要慢一个完整的十进制数量级,因为它复制每个两个字节的块(可能为此分配堆内存),然后调用一个通用的文本到数字的转换例程,不能像上面的专用代码那样高效地编写。 Christian 的方法(使用 istringstream)比 that 慢五倍。这是一个基准图 - 即使要解码一小块数据,您也可以分辨出差异,并且随着差异变大,它变得明显。 (请注意,两个轴都在对数刻度上。)

这是过早的优化吗?一定不行。这种操作会被塞入库例程中,被遗忘,然后每秒调用数千次。它需要尖叫。几年前我参与了一个项目,该项目在内部大量使用了 SHA1 校验和——我们通过将常见操作存储为原始字节而不是十六进制来获得 10-20% 的加速,仅当我们必须将它们展示给用户——那是已经被调到死的转换功能。老实说,这里可能更喜欢简洁而不是性能,这取决于更大的任务是什么,但如果是这样,你到底为什么要使用 C++ 编码?

另外,从教学的角度来看,我认为展示这类问题的手工编码示例很有用;它揭示了更多关于计算机必须做什么的信息。

【讨论】:

  • -1:忽略标准库设施。我要加分,因为它们在以前的帖子中提到过。
  • 我忽略了它们,因为它们比手动操作慢十倍。见编辑。
  • +1 用于关注性能,因为这可能是经常使用的东西,例如在性能关键循环中。也为了意识到它不是“正确”的教科书答案,并建议将其隐藏在库函数后面。这是丑陋代码的最佳位置:在漂亮的界面后面。
  • 基准+1;虽然我认为 James 和 Christian 的实现对于我将要使用它的地方来说很好。
【解决方案4】:
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;

【讨论】:

  • 可以预分配,长度提前知道!
  • 我不想这么说,但这比接受的答案慢五倍(它本身比我的答案慢十倍)。
  • @Zack:我编辑了我的答案并像 Caron 提到的那样预先分配了输出字符串的大小,而你已经这样做了。我很感兴趣我的解决方案和你的解决方案之间的性能差异是什么?
  • 我的基准测试程序为所有三种情况预先分配了输出字符串;唯一的区别是 for 循环中的代码。
【解决方案5】:

我不能评论,但是zwol的解决方案有一个bug:

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

正确

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

因为移位运算符的优先级低于加法

【讨论】:

  • 感谢您指出这个错误。我很尴尬自己没有注意到它,我已经更正了我的答案。 (ti7 这样做的尝试被那些过于严格遵守规则的人拒绝了。)
  • 遗憾的是,我只是在 ti7 尝试编辑我的答案时才听说 - 我们没有收到有关问题的其他答案的通知。如果可以的话,这就是为什么评论会更好。
猜你喜欢
  • 2013-02-07
  • 2018-01-22
  • 1970-01-01
  • 2015-02-06
  • 2019-07-27
  • 1970-01-01
  • 2012-02-03
  • 1970-01-01
  • 2017-08-23
相关资源
最近更新 更多