C删除字符串中的字符

C deleting char in string

现在我正在使用这段代码删除字符串中的一些字符。

void eliminate(char *str, char ch){
    for(; *str != '[=10=]';str++){
        if(*str == ch){
            strcpy(str, str+1);
            str--;
        }
    }
}

在 char *str 中有一些字符串,例如

"sll , , 16"

删除“$”后,字符串如下所示

"sll 6, 5, 16"

但是删除“,”后,字符串变得很奇怪

"sll 6 5 6"

上面的代码有没有问题?而且,它只发生在 Linux 和在线 GDB 中。我的 window 笔记本电脑中的 VS 代码可以很好地消除目标字符。

正如评论中所指出的,strcpy() 在处理具有重叠内存块的数据时是不安全的。 memmove(dst, src, len) 是在 srcdst 内存重叠的情况下使用辅助缓冲区的替代方法。

您可以简单地跳过要循环消除的字符:

#include <stdio.h>

void drop_char (char *str, char ch) {
    if (!str) return;

    for(char* cp = str; 1 ; ) {
        if(*cp != ch)
            *str++ = *cp;
        if ('[=10=]' == *cp++)
            break;
    }
}

int main () {
    char str [] = "sll     , , 16";
    printf ("Original   : [%s]", str);

    drop_char(str, '$');
    printf ("\nDropping $ : [%s]", str);

    drop_char(str, ',');
    printf ("\nDropping , : [%s]", str);

    printf ("\n");

    return 0;
}

在不使用显式指针数学的情况下,我们可以在迭代输入字符串时使用两个索引。 i 是每次迭代都会递增的索引,而 j 仅在 not 找到目标字符时递增。

void drop_char(char *str, char ch) {
    size_t i = 0, j = 0;    

    for (; str[i]; ++i) {
        if (str[i] != ch) 
            str[j++] = str[i];
    }

    str[j] = '[=10=]';
}

如果我们有字符串 char test[] = "hello" 并调用 drop_char(test, 'l') 过程如下:

i = 0, j = 0
char: 'h'
'h' != 'l'
test[0] = 'h'

i = 1, j = 1
char: 'e'
'e' != 'l'
test[1] = 'e'

i = 2, j = 2
char: 'l'
'l' == 'l'

i = 3, j = 2
char: 'l'
'l' == 'l'

i = 4, j = 2
char: 'o'
'o' != 'l'
test[2] = 'o'

i = 5, j = 3
test[3] = '[=11=]'

对于使用函数 strcpy 重叠字符串的初学者,如本声明

strcpy(str, str+1);

调用未定义的行为。

来自C标准(7.23.2.3 strcpy函数)

2 The strcpy function copies the string pointed to by s2 (including the terminating null character) into the array pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined.

此声明

str--;

当删除的字符是字符串的第一个字符时,也可以调用未定义的行为。

关于函数声明的一些说明。

这样的函数应该遵循 C 标准字符串函数的一般约定和 return 指向结果字符串的指针。

另一个通用约定是此类函数不检查传递的指针是否等于NULL。函数的用户有责任将有效的指针传递给字符串。

此外,如果用户将 '[=16=]' 作为要删除的字符传递,则该函数将不对源字符串执行任何操作,而只是 return 它。

这是一个演示程序,展示了如何声明和定义函数。

#include <stdio.h>

char * eliminate( char *s, char c )
{
    if ( c != '[=12=]' )
    {
        char *src = s, *dsn = s;

        do
        {
            if ( *src != c )
            {
                if ( dsn != src ) *dsn = *src;
                ++dsn;
            }
        } while ( *src++ );
    }

    return s;
}

int main( void )
{
    char s[] = "sll     , , 16";

    printf( "\"%s\"\n", eliminate( s, '$') );
}

程序输出为

"sll     6, 5, 16"