【发布时间】:2011-04-16 23:28:54
【问题描述】:
假设我有一个类似的字符串:
string hex = "48656c6c6f";
其中每两个字符对应其ASCII的十六进制表示,值,例如:
0x48 0x65 0x6c 0x6c 0x6f = "Hello"
那么我怎样才能从"48656c6c6f" 获得"hello" 而无需创建查找ASCII 表? atoi() 显然不会在这里工作。
【问题讨论】:
假设我有一个类似的字符串:
string hex = "48656c6c6f";
其中每两个字符对应其ASCII的十六进制表示,值,例如:
0x48 0x65 0x6c 0x6c 0x6f = "Hello"
那么我怎样才能从"48656c6c6f" 获得"hello" 而无需创建查找ASCII 表? atoi() 显然不会在这里工作。
【问题讨论】:
如果您将0x 添加到每个十六进制数字对,strtol 应该可以完成这项工作。
【讨论】:
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 中存储一个长度。现在你为什么要这样做?
string::length() O(1)?
十六进制数字很容易转换为二进制:
// 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++ 编码?
另外,从教学的角度来看,我认为展示这类问题的手工编码示例很有用;它揭示了更多关于计算机必须做什么的信息。
【讨论】:
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;
【讨论】:
我不能评论,但是zwol的解决方案有一个bug:
c = c << 4 + hexval(*p);
正确
c = (c << 4) + hexval(*p);
因为移位运算符的优先级低于加法
【讨论】: