首页 > 解决方案 > 从堆栈上的字符串中修剪字符?

问题描述

我需要一个 C 函数,它接受一个字符串(分配在堆栈上,而不是在堆上),它的正面和背面可能具有相同的字符;我想剪掉那些字符。我可能不知道有多少页眉/页脚字符,也不一定总是知道有问题的字符是什么。

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

xxxThis is a string.xxxx

……那我想要……</p>

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] = '\0';
        printf("%s\n", trimString( str1, 'x' ) );
        printf("END OF PROGRAM.\n");
        return 0;
}

这是输出:

This is a stringing.xxxx
END OF PROGRAM.

有两个明显的问题。首先,我认为我正确地删除了“x”字符,但是当我将修剪后的字符串复制回“str1”变量时,我只用新字符串覆盖了旧字符串的前 n 个字符。原弦的残骸还在。

更严重的是,我并不接近像这样调用这个函数的目标:

str1 = trimString( str1, ‘x’ );

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

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

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

有什么建议或建议吗?

标签: cchartrimc-stringsfunction-definition

解决方案


这个给你。

#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] = '\0';
    
    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 != '\0' )
    {
        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] = '\0';
    
        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;
}

推荐阅读