如何在C语言中编写一个函数来删除重复的字母?

3

我试图编写一个函数,从字符串中删除重复的字符。该函数只应该删除相邻的重复字符,而不是整个字符串中的所有字符。例如:'aabbaa'应该变为'aba'(而不是'ab')。由于我对C语言的指针等不太熟悉,需要一些帮助。以下是我目前的代码,但它根本不起作用,而且当我尝试返回string[]时会出现错误:

char doubleletter( char *string[] ) {
char surname[25];
int i;
for((i = 1) ; string[i] != '\0' ; i++) {

    if (string[i] == string[(i-1)]) {   //Supposed to compare the ith letter in array with one before
        string[i] = '\0' ;              //Supposed to swap duplicate chars with null
    }


}
surname[25] = string;

return surname ;
5个回答

2

举个例子,它不会修改输入字符串并返回一个新的动态分配的字符串。我认为这很容易理解:

char *new_string_without_dups(const char *input_str, size_t len)
{
    int i = 1;
    int j = 0;
    char tmpstr[len+1] = {0};

    for (; i < len; i++) {
        if (input_str[i] == input_str[i-1]) {
            continue;
        }
        tmpstr[j] = input_str[i];
        j++;
     }

     return strdup(tmpstr);
}

使用后不要忘记释放返回的字符串。

请注意,有几种方法可以适应/改进这一点。目前需要C99 std,因为在编译时无法知道数组大小。如果输入保证以\0结尾的字符串,则可以摆脱len参数等其他事情。我将把它留作练习。


2
尝试以下内容。这是一段清晰、简单且专业的代码。 :)
#include <stdio.h>

char * unique( char *s ) 
{
    for ( char *p = s, *q = s; *q++; )
    {
        if ( *p != *q ) *++p = *q;
    }

    return s;
}


int main(void) 
{
    char s[] = "aabbaa";

    puts( unique( s ) );

    return 0;
}

输出结果为:
aba

此功能还可以以以下方式重写,以避免不必要的复制。
char * unique( char *s ) 
{
    for ( char *p = s, *q = s; *q++; )
    {
        if ( *p != *q )
        {
            ( void )( ( ++p != q ) && ( *p = *q ) );
        }
    }

    return s;
}

或者

char * unique( char *s ) 
{
    for ( char *p = s, *q = s; *q++; )
    {
        if ( *p != *q && ++p != q ) *p = *q;
    }

    return s;
}

看起来最后的实现是最好的。:)


1
@nIcE cOw:'\0'字符没有特殊处理,所以像"aabbaa\0"这样的唯一字符被翻译成"aba\0" ;) - oo_miguel
1
@来自莫斯科的Vlad:我同意这确实是迄今为止最专业的,但对于C语言新手来说肯定不是最易懂的 :P - oo_miguel
@oo_miguel,关于我上次对该函数的实现,您有什么看法? - Vlad from Moscow
3
给大家提醒一下:代码看起来专业并不一定就是专业的。实际上,让代码变得专业的因素包括易读性、可移植性、错误处理和正确性等,而不是诸如使用单字符变量名、缺少括号、花括号和换行符等看起来很酷的东西。 - Jite
1
@来自莫斯科的Vlad:但是在你最后的解决方案中,对于大多数输入的大多数字符,你都有一个赋值和一个比较。(请在聊天中继续) - oo_miguel
显示剩余21条评论

2

您代码背后的想法是正确的,但是您犯了两个基本错误:

  1. 您从一个返回类型为char的函数中返回了一个char []。虽然在这种情况下char []char *会表现出相同的行为,但是char []char *char是三种不同的类型。但是,要能够返回字符串,您必须从函数中返回char *

  2. 您返回了自动分配的内存。在其他语言中,内存是引用计数的,这是可以接受的。但在C语言中,这会导致未定义的行为。您不能在函数外部使用函数内部自动分配的内存。当函数退出后,该内存被视为空,并将被重复使用,即您的值将被覆盖。您必须要么传入一个缓冲区来保存结果,要么在函数内部使用malloc()进行动态分配。哪种方法取决于您的风格。您也可以重用输入缓冲区,但是在需要保留输入的任何情况下都不建议编写该函数,而且这将使您无法将const char*传递到该函数中,即您将无法执行以下操作:

const char *str = "abbc"; ... doubleletter(str,...);

如果我要编写该函数,我可能会给它取一个这样的名字:

int doubleletter (const char *in, size_t inlen, char *out, size_t outlen){
    int i;
    int j = 0;
    if (!inlen) return 0;
    if (!outlen) return -1;
    out [j++] = in[0];
    for (i = 1; i < inlen; ++i){
        if (in[i - 1] != in[i]){
            if (j > outlen - 1) return -1;
            out[j++] = in[i];
        }
    }
    out[j] = '\0';
    return j - 1;
}

int main(void) {
    const char *str1 = "aabbaa";
    char out[25];
    int ret = doubleletter(str1, strlen(str1), out, sizeof(out)/sizeof(out[0]));
    printf("Result: %s", out);
    return 0;
}

2
首先,在for循环中删除括号(为什么一开始要放它们?)。
其次,如果在字符串中间放置\0,则该字符串将变短。
在C语言中,\0终止数组(字符串),因此如果你有:
ababaabababa
并用\0替换其中的第二个'a':
ababa\0baba
对于编译器来说,实际上相当于你把这个字符串切成了:
ababa 第三个错误可能是你在这里向函数传递了一个二维数组:char *string[]。这等效于传递char **string,而你实际上想要传递一个字符串(也就是一个指针,也就是一个数组:char *string或者char string[])。
下一件事:你在内部假设传递的字符串将少于24个字符(+\0),但你没有在任何地方进行检查。
我猜最简单的方法(虽然可能不是最聪明的方法)是在这个for循环中将传递的字符串复制到另一个字符串中,省略重复的字符。

1
我建议使用2个索引来直接修改字符串:
void remove_doubles(char *str)
{

    // if string is 1 or 0 length do nothing.
    if(strlen(str)<=1)return; 

    int i=0;   //index (new string)
    int j=1;   //index (original string)


    // loop until end of string
    while(str[j]!=0)
    {
        // as soon as we find a different letter,
        // copy it to our new string and increase the index.
        if(str[i]!=str[j])
        {
            i++;
            str[i]=str[j];
        }

        // increase index on original/old string
        j++;
    }

    // mark new end of string
    str[i+1]='\0';

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接