快速ascii块为整数

时间:2011-07-28 18:46:44

标签: c++

在stl中是否有任何将ascii数据转换为其十六进制表示的整数形式的命令?例如:“abc” - > 0x616263。

我有我能想到的最基本的方式:

uint64_t tointeger(std::string){
    std::string str = "abc";
    uint64_t value = 0;                  // allow max of 8 chars
    for(int x = 0; x < str.size(); x++)
        value = (value << 8) + str[x];
    return value;
}

如上所述:tointeger("abc");返回值0x616263

但这太慢了。因为我必须使用这个功能数十万次,所以它显着减慢了我的程序。有4或5个函数依赖于这个函数,并且每个函数被调用数千次,此外这个函数被调用数千次

更快的方法是什么?

5 个答案:

答案 0 :(得分:4)

您希望将字符串中的ASCII字符打包为64位整数。

由于std :: string不是内在类型,为安全起见,请将数据复制到缓冲区中:

uint_64 values[100]; // Allocate memory on a 64-bit boundary.

char * p = (char *) values; // Point to the memory as characters.

std::string example("beethoven");

std::copy(example.c_str(), p, example.length();

就对齐而言,复制更安全。为了更快,但更危险,只需避免副本:

  uint_64 danger;
  danger = *((uint_64 *) example.c_str());

std::string::c_str方法返回指向文本的c样式字符串表示的指针,但不保证文本永远持续,因此需要复制。此外,指针仅保证在字符对齐上。因此,如果它恰好位于地址0x1003处,则处理器可能会生成一个调整错误(或因为必须在未对齐的边界处获取而减速)。

编辑1:

此方法未考虑Endianness。该方法使用平台的Endianness。改变Endianness会降低性能。

答案 1 :(得分:2)

您是否尝试过多字符常量?即

int value = 'abc';

答案 2 :(得分:1)

编辑:重新阅读这个问题看起来意图是最多8个字符的字符串的BCD转换,除了每个字符使用8位而不是4位。

你的方法看起来很合理,或者你可以使用memcpy(在big-endian上字符串as-is,你必须在little-endian上反转字符串)。

但是,如果这是您的性能瓶颈,我想您可能希望重新考虑为什么您需要这样做数十万次。也许对算法进行根本性改变会比尝试微优化转换产生更大的性能提升。例如,将值存储在内部为uint64_t,并且仅在显示/接口需要时转换为字符串形式。或者只是将其永久存储为字符串,无需将其转换为伪BCD格式。

答案 3 :(得分:0)

做某事的最快方法就是不要这样做。

也许您可以将数据存储为整数,并且只在必要时将其转换为字符串?你还需要将数据转换成几十万次吗?

如果你真的必须,我可能会使用一个简单的固定大小的数组(不是字符串)并展开循环。但这是微观优化,在大多数情况下,最好只是找到一种不同的方式去做你想做的事情。

答案 4 :(得分:0)

如果您对字符串的存储方式有限制,则可以将数据直接转换为int或long。如果你知道你的字符串在末尾用NULL(0)字节填充到至少8字节对齐,那么下面的代码就可以了。

uint64_t value = *(*unint64_t)str;

您当前的代码段没有任何内在效率低下的内容。操作并不慢。由于您允许的最大字符数为8,因此您可以使用开关案例并循环展开。

uint64_t value = 0;
switch(str.size()) {
    case 0:
        value = 0;
        break;
    case 1: // the 2nd char is a null anyways
    case 2:
        value = *(*uint16_t)str;
        break;
    case 3: // the 4th char would be null
    case 4:
        value = *(*uint32_t)str;
        break;
    case 5:
    case 6:
        value = *(*uint32_t)str + *((*uint16_t)(str+4));
        break;
    case 7:
    case 8:
    default: // 8 or more do the first 8 
        value = *(*uint64_t)str;
        break;
}

因为我们使用switch case语句,所编译的代码将是一个跳转表而不是一个循环(每次迭代都需要一个比较操作)。另外,因为我们将内存转换为不同的类型,所以我们不需要分别遍历每个字符串字符/字节。 记忆价值 0x8000 0x65,0x66,0x67,0x00 - &gt; “ABC”,0 大小为3但空终止符使其长度为4个字节,因此我们可以将内存值直接转换为uint32。

我不用c ++编写代码,所以希望编译语义是正确的。