-2

我写了一个简单的程序,可以打开一个 csv 文件,读取它,创建一个新的 csv 文件,然后只写一些列(我不想要所有的列,希望删除一些会使文件更易于管理) . 该文件为1.15GB,但fopen()没有问题。分段错误发生在第一个进度后不久的我的 while 循环中printf()

我只测试了 csv 的前几行,下面的逻辑就是我想要的。奇怪的部分何时index == 0是由于最后一列的形式(xxx, yyy)\n,逗号分隔值文件中的只是荒谬)。

这是代码,while循环是问题所在:

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

int main(int argc, char** argv) {
    long size;
    FILE* inF = fopen("allCrimes.csv", "rb");
    if (!inF) {
        puts("fopen() error");
        return 0;
    }
    fseek(inF, 0, SEEK_END);
    size = ftell(inF);
    rewind(inF);

    printf("In file size = %ld bytes.\n", size);
    char* buf = malloc((size+1)*sizeof(char));
    if (fread(buf, 1, size, inF) != size) {
        puts("fread() error");
        return 0;
    }
    fclose(inF);
    buf[size] = '\0';

    FILE *outF = fopen("lessColumns.csv", "w");
    if (!outF) {
        puts("fopen() error");
        return 0;
    }

    int index = 0;
    char* currComma = strchr(buf, ',');
    fwrite(buf, 1, (int)(currComma-buf), outF);

    int progress = 0;
    while (currComma != NULL) {
        index++;
        index = (index%14 == 0) ? 0 : index;
        progress++;
        if (progress%1000 == 0) printf("%d\n", progress/1000);

        int start = (int)(currComma-buf);
        currComma = strchr(currComma+1, ',');
        if (!currComma) break;
        if ((index >= 3 && index <= 10) || index == 13) continue;

        int end = (int)(currComma-buf);
        int endMinusStart = end-start;
        char* newEntry = malloc((endMinusStart+1)*sizeof(char));
        strncpy(newEntry, buf+start, endMinusStart);
        newEntry[end+1] = '\0';

        if (index == 0) {
            char* findNewLine = strchr(newEntry, '\n');
            int newLinePos = (int)(findNewLine-newEntry);
            char* modifiedNewEntry = malloc((strlen(newEntry)-newLinePos+1)*sizeof(char));
            strcpy(modifiedNewEntry, newEntry+newLinePos);
            fwrite(modifiedNewEntry, 1, strlen(modifiedNewEntry), outF);
        }
        else fwrite(newEntry, 1, end-start, outF);
    }
    fclose(outF);

    return 0;
}

编辑:原来的问题是 csv 文件,在我没有预料到的地方有导致逻辑失败的地方。我最终编写了一个新的解析器,它删除了逗号数量不正确的行。它删除了 243,875 行(约占文件的 4%)。我将发布该代码,因为它至少反映了一些关于free()

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

int main(int argc, char** argv) {
    long size;
    FILE* inF = fopen("allCrimes.csv", "rb");
    if (!inF) {
        puts("fopen() error");
        return 0;
    }
    fseek(inF, 0, SEEK_END);
    size = ftell(inF);
    rewind(inF);

    printf("In file size = %ld bytes.\n", size);
    char* buf = malloc((size+1)*sizeof(char));
    if (fread(buf, 1, size, inF) != size) {
        puts("fread() error");
        return 0;
    }
    fclose(inF);
    buf[size] = '\0';

    FILE *outF = fopen("uniformCommaCount.csv", "w");
    if (!outF) {
        puts("fopen() error");
        return 0;
    }

    int numOmitted = 0;
    int start = 0;
    while (1) {
        char* currNewLine = strchr(buf+start, '\n');
        if (!currNewLine) {
            puts("Done");
            break;
        }

        int end = (int)(currNewLine-buf);
        char* entry = malloc((end-start+2)*sizeof(char));
        strncpy(entry, buf+start, end-start+1);
        entry[end-start+1] = '\0';

        int commaCount = 0;
        char* commaPointer = entry;
        for (; *commaPointer; commaPointer++) if (*commaPointer == ',') commaCount++;

        if (commaCount == 14) fwrite(entry, 1, end-start+1, outF);
        else numOmitted++;

        free(entry);
        start = end+1;
    }
    fclose(outF);
    printf("Omitted %d lines\n", numOmitted);

    return 0;
}
4

2 回答 2

1

你在 malloc'ing 但从不释放。可能你的内存用完了,你的 malloc 之一返回NULL,以及随后对str(n)cpysegfaults 的调用。

添加free(newEntry);,free(modifiedNewEntry);在各自的fwrite调用之后立即解决您的内存短缺问题。

另请注意,在您的循环中,您计算buf​​包含整个文件的缓冲区的偏移量。这些偏移量保存在类型变量中,int其系统上的最大值对于您正在处理的数字可能太小。另请注意,添加大ints 可能会导致负值,这是段错误的另一个可能原因(到 buf 的负偏移量会将您带到缓冲区外的某个地址,甚至可能不可读)。

于 2013-09-14T07:43:33.833 回答
1

malloc(3)函数可能(有时确实)失败。

至少编写类似的代码

    char* buf = malloc(size+1);
    if (!buf) {
       fprintf(stderr, "failed to malloc %d bytes - %s\n", 
               size+1, strerror(errno));
       exit (EXIT_FAILURE);
    }

我强烈建议清除a (或以其他方式使用....)memset(buf, 0, size+1)的成功结果,不仅因为以下可能会失败(您正在测试),而且可以简化调试和重现性。同样对于其他所有调用or (您应该始终测试它们以防失败)....malloccallocfreadmalloccalloc

请注意,根据定义 sizeof(char)始终为1。因此我将其删除

正如其他人指出的那样,您有内存泄漏,因为您没有free正确调用。像valgrind这样的工具可以提供帮助。

您需要学习如何使用调试器(例如gdb)。不要忘记编译所有警告和调试信息(例如gcc -Wall -g)。并改进您的代码,直到您没有收到任何警告。

了解如何使用调试器是编程(尤其是 C 或 C++)时必不可少的技能。这种调试技能(以及使用调试器的能力)将在您贡献的每个 C 或 C++ 程序中发挥作用。

顺便说一句,您可以使用getline(3)逐行读取文件(这也可能失败,您应该对其进行测试)。

于 2013-09-14T07:53:19.983 回答