在 c 中提取 "valid" 子字符串的快速方法

quick way to extract "valid" substring in c

我写了两个函数来从一个字符串中提取一个“有效”的子串,这意味着该子串只能包含字母和数字。但是,如果要检查的字符串很长,即使性能差距不太明显,它们也会开始失去性能,我对此并不太兴奋。有没有更快的方法来“验证”字符串?这是我的代码:

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <wchar.h>

wchar_t* validstr(wchar_t* src, wchar_t* cc, unsigned int* index) {
    wchar_t* valid = calloc(1, sizeof(wchar_t));
    while (isalpha(*cc) || isdigit(*cc)) {
        valid = realloc(valid, (wcslen(valid) + 2) * sizeof(wchar_t));
        wcscat(valid, (wchar_t[]) { *cc, 0 });
        ++* index;
        *cc = src[*index];
    }
    return valid;
}

wchar_t* validstr2(wchar_t* src) {
    wchar_t* valid = calloc(1, sizeof(wchar_t));
    while (isalpha(*src) || isdigit(*src)) {
        valid = realloc(valid, (wcslen(valid) + 2) * sizeof(wchar_t));
        wcscat(valid, (wchar_t[]) { *src, 0 });
        src++;
    }
    return valid;
}

int main() {
    wchar_t* str = L"valid10+(notvalidanymore";

    // usage for validstr()
    wchar_t current = str[0];
    unsigned int index = 0;
    printf("%ls\n", validstr(str, &current, &index));

    // usage for validstr2()
    printf("%ls\n", validstr2(str));
}

您应尽可能避免重新分配。

所以您可以先数一下与您相关的字符:

size_t length = 0
for(wchar_t* tmp = cc; *tmp && iswalnum(*tmp); ++tmp)
{
    ++length
}

请注意,isalnum 函数涵盖字母和数字 – 但无论如何您都应该使用宽字符函数(isw[...],请注意附加的 w)。

计算完后,您只需复制感兴趣的值即可:

wchar_t* valid = malloc((length + 1) * sizeof(wchar_t));
//                                ^ terminating null character!

memcpy(valid, cc, length*sizeof(wchar_t));
valid[length] = 0;

// for validstr:
memcpy(cc, src, length*sizeof(wchar_t));
*index = length;

相比calloc,我更喜欢这里的malloc,因为不需要对数组进行零初始化,无论如何它都会被覆盖。

请注意,validstr 的上述版本与您的实现略有不同: 先递增,然后复制字符,这会跳过 [=19 的第一个字符=].我的版本从第一个字符开始复制。如果跳过第一个字符实际上是有意的,那么从第一个字符开始复制你就可以了:

memcpy(cc, src + 1, length);

请注意,这个版本 (validstr(wchar_t*, wchar_t*, unsigned int*)) 无论如何对我来说都不是很安全,您可能很容易读取超出 src 范围的内容,这将导致未定义的行为——除非您始终以任何方式保证src 至少与复制的子字符串一样长。