我使用以下命令来查找字符串中的分隔符数量:
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/