确定数字是否出现在字符串中的最快方法是什么?

Evi*_*ach 5 c string performance

这个简单的解决方案迅速涌入我的脑海.

#include <ctype.h>

int digit_exists_in
(
    const char *s
)
{
    while (*s)
    {
        if (isdigit(*s))
        {
            return 1;
        }
        else
        {
            s++;
        }
    }

    return 0;
}

int main(void)
{
    int foundDigit = digit_exists_in("abcdefg9ijklmn");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

还有什么其他技术可以提高速度?

被搜索的实际字符串是可变长度,字符本身是ASCII,而不是完整的字符集.字符串是NUL终止的.

Nic*_*oic 18

顺便说一句,liw.fi是对的.我对此感到有点惊讶,因为strcspn必须比isdigit()方法做更普遍的问题,但似乎是这样的:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#define NTESTS 10000
#define TESTSIZE 10000

char stest1[TESTSIZE];
char stest2[TESTSIZE];

int test_isdigit(char *s) {
    while (*s) {
        if (isdigit(*s)) return 1;
        s++;
    }
    return 0;
}

int test_range(char *s) {
    while (*s) {
        if ((*s >= '0') && (*s <= '9')) return 1;
        s++;
    }
    return 0;
}

int test_strcspn(char *s) {
    return s[strcspn(s, "0123456789")] != '\0';
}

int main(int argc, char **argv) {
    long int i;
    for (i=0; i<TESTSIZE; i++) {
        stest1[i] = stest2[i] = 'A' + i % 26;
    }
    stest2[TESTSIZE-1] = '5';

    int alg = atoi(argv[1]);

    switch (alg) {
        case 0:        
            printf("Testing strcspn\n");
            for (i=0; i<NTESTS; i++) {
                assert(test_strcspn(stest1) == 0);
                assert(test_strcspn(stest2) != 0);
            }
            break;
        case 1:
            printf("Testing isdigit() loop\n");
            for (i=0; i<NTESTS; i++) {
                assert(test_isdigit(stest1) == 0);
                assert(test_isdigit(stest2) != 0);
            }
            break;
        case 2:
            printf("Testing <= => loop\n");
            for (i=0; i<NTESTS; i++) {
                assert(test_range(stest1) == 0);
                assert(test_range(stest2) != 0);
            }
            break;
        default:
            printf("eh?\n");
            exit(1);
    }    

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在他们自己的游戏中击败标准库非常困难......(通常的警告适用 - YMMV)

$ gcc -O6 -Wall -o strcspn strcspn.c 

$ time ./strcspn 0
Testing strcspn

real    0m0.085s
user    0m0.090s
sys 0m0.000s

$ time ./strcspn 1
Testing isdigit() loop

real    0m0.753s
user    0m0.750s
sys 0m0.000s

$ time ./strcspn 2
Testing <= => loop

real    0m0.247s
user    0m0.250s
sys 0m0.000s
Run Code Online (Sandbox Code Playgroud)

更新:为了好玩,我添加了一个基于Mike Dunlavey答案的位图查找版本:

char bitmap[256] = {
        /* 0x00 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};

int test_bitmap(char *s) {
    while (!bitmap[*(unsigned char *)s]) s++;
    return (*s);
}
Run Code Online (Sandbox Code Playgroud)

其中略胜一筹(〜.170s)但仍无法触及strcspn!

  • https://www.reddit.com/r/simd/comments/myqv32/i_simply_implemented_and_practice_custom_string/ 有一个 SIMD strcspn,适合*大*输入。(对于小输入可能更糟。)还有关于 code-review.SE 的半相关评论 [从字符串缓冲区中就地删除所有不需要的字符](https://codereview.stackexchange.com/posts/comments/518290 ) (2认同)

小智 12

我将首先使用适当的库函数strcspn,假设库已经被极端偏见优化:

#include <string.h>
#include <stdio.h>

int digit_exists_in(const char *s)
{
    return s[strcspn(s, "0123456789")] != '\0';
}

int main(void)
{
    printf("%d\n", digit_exists_in("foobar"));
    printf("%d\n", digit_exists_in("foobar1"));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果库尚未充分优化,那么将优化放入库中是个好主意,这样每个人都可以从中受益.(你有来源,对吧?)


Chr*_*ith 10

没有更快的算法,但您可以查看每个指令的完整寄存器的字节值或使用SIMD操作来加快速度.如果某个范围内有任何数字,或者如果SIMD操作在足够大的矢量上足够快,您可以使用掩码和零测试来查看它是否可行,您可以迭代测试特定数值的测试.字节向量比做字符比较快.

所以,例如,你可以这样做:

byte buffer[8] = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 };
uint64 *mask = (uint64 *) buffer; //this is just for clarity

if (*((uint64 *) s) & *mask) == 0)
    //You now don't need to do the < '0' test for the next 8 bytes in s
Run Code Online (Sandbox Code Playgroud)

一些优化器可能足够智能,只需从上面的代码示例中为您完成此操作.

你最好比较一个TON字节来考虑这个级别的优化.


Bri*_*ndy 7

任何算法都是O(N).

我认为isdigit已经非常有效了.