-2

csv ファイルを開き、それを読み取り、新しい csv ファイルを作成し、一部の列のみを書き込む簡単なプログラムを作成しました (すべての列が必要なわけではなく、一部を削除することでファイルがより管理しやすくなることを望んでいます)。 . ファイルは1.15GBですがfopen()問題ありません。最初の progress の直後に while ループでセグメンテーション違反が発生しますprintf()

私はcsvの最初の数行だけでテストしましたが、以下のロジックは私が望むことを行います. when の奇妙なセクションは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(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)を使用してファイルを1行ずつ読み取ることができます(これも失敗する可能性があるため、テストする必要があります)。

于 2013-09-14T07:53:19.983 に答える
1

あなたはmalloc'ingしていますが、解放することはありません。おそらく、メモリが不足し、mallocs の 1 つが返さNULLれ、その後にsegfaults が呼び出された可能性がありますstr(n)cpy

free(newEntry);それぞれの呼び出しのfree(modifiedNewEntry);直後にを追加するとfwrite、メモリ不足が解決されます。

bufまた、ループ内で、ファイル全体を含むバッファーへのオフセットを計算することにも注意してください。これらのオフセットはint、システム上の最大値が処理している数値に対して小さすぎる可能性があるタイプの変数に保持されます。また、大きなints を追加すると、セグメンテーション違反の別の原因である負の値になる可能性があることにも注意してください (buf への負のオフセットにより、バッファー外のアドレスに移動する可能性があり、読み取りさえできない可能性があります)。

于 2013-09-14T07:43:33.833 に答える