3

CでテキストファイルからCSVを読み込もうとしています。テキストファイルの形式は

1,Bob,bob@gmail.com
2,Daniel,daniel@gmail.com
3,John,john@gmail.com

プログラムを実行すると、番号は正常に表示されますが、名前と電子メールがゴミとして表示されます。これが私のプログラムです...

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

typedef struct {
    int number;
    char* name;
    char* email;
} Owner;

Owner owners[100];

int load(char* filename)
{
    char buffer[200];
    char token[50];
    Owner* owner;
    int owners_size = 0;
    FILE* file = fopen(filename, "r");

    while(fgets(buffer, 200, file) != NULL)
    {
        owner = (Owner*)malloc(sizeof(Owner));
        owner->number = atoi(strtok(buffer, ","));
        owner->name = strtok(NULL, ",");
        owner->email = strtok(NULL, ",");
        owners[owners_size++] = *owner;
    }

    fclose(file);
    return owners_size;
}

int main()
{
    int choise, owners_size, index;
    char* owners_filename = "owners2.txt";

    owners_size = load(owners_filename);

    if(owners_size)
    {
        printf("owners size: %d\n\n", owners_size);

        for(index = 0; index < owners_size; index++)
            printf("%d, %s %s\n", owners[index].number, owners[index].name, owners[index].email);
    }
}

だれか理由を教えてください。私はあなたの助けに感謝します。

4

4 に答える 4

5

2 つの問題:

  1. 構造内の文字列にスペースを割り当てませんでした:

    typedef struct
    {
        int   number;
        char *name;
        char *email;
    } Owner;
    

    これらのポインターが名前を保持するために指すスペースを提供する必要があります。

  2. 入力の各行で再利用されるバッファーへのポインターを提供し続けます。

    while(fgets(buffer, 200, file) != NULL)
    {
        owner = (Owner*)malloc(sizeof(Owner));
        owner->number = atoi(strtok(buffer, ","));
        owner->name = strtok(NULL, ",");
        owner->email = strtok(NULL, ",");
        owners[owners_size++] = *owner;
    }
    

    最初の行は、いくつかのポインターとしてバッファーに格納されます。次に、次の行がバッファを上書きし、元の入力全体を踏みにじって、行を再びチョップします。

使用を検討してくださいstrdup()

while (fgets(buffer, 200, file) != NULL)
{
    owner = (Owner *)malloc(sizeof(Owner));
    owner->number = atoi(strtok(buffer, ","));
    owner->name = strdup(strtok(NULL, ","));
    owner->email = strdup(strtok(NULL, ","));
    owners[owners_size++] = *owner;
}

これは少し危険なコードです (実稼働コードでは使用しません)。これはstrtok()、予期したときにトークンが見つかったかどうか (またはstrdup()成功したかどうか) をチェックしないためです。strtok()ここでも、本番コードでは使用しません。POSIXstrtok_r()または Microsoftstrtok_s()が利用可能であればそれらを使用するか、おそらく and を使用する代替手法を使用strspn()strcspn()ます。が利用できない場合strdup()は、同じ名前または別の名前で独自に作成できます。

char *strdup(const char *str)
{
    size_t len = strlen(str) + 1;
    char *dup = malloc(len);
    if (dup != 0)
        memmove(dup, str, len);  // Or memcpy() - that is safe in this context
    return(dup);
}

コードが単純な CSV ファイルにのみ適していることに気付くかもしれません。次のような行 (正当な CSV) に遭遇した場合、問題が発生します (値に引用符が含まれており、引用符で囲まれた文字列内のコンマが原因で誤って分割されます)。

1,"Bob ""The King"" King","Bob King, Itinerant Programmer <bob@gmail.com>"
于 2012-10-22T21:20:03.033 に答える
3

によって返されるポインターは、strtok()解析しているバッファー内のアドレス (この場合はローカル変数) を指しますbuffer。が変数を返すときload()、それは範囲外です (すべてのインスタンスがowners同じアドレスを指しているわけではない場合でも)。によって返された文字列をコピーする必要がありますstrtok()。利用可能な場合は使用するstrdup()か、malloc()と を使用できますstrcpy()

malloc()それらの配列が既に存在するため、の新しいインスタンスを作成する必要はありませんOwner(現状のコードにはメモリ リークがあります)。

owners配列の境界を超えることに対する保護はないことに注意してください。ファイルに複数の100エントリがある場合、ループは配列の境界を超えます。whileこれを防ぐために、 の終了条件を拡張します。

while(owners_size < sizeof(owners) / sizeof(owners[0]) &&
      fgets(buffer, 200, file) != NULL)
{
}
于 2012-10-22T21:19:03.403 に答える
1

ポインターをローカル バッファーに格納しました。あなたが去るとload()、これbufferはなくなり、もうアクセスできなくなります。

メモリを構造体にコピーする前にname、メモリを割り当てる必要があります。emailOwner

char *tok;
tok = strtok(NULL, ",");
len = strlen(tok);
owner->name = malloc(len + 1);
strcpy(owner->name, tok);
...

[編集:終了文字 len+1のためのスペースを確保するために、バイトを割り当てる必要があります。【ザック】NUL

于 2012-10-22T21:18:21.460 に答える
1

ライン バッファは 1 つしかありません。ループのすべてのサイクルでload、前のサイクルのテキストが上書きされます。さらに悪いことに、バッファはload返されるときに破棄されます。

手っ取り早いのは変えること

owner->name = strtok(NULL, ",");
owner->email = strtok(NULL, ",");

owner->name = strdup(strtok(NULL, ","));
owner->email = strdup(strtok(NULL, ","));

(お持ちでない方はstrdup本物のコンピューターを入手する書き方はとても簡単です。)

ただし、コードをレビューしているとしたら、固定サイズのライン バッファー、固定サイズの所有者配列、メモリ リーク、atoi代わりにを使用すること、 を代わりにstrtol使用すること、引用符の処理と解析エラーの回復がないことを指摘するでしょう。 、および各行を単位として割り当ててから、ポインターをそこに保存する方が効率的であることを指摘します。strtokstrsep

于 2012-10-22T21:21:51.703 に答える