在32位或64位中检测一个字节中的ASCII字符

3

当我在寻求比依次检查每个字节更快的C语言strlen函数时,我找到了这个宏:

#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)

这个宏读取4个字节,当它至少找到一个NUL字节时返回1。否则返回0。

我想知道是否可以使用相同的技术来查找ASCII表中的任何字符(我不希望使用逐字节循环)。

我尝试了很多组合,最好的结果是:

// in this example I wanted to find a '#'

int32_t detectsharp(int32_t c) {
    c = ~(c - 0x24242424) & ~c;
    return ((c - 0x01010101) & ~c & 0x80808080);
}

但它不能与0x22222222 ("""")或类似0x24212121 ($!!!)的内容一起使用。


3
如果您对整数使用异或运算0x23232323,然后尝试检测零字节,它应该可以工作。这应该可以检测到#符号。对于所有其他字符也是类似的。 - Ctx
你应该使用 uint32_t 而不是 int32_t - chqrlie
请注意,SIMD比这种技巧要快得多。请参见使用SSE比较16字节字符串使用SSE 4.2指令实现strcmp、strlen和strstr。最好将工作留给标准库,如果可能的话,它们将使用SIMD。 - phuclv
1个回答

4

如果你先使用异或运算符将任何字符与整数进行运算,那么这个函数就能够检测到该字符。

#define DETECTCHAR(x,c) (DETECTNULL((x) ^ ((c)*0x01010101l) ))

乘法将char分配到int的4个字节中,异或运算会清除char所在的字节。

你可能想将 c 强制转换为 (unsigned char)(c),以避免在默认情况下 char 为有符号字符的平台上出现负字符问题。此外,l 后缀是可选的,并且更易读为 L - chqrlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接