使用C从文件中删除相邻的重复行

时间:2017-10-21 11:44:26

标签: c duplicates lines

如果空行重复,也应删除空行。如果line有转义序列(如\t),则它与空行不同。下面的代码是删除太多行,或者有时会留下重复的行。如何解决这个问题?

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

int main()
{
    char a[6000];
    char b[6000];
    int test = 0;
    fgets(a, 6000, stdin);
    while (fgets(b, 6000, stdin) != NULL) {
        for (int i = 0; i < 6000; i++) {
            if (a[i] != b[i]) {
                test = 1;
            }
        }
        if (test == 0) {
            fgets(b, 6000, stdin);
        } else {
            printf("%s", a);
        }
        int j = 0;
        while (j < 6000) {
            a[j] = b[j];
            j++;
        }
        test = 0;
    }
    return 0;
}

4 个答案:

答案 0 :(得分:2)

你的逻辑主要是合理的。你的思路是正确的:

  1. previousa)中读取一行。
  2. 将另一行读入currentb)。
  3. 如果previouscurrent具有相同的内容,请转到步骤2.
  4. 打印previous
  5. current移至previous
  6. 转到第2步。
  7. 然而,这仍有一些问题。

    不必要的行读

    首先,请考虑以下代码:

    while(fgets(b,6000,stdin)!=NULL) {
        ...
        if(test==0) {
            fgets(b,6000,stdin);
        }
        else {
            printf("%s",a);
        }
        ...
    }
    

    如果ab具有相同的内容(test==0),则使用未经检查的fgets再次读取一行,除非您再次阅读 < / strong>评估循环条件fgets(b,6000,stdin)!=NULL时。问题是你大多忽略了刚读过的那一行,这意味着你正在将一条未知的行从b移动到a。由于循环已经读取另一行并正确检查失败,只需让循环读取该行,并反转if语句的相等性测试以打印a test!=0

    最后一行在哪里?

    您的逻辑也不会打印最后一行。考虑一行1行。你读它,然后循环条件中的fgets尝试读取另一行,因为你在文件的末尾失败了。循环外没有打印语句,因此您永远不会打印该行。

    那么有两行不同的文件呢?你读第一行,然后是最后一行,看它们是不同的,然后打印第一行。然后用最后一行覆盖第一行的缓冲区。你没有读到另一行,因为没有了,最后一行也没有打印出来。

    您可以将第一个(未经检查的)fgets替换为a[0] = 0来解决此问题。这使得a的第一个字节成为空字节,这意味着字符串的结尾。它不会与您阅读的行进行比较,因此会打印test==1,意味着a。由于a中没有要打印的字符串,因此不会打印任何内容。事情继续正常进行,b的内容被移入a,另一行被读取。

    唯一的最后一行问题

    这留下了一个问题:如果不是重复,则不会打印最后一行。要解决此问题,只需打印b而不是a

    最后的食谱

    1. 0分配给previousa[0])的第一个字节。
    2. currentb)中读取一行。
    3. 如果previouscurrent具有相同的内容,请转到步骤2.
    4. 打印current
    5. current移至previous
    6. 转到第2步。
    7. 正如您所看到的,它与您现有的逻辑没有太大区别;只有步骤1和4不同。它还确保检查所有fgets个调用。如果文件中没有行,则不打印任何内容。如果文件中只有一行,则打印出来。如果2行不同,则都打印。如果2行相同,则打印第一行。

      可选:优化

      • 您只需检查任一字符串中的第一个空字节,而不是检查所有6000个字节,因为fgets将自动添加一个以标记字符串的结尾。
      • 更快的方法是在break循环的if语句中添加for语句。如果单个字节不匹配,则整行不重复,因此如果只有字节10在两个1000字节行中不同,则可以更快地停止比较!

答案 1 :(得分:1)

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

int main(void)
{
char buff[2][6000];
unsigned count=0;
char *prev=NULL
        , *this= buff[count%2]
        ;
while( fgets(this, sizeof buff[0] , stdin)) {
        if(!prev || strcmp(prev, this) ) { // first or different
                fputs(this, stdout);
                prev=this;
                count++;
                this=buff[count%2];
                }
        }
fprintf(stderr, "Number of lines witten: %u\n", count);
return 0;
}

答案 2 :(得分:0)

您的代码中存在一些问题,例如:

    for(int i=0; i<6000; i++) {
        if(a[i]!=b[i]) {
            test=1;
        }
    }

在这个循环中,每次整个缓冲区将逐个字符进行比较,即使它找到if(a[i]!=b[i])的某个值i。可能你应该在test=1之后打破循环。

由于您没有在循环外打印行,因此您的逻辑也不适用于只有1行的文件。

另一个问题是大小为6000字符的固定长度缓冲区。

您可以使用getline来解决问题。你可以做 -

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

int main()
{
        char * line = NULL;
        char * comparewith = NULL;
        int notduplicate;
        size_t len = 0;
        ssize_t read;

        while ((read = getline(&line, &len, stdin)) != -1) {
                ((comparewith == NULL ) || (strcmp (line, comparewith) != 0)) ? (notduplicate = 1) : (notduplicate = 0);
                if (notduplicate) {
                        printf ("%s\n", line);
                        if (comparewith != NULL)
                                free(comparewith);
                        comparewith = line;
                        line = NULL;
                }
        }

        if (line)
                free (line);

        if (comparewith)
                free (comparewith);

        return 0;
}

需要注意的重点:

getline()不在C标准库中。 getline()最初是GNU扩展,standardized in POSIX.1-2008。因此,此代码可能无法移植。为了使其可移植,您需要推送自己的getline() this之类的内容。

答案 3 :(得分:0)

这是一个更简单的解决方案,对行长度没有限制:

Activity 3

代码会跳过超过2个连续换行符的序列,因此会删除重复的空行。