c - 查找字符串中字符串分隔符的数量

标签 c string char

我使用以下命令来查找字符串中的分隔符数量:

char * string = "xi--len--xia"
char * split_on = "--";

size_t len_joined_string = strlen(string);
size_t len_split_on = strlen(split_on);
size_t num_separators_in_string = 0;

for (int i=0; i < len_joined_string; i++) {
    if (joined_string_buffer[i] == split_on[0]) {
        int has_match = 1;
        for (int j=1; j < len_split_on; j++) {
            if (joined_string_buffer[i+j] != split_on[j])
                has_match = 0;
        }
        if (has_match)
            num_separators_in_string ++;
    }
}

有没有办法在内置 C 函数中执行上述操作,或者是否需要我编写上面的代码?

来自另一个问题,Counting number of occurrences of a char in a string in C ,对 char 执行此操作看起来更简单一些:

for (i=0; s[i]; s[i]=='.' ? i++ : *s++);

但是,当分割字符串(字符数组)而不是单个 char 时,是否有类似的情况?

最佳答案

刚刚完成

你可以这样做:

const char * split = "--";
int i;
for(i=0; s[i]; strncmp(&s[i], split, strlen(split)) ? *s++ : i++);

请注意,我翻转了 *s++i++因为 strncmp 在相等时返回 0。另外,您可能需要根据您想要如何处理类似 "xi---len----xia" 的字符串来修改它。 .

使其可读

如果你问我,上面的代码片段看起来有点笨拙且难以理解。如果你问我它有什么作用,我需要相当长的时间才能弄清楚。它看起来像是“看看我能做什么”。

将其放入函数

我会将其放入这样的函数中,以便为正在阅读您代码的人隐藏这个可怕的 for 循环。

size_t no_splits(const char *s, const char *split) 
{
    size_t i;
    for(i=0; s[i]; strncmp(&s[i], split, strlen(split)) ? *s++ : i++)
        ; // Put semicolon here to suppress warnings
    return i;
}

使逻辑可读

但是话又说回来,当您将代码插入到一个命名良好的函数中时,就基本上不再需要缩短代码了。因此为了可读性,我将其重写为:

size_t no_splits(const char *s, const char *split) 
{
    size_t i=0;

    // Not only more readable, but also better for performance
    const size_t len=strlen(split);

    while(s[i]) {
        if(strncmp(&s[i], split, len))
            // The only reason to use * in above code was to suppress a warning
            s++; 
        else
            i++;
    }
    return i;
}

请注意,在最后一段代码中,我删除了两个唯一目的是抑制警告的内容。我并不是说仅仅为了抑制警告而做的事情总是错误的,但是当你这样做时,这表明你应该考虑重新设计你的代码。尽管可以有不同的使用方式,但使用 for 循环的常用方法是 for(<init>; <condition>; <inc/dec>)背离这个约定通常是一件坏事。不仅是因为可读性,还因为它使优化器变得更加困难。原因是优化器可以识别常见模式并具有优化它们的规则。

将逻辑更改为更直观的内容

实际上,我也认为这在递增 s 之间交替。和i很困惑。这是一个(对我来说)更有意义的版本。将 while 循环更改为:

while(*s) {
    if(strncmp(s, split, len) == 0)
        i++;
    s++;
}

如果您确实想压缩它,请更改为:

// *s++ is back, but this time with another purpose than suppressing warnings
while(*s++) // Or for(; *s; s++) which I personally think looks better
    if(strncmp(s, split, len) == 0)
        i++;

滥用语法

下面是一个示例,说明如何真正滥用 for 循环的语法。这是我用empty for body编写的矩阵乘法:

// Do NOT do like this!!!
void matrix_multiply(int *A, int *B, int *C, int N)
{
    for(    int i=0,j=0,k=0;
            k++<N ? 1 : ++j<N ? k=1 : ++i<N ? k=1+(j=0) : 0;
            C[i*N + j] = A[i*N + k -1] * B[(k-1)*N + j] + (k==1 ? 0 : C[i*N + j])
    );
}

这是插入排序的示例:

// Do NOT do like this either!!!
void insertionSort(int *list, int length)
{
    for(int i=0, j=0, max=0; 
        ++j<length ? 1 : i<length-1 ? max=j=i+++1+ 0*
            (0*((0*(j=list[i-1])) ? 0 : ((0*(list[i-1]=list[max])) 
            ? 0 : list[max]=j))) : 0;
        list[j]>list[max] ? max=j : 0
    ); 
}

上面的代码片段是基本上将 for 循环发挥到极致的示例。

摘要

总的来说,我想说你应该有非常充分的理由来编写函数 no_splits与我使用 while 循环的更具可读性的版本相比,以另一种方式进行。性能是一个合理的理由,但首先要确保这段代码确实是瓶颈。请记住,短代码并不意味着快速代码。如果您确实想使用 for 循环,那么至少像我一样将其放入函数中,并给它一个注释来描述它的作用。但这个片段是我对您的目的的最终建议:

// Count the number of occurrences of substr in str
size_t no_splits(const char *str, const char *substr) 
{
    size_t ret = 0;
    const size_t len = strlen(substr);

    for(; *str; str++) 
        if(strncmp(str, substr, len) == 0)
            ret++;

    return ret;
}

如果你想让它更快一点,尤其是长时间 substr ,你可以这样做:

size_t no_splits(const char *str, const char *substr) 
{
    size_t ret = 0;
    const size_t len = strlen(substr);

    for(size_t n = strlen(str) - len; n >= 0; n--)
        if(strncmp(&str[n], substr, len) == 0)
            ret++;

    return ret;
}

关于c - 查找字符串中字符串分隔符的数量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58161244/

相关文章:

计数排序 - C

c++ - 共享一个平台的部分结构,同时将其他平台的部分结构屏蔽掉

c - 为什么从 getopt_long() 中省略这一行会导致段错误?

java - 为什么它返回错误的字符串?

c++ - 在 C++ 中从 uint8_t 转换为 long

c char[] to jstring printf 适用于 int 类型,但不适用于 string

c - 在 8 秒内检测 16 GB 笔式驱动器上的内容更改

ruby - 填充文本表单 - 字符串太小?

php - 如果不存在,如何将 .jpg 添加到字符串末尾

c - 使用数组和 strcat 将字符附加到字符串