【问题标题】:Removing consecutive repeated characters from string using C使用C从字符串中删除连续重复的字符
【发布时间】:2018-10-21 02:22:17
【问题描述】:

我正在尝试从给定字符串中删除连续重复的字符。

例子:

bssdffFdcrrrtttii ***#

输出应该是:

bsdfFdcrti *#

这段代码不起作用,只打印第一个字符(b),我想了解我的错误。 当我进行 printf 测试时,它可以工作,但不适用于空格。 我认为问题可能出在新的 char 数组上。

void Ex6() {
    char* string[80];
    scanf("%s", &string);
    puts(removeDup(string));
}

char* removeDup(char *string) {
    int i, c = 0;
    char* newString[80];
    for (i = 0; i < strlen(string); i++) {
        if (string[i] != string[i + 1]) {
            newString[c++] = string[i];
        }
    }
    return newString;
}

【问题讨论】:

  • 您认为char* string[80];char string[80]; 之间有什么区别? “它有效,但不适用于空格” --> 将 char* string[80]; scanf("%s", &amp;string); 更改为 char string[80]; fgets(string, sizeof string, stdin);启用所有编译器警告 以节省时间。报告您不清楚的警告。
  • char* xchar* x[n]char x[n] 之间存在巨大差异,因此在指定类型时请非常小心

标签: c arrays char


【解决方案1】:

你的程序有几个问题:

  • newString 的声明应为 char newString[80],即字符数组而不是字符指针数组,Ex6 中的声明也是如此。
  • scanf 的调用应该是scanf("%s", string),因为string 已经是一个字符数组的地址,但是...
  • 使用fgets 从用户那里读取一个字符串,以确保您读取空格(如果它很重要),并且不会超出缓冲区。
  • newString 在堆栈上分配,因此不应返回给调用者。最好做一个char *newString = strdup(string),或者稍微不那么草率的char *newString = malloc(strlen(string)+1),它将调用malloc 以获得足以容纳原始字符串的内存块,因此没有重复的版本——cmets 正确地指出出这可以优化。原则上,调用者,即Ex6,必须free返回指针以避免内存泄漏,但在如此短的程序中几乎无关紧要。
  • 结果需要一个空终止符:newString[c] = '\0'

否则,removeDup 函数似乎可以正常工作。

所以,把所有这些放在一起:

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

char* removeDup(const char *string)
{
    size_t i, c = 0;
    size_t string_len = strlen(string);
    char *newString = malloc(string_len + 1);

    for (i = 0; i < string_len; i++) {
        if (string[i] != string[i + 1]) {
            newString[c++] = string[i];
        }
    }
    newString[c] = '\0';

    return newString;
}

#define MAX_STRING_LEN 80

void Ex6() {
    char string[MAX_STRING_LEN];
    char* result;

    if (fgets(string, MAX_STRING_LEN, stdin) != NULL) {
        result = removeDup(string);

        printf("%s", result);
        free(result);
    }
}

最后,我同意@tadman 的评论。由于无论如何都要遍历输入字符串来计算长度,我们不妨优化一下结果字符串的大小:

char* removeDup(const char *string)
{
    size_t i, c = 0;
    char *newString;

    for (i = 0; string[i] != '\0'; i++)
        c += (string[i] != string[i + 1]);

    newString = malloc(c + 1);

    for (i = c = 0; string[i] != '\0'; i++) {
        if (string[i] != string[i + 1]) {
            newString[c++] = string[i];
        }
    }
    newString[c] = '\0';

    return newString;
}

【讨论】:

  • 在更改newString 的声明并使用-Wno-return-stack-address 编译后,我没有收到段错误(即使这不好),但我没有使用Ex6
  • 您可以执行一次计算来计算您需要多少个字母,而另一次则分配一个大小合适的缓冲区。
  • size_t string_len 是一个很好的改进。为了一致性(和正确性),ic 应该与 string_len 是同一类型的 size_t
  • 您应该测试fgets() 的返回值以避免在空文件上出现未定义的行为。您还可以const 将参数限定为removeDupchar *removeDup(const char *string)。还包括&lt;stdio.h&gt;&lt;stdlib.h&gt;&lt;string.h&gt;
  • @tadman:你说得对。我已采纳您的建议。
【解决方案2】:

您的程序中有很多问题。它甚至不会编译,更不用说运行了。此外,最成问题的问题是,您正在从一个函数返回一个指向局部变量的指针,该函数在完成时停止其作用域。您的程序的简化版本如下:

void Ex6() 
{
   char string[80];
    scanf("%s", string);
        int i, c = 0;
    char newString[80];
    for (i = 0; i < strlen(string); i++) {
        if (string[i] != string[i + 1]) {
            newString[c++] = string[i];
        }
    }
    newString[c] = '\0';
    puts(newString);
}

【讨论】:

  • scanf("%s", string); 保留了 OP 的“它有效但不适用于空格”的问题
  • @chux,感谢您的反馈。我已经更正了。
【解决方案3】:

您可以通过修改现有字符串来使用 O(n) 时间和 O(1) 空间:

#include <stdio.h>

char* removeDup(char* input) {
        char* newTail = input, *oldTail = input;
        while (*oldTail) {
            if (*newTail == *oldTail) {
                ++oldTail;
            } else {
                *++newTail = *oldTail++;
            }
        }
    return newTail;
}

int main() {
   char string[] = "bssdffFdcrrrtttii ***#";
   char* newEnd = removeDup(string);
   char* tmp = string;
   while (tmp != newEnd) {
       printf("%c", *tmp++);
   }
   //Print the last char if string had any duplicates
   if(*tmp) {
       printf("%c", *tmp++);
   }
   return 0;
}

【讨论】:

  • 我同意这个解决方案,但这取决于预期的界面。此外,您的解决方案比 C 更 C++,添加空终止符可能会更好,这样就不需要某种 substr。
  • 感谢您指出这一点,不知何故我错过了我们处理的是 C 而不是 C++
  • 这是一个很好的解决方案,我可能会返回 newTail 的第一个字符而不是最后一个字符,但仍然非常好。哦,也可以在最后一个非重复字符中添加一个 \0 。不过,很好的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-02
  • 2017-04-03
  • 2015-01-26
  • 2018-08-05
  • 2021-12-17
  • 1970-01-01
  • 2019-11-29
相关资源
最近更新 更多