Trim 堆栈中字符串中的字符?

Trim Characters off a String on the Stack?

我需要一个C函数,它接受一个字符串(分配在栈上,而不是堆上),前后可能有相同的字符;我想 trim 关闭那些字符。我可能不知道有多少 header/footer 个字符,也不一定总是知道冒犯性字符是什么。

换句话说,如果我的原始字符串是:

xxxThis is a string.xxxx

……那我要……

This is a string.

回来了。理想情况下,我喜欢这样的解决方案:

char str1[50] = “xxxThis is a string.xxxx”;
str1 = trimString( str1 );
printf(“Returned string is:: %s\n”, str1);       // prints “This is a string.”

这是我的代码:

char* trimString( char* str1, char x ){
        int i = 0;
        int j = (int)strlen( str1 ) - 1;
        printf("string is :: >>%s<<\n", str1);
        while( str1[i] == x ){
                i++;
        }
        while( str1[j] == x ){
                j--;
        }

        str1 = strncpy( str1, str1+i, (j-i) );

        return str1;
}


int main(){

        char str1[50] = "xxxThis is a string.xxxx";
        str1[25] = '[=14=]';
        printf("%s\n", trimString( str1, 'x' ) );
        printf("END OF PROGRAM.\n");
        return 0;
}

这是输出:

This is a stringing.xxxx
END OF PROGRAM.

有两个明显的问题。首先,我认为我正确地去掉了 'x' 字符,但是当我将 trimmed 字符串复制回“str1”变量时,我只用新字符串覆盖了旧字符串的前 n 个字符细绳。原始字符串的剩余部分仍然存在。

更严重的是,我离这样调用这个函数的目标还很远:

str1 = trimString( str1, ‘x’ );

我可以这样调用我的函数:

char tmpStr[50] = trimString( str1, ‘x’ );
memcpy( str1, tmpStr );

但这很痛苦,现在我不得不担心临时字符串的大小。另外,我认为我必须调用 trimString() 很多很多次,所以如果我可以用一行代码就可以了,而不必担心管理临时字符串等,那就太好了。

有什么建议或建议吗?

您需要在移位后的字符串末尾添加一个空终止符。

您还需要使用 memmove() 而不是 strncpy(),因为后者不允许在重叠字符串之间进行复制。

并且您复制的长度有 off-by-one 错误。

#include <stdio.h>
#include <memory.h>

char* trimString( char* str1, char x ){
    int i = 0;
    int j = (int)strlen( str1 ) - 1;
    printf("string is :: >>%s<<\n", str1);
    while( str1[i] == x ){
        i++;
    }
    while( str1[j] == x ){
        j--;
    }

    memmove( str1, str1+i, (j-i+1) );
    str1[j - i + 1] = '[=10=]'; // add null terminator

    return str1;
}

int main(){

    char str1[50] = "xxxThis is a string.xxxx";
    str1[25] = '[=10=]';
    printf("%s\n", trimString( str1, 'x' ) );
    printf("END OF PROGRAM.\n");
    return 0;
}

你做不到

str1 = trimString(...);

因为你不能给数组赋值。你所能做的就是修改它们的内容。

由于函数修改了给定的数组,因此您不需要赋值。你可以写:

trimString(str1, 'x');
printf("%s\n", str1);

给你。

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

char * trimString( char *s, char c )
{
    size_t i = 0;
    
    while ( s[i] == c ) i++;
    
    size_t n = strlen( s + i );
    
    while ( n && s[n + i - 1] == c ) --n;
    
    s[n + i] = '[=10=]';
    
    if ( i != 0 )
    {
        memmove( s, s + i, n + 1 );
    }
    
    return s;
}

int main(void) 
{
    char s[50] = "xxxThis is a string.xxxx";
    
    puts( s );
    puts( trimString( s, 'x' ) );
    
    return 0;
}

程序输出为

xxxThis is a string.xxxx
This is a string.

至于您的函数实现,它可以在例如用户传递空字符串时调用未定义的行为。在这种情况下变量 j 的值可以是负数

    int j = (int)strlen( str1 ) - 1;
    

并使用这个负值,该函数将访问字符数组之外的内存。

    while( str1[j] == x ){
            j--;
    }

而且没有检查 j 的当前值是否等于或小于 0。

另外你不能使用函数strncpy

str1 = strncpy( str1, str1+i, (j-i) );

并且在任何情况下,此调用都忘记复制终止零。

注意只有在i不等于0的情况下才需要在字符数组中移动字符串,否则适当设置终止零即可。

为了使函数更安全,您可以在函数内部检查传递的字符(第二个参数)是否等于终止零。

例如

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

char * trimString( char *s, char c )
{
    if ( c != '[=15=]' )
    {
        size_t i = 0;
    
        while ( s[i] == c ) i++;
    
        size_t n = strlen( s + i );
    
        while ( n && s[n + i - 1] == c ) --n;
    
        s[n + i] = '[=15=]';
    
        if ( i != 0 )
        {
            memmove( s, s + i, n + 1 );
        }
    }
    
    return s;
}

int main(void) 
{
    char s[50] = "xxxThis is a string.xxxx";
    
    puts( s );
    puts( trimString( s, 'x' ) );
    
    return 0;
}