【问题标题】:In C, how do I remove identical, and consecutive lines in a char array?在 C 中,如何删除 char 数组中相同且连续的行?
【发布时间】:2018-04-28 08:14:25
【问题描述】:

我正在寻找有关创建函数的帮助。

函数 deleteIdents() 将删除 char 数组中的相同行,因为它们是连续的。它将保留相同的行之一。

我不需要检查整行是否相同。对于这种情况,只有前 79 个字符 MAXCHARS 就可以了。

所以,例如,如果我的数组包含

Hello World
Hi World
Hello World
Hello World
Hello World
Hi there

会改成

Hello World
Hi World
Hello World
Hi there

在我的脑海中,函数看起来类似于:

int deleteIdents(char *a)
{
    int i;
    for (i=0; i<=MAXCHARS; i++) {
        if (a[i] != '\n')
            /* copy into new array */
        }
    }
}

但我不确定。如果您有解决方案,我会很高兴并感谢您:)

【问题讨论】:

  • 到目前为止你尝试过什么?这看起来像是一道作业题。
  • 这是我正在开发的一个更大程序的子功能。我一直在尝试使用 while 语句,并且仍在努力。
  • 你能告诉我们这个while语句吗?

标签: c arrays char


【解决方案1】:

读第一行而不是第二行,比较它们是否相等进入循环,直到它们不相等。所以这里是代码:

char *first_line = malloc(MAXLINE);
char *second_line = malloc(MAXLINE);

getline(first_line);

do {
   getline(second_line);
} while (strcmp (first_line, second_line));

对于getline() 实现搜索,有很多示例。或者here你有我的。

【讨论】:

  • 建议char *first_line; --> char first_line[79 + 2]; 同样适用于second_line
  • 以null结尾,这就是为什么
  • 79 因为“只是前 79 个字符”。 +1 表示 null 字符,+1 表示'\n'(可能适用于getline),所以[79 + 1 /* + 1 */]
【解决方案2】:

另一个如何实现它的例子。想法是保留 2 个指针,只有在条目不同时才增加第一个指针。还分配了一些额外的存储空间,以避免已被覆盖的条目的内存泄漏。

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

int unique(char **strings, int size) {
    if (!strings) {
        return -1;
    }
    int head = 0, newHead = 0, duplicatedElementsHead = 0;
    //Save duplicates to avoid memory leaks
    char** duplicatedEntries = malloc(size*sizeof(char*));

    while (head < size) {
        //String are the same
        if (!strcmp(strings[head], strings[newHead])) {
            if (head != newHead) {
                duplicatedEntries[duplicatedElementsHead++] = strings[newHead];
            }
            ++head;
        } else {
            strings[++newHead] = strings[head++];
        }
    }

    //Put duplicated entries after new end
    int idx = 0, tmpHead = newHead + 1;
    for (; idx < duplicatedElementsHead; ++idx) {
        strings[tmpHead++] = duplicatedEntries[idx];
    }

    free(duplicatedEntries);
    return newHead;
}

int main() {
    char **strings = malloc(8*sizeof(char*));
    strings[0] = "Hello World";
    strings[1] = "Hi World";
    strings[2] = "Hi World";
    strings[3] = "Hello World";
    strings[4] = "Hello World";
    strings[5] = "Hi there";
    strings[6] = "Hia";
    strings[7] = "Hi";
    int newEnd = unique(strings, 8);
    for (int i=0; i < newEnd; ++i) {
        printf("%s\n", strings[i]);
    }
    free(strings);
}

【讨论】:

    【解决方案3】:

    您实际上是在编写 unix/linux 实用程序“uniq”的核心功能。

    cat filename | sort | uniq > newfile
    #or skip sort, since you didn't mention
    cat filename | uniq > newfile
    

    你可以只使用 popen 和 uniq (类似这样的......)

    FILE *uniqfh;
    uniqfh = popen("cat file1 | uniq" , "r");
    if (uniqfh == NULL) { //handle error
    }
    while( fgets(uniqfh, buffer, buffersize) ) printf("%s\n",buffer);
    

    但是说真的,你可以写 uniq() 的核心,

    static long MAXUNIQ=79; //or whatever you want
    char*
    isdup(char* prev, char* next, long len)
    {
        //if( !prev || !next) error
        long n = len<=0 ? MAXUNIQ : len;
        for(  ; *prev==*next && n --> 0; ) { //down-to operator (sic)
            ; //clearly nothing happening here!
        }
        return( (n<1) || !(*p+*n) );
    }
    /yeah, this is actually strncmp, but hey
    

    您需要一个“字符串”数组(char* 或 char[]),让我们阅读它们,

    char* ray[ARRAYMAX]; //define how many elements of your arRay
    //could use, char** ray; and malloc(ARRAYMAX*sizeof(char*))
    
    long
    read_array(FILE* fh, char* ray[])
    {
        char buffer[MAXLINE+1];
        long count=0;
        while( fgets(buffer,sizeof(buffer),fh) ) {
            //you could eat dups here, or in separate function below
            //if( (count<1) && !isdup(ray[count-1],buffer,MAXUNIQ) )
            ray[count++] = strdup(buffer);
        }
        //ray[0] through ray[count-1] contain char*
        //count contains number of strings read
        return count;
    }
    long
    deleteIdents(long raysize, char* ray[]) //de-duplicate
    {
        long kept, ndx;
        for( ndx=1, kept=0; ndx<raysize; ++ndx ) {
            if( !isdup(ray[kept],ray[ndx]) ) {
                ray[kept++] = ray[ndx];
            }
            else {
                free(ray[ndx]);
                ray[ndx] = NULL; //not entirely necessary, 
            }
        }
        return kept; //new ray size
    }
    

    你需要这个来调用它...

    ...
    long raysize;
    char* ray[ARRAYMAX] = {0}; //init to null pointers
    raysize = read_array(fopen(filename,"r"),ray);
    raysize = deleteIndents(raysize,ray);
    ...
    

    稍后,您将需要释放 malloc 的字符串,

    for( ; 0 <-- raysize; ) { free(ray[raysize]);  ray[raysize] = NULL; }
    

    【讨论】:

    • 是的,raysize/count/kept/ndx 应该是 size_t,但你真的要在内存中保留超过 20 亿个字符串的数组吗?好的,服务器已经有 256-512GB 内存,所以使用 size_t。
    • 您在读取文件时不检查阵列容量......您可能会泄漏它。
    • 问题是消除数组中相邻的重复项。你读了吗? 一个数组。 PO 没有提及文件……您是否沉迷于重新制作 uniq(1) 工具?
    • 另外,如果你想要uniq(1) 的功能,你不需要更多的空间来读取行:你要比较的那些。如果它们相等,则什么也不做,而是读取另一行,如果它们不同,则输出两行,丢弃第一行,将第二行复制到第一行,然后再读取一行。当您完成并且无法再读取任何行时,您输出您拥有的行的内容,您就完成了。
    • OP 没有将数组传递给他的函数,我想说明如何填充和读取字符串数组以使其受益。不,我不会使用数组来实现 uniq 。答案使用了他的函数,并修复了函数签名。
    【解决方案4】:

    以下程序对字符串元素数组执行您需要的操作。我们使用两个指针导航数组,初始化为第一个和第二个元素。我们运行array_n - 1 比较一个元素与下一个元素的循环,比较两个字符串...如果它们不同,我们将*source_ptr 字符串指针复制到*target_ptr 位置。如果它们不同,我们只增加source_ptr,因此它指向下一个数组字符串,但是不复制它(这使我们有效地删除了指针)我们也在管理新数组(我们使用了相同的数组作为源和目标,因为我们只能删除数组元素,所以每次我们在两个指针之间都有一个更大的洞)

    pru.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* array of strings */    
    char *array[] = {
        "Hello World",
        "Hi World",
        "Hello World",
        "Hello World",
        "Hello World",
        "Hi there",
    };
    
    size_t array_n = sizeof array / sizeof *array;
    
    int main()
    {
        int i;
    
        char **target_ptr = array, **source_ptr = array + 1;
        size_t new_length = 1;
    
        for (i = 1; i < array_n; i++) {
            /* if strings pointed to by pointers are equal */
            if (strcmp(*target_ptr, *source_ptr) == 0) {
                /* go to the next, effectively discarding the second pointer */
                source_ptr++;
            } else {
                /* copy both pointers in place, to the destination array */
                *target_ptr++ = *source_ptr++;
                new_length++; /* increment array length */
            }
        }
        /* finally, we have in the array only the good pointers */
    
        /* print'em */
        for (i = 0; i < new_length; i++)
            printf("%s\n", array[i]);
    
        exit(0);
    }
    

    仅此而已。

    样品运行:

    $ pru
    Hi World
    Hello World
    Hi there
    Hello World
    $ _
    

    【讨论】:

      猜你喜欢
      • 2018-10-06
      • 2018-04-07
      • 2022-01-12
      • 2019-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-16
      • 2013-03-04
      相关资源
      最近更新 更多