web-dev-qa-db-ja.com

Cで1行ずつテキストファイルを調べる

私はCISクラスの小さな演習に取り組んできましたが、Cがファイルから読み取るために使用する方法に非常に混乱しています。本当に必要なのは、ファイルを1行ずつ読み取り、各行から収集した情報を使用していくつかの操作を行うことです。私はgetl​​ineメソッドなどを運が悪かったので試してみました。私のコードは現在次のとおりです。

int main(char *argc, char* argv[]){
      const char *filename = argv[0];
      FILE *file = fopen(filename, "r");
      char *line = NULL;

      while(!feof(file)){
        sscanf(line, filename, "%s");
        printf("%s\n", line);
      }
    return 1;
}

現在、sscanfメソッドでsegフォールトが発生していますが、その理由はわかりません。私は完全なC初心者であり、私が見逃していた大きな画像があるかどうか疑問に思っています。ありがとう

53
Dan Bradbury

非常に少数の行で非常に多くの問題。私はおそらくいくつかを忘れます:

  • argv [0]はプログラム名であり、最初の引数ではありません。
  • 変数を読み取りたい場合は、そのメモリを割り当てる必要があります
  • feofでループすることはありません。失敗するまでIO関数でループし、feofは失敗の理由を特定するのに役立ちます。
  • sscanfは行を解析するためにあります。ファイルを解析する場合は、fscanfを使用します。
  • 「%s」は?scanfファミリの形式として最初のスペースで停止します
  • 行を読み取るための標準関数はfgetsです。
  • メインから1を返すことは失敗を意味します

そう

#include <stdio.h>

int main(int argc, char* argv[])
{
    char const* const fileName = argv[1]; /* should check that argc > 1 */
    FILE* file = fopen(fileName, "r"); /* should check the result */
    char line[256];

    while (fgets(line, sizeof(line), file)) {
        /* note that fgets don't strip the terminating \n, checking its
           presence would allow to handle lines longer that sizeof(line) */
        printf("%s", line); 
    }
    /* may check feof here to make a difference between eof and io failure -- network
       timeout for instance */

    fclose(file);

    return 0;
}
120
AProgrammer

ファイルから行を読み取るには、fgets関数を使用する必要があります。指定されたファイルから、改行文字またはEOFまでの文字列を読み取ります。

sscanfを定数文字列リテラル%sに読み込むためのフォーマット文字列としてfilenameを使用するため、コードでlineを使用してもまったく機能しません。

SEGVの理由は、lineが指す未割り当てメモリに書き込むためです。

7
Abrixas2

\t改行ではなく、\nタブなどの他の区切り文字を扱っているとします。

区切り文字へのより一般的なアプローチは、 getc() の使用です。これは、一度に1文字を取得します。

getc()intを返すため、EOFとの等価性をテストできます。

次に、最大line[BUFFER_MAX_LENGTH]文字をスタックに格納するために、タイプcharの配列BUFFER_MAX_LENGTH-1を定義します(最後の文字を\0ターミネータ文字として保存する必要があります)。

配列を使用すると、mallocおよびfreeを使用してヒープ上に適切な長さの文字ポインターを作成する必要がなくなります。

#define BUFFER_MAX_LENGTH 1024

int main(int argc, char* argv[])
{
    FILE *file = NULL;
    char line[BUFFER_MAX_LENGTH];
    int tempChar;
    unsigned int tempCharIdx = 0U;

    if (argc == 2)
         file = fopen(argv[1], "r");
    else {
         fprintf(stderr, "error: wrong number of arguments\n"
                         "usage: %s textfile\n", argv[0]);
         return EXIT_FAILURE;
    }

    if (!file) {
         fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
         return EXIT_FAILURE;
    }

    /* get a character from the file pointer */
    while(tempChar = fgetc(file))
    {
        /* avoid buffer overflow error */
        if (tempCharIdx == BUFFER_MAX_LENGTH) {
            fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
            return EXIT_FAILURE;
        }

        /* test character value */
        if (tempChar == EOF) {
            line[tempCharIdx] = '\0';
            fprintf(stdout, "%s\n", line);
            break;
        }
        else if (tempChar == '\n') {
            line[tempCharIdx] = '\0';
            tempCharIdx = 0U;
            fprintf(stdout, "%s\n", line);
            continue;
        }
        else
            line[tempCharIdx++] = (char)tempChar;
    }

    return EXIT_SUCCESS;
}

char *を使用する必要がある場合でも、このコードを使用できますが、行の入力に満たされると、line[]配列をstrdup()使用できます。 freeこの重複した文字列を使い終わったら、メモリリークが発生します。

#define BUFFER_MAX_LENGTH 1024

int main(int argc, char* argv[])
{
    FILE *file = NULL;
    char line[BUFFER_MAX_LENGTH];
    int tempChar;
    unsigned int tempCharIdx = 0U;
    char *dynamicLine = NULL;

    if (argc == 2)
         file = fopen(argv[1], "r");
    else {
         fprintf(stderr, "error: wrong number of arguments\n"
                         "usage: %s textfile\n", argv[0]);
         return EXIT_FAILURE;
    }

    if (!file) {
         fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
         return EXIT_FAILURE;
    }

    while(tempChar = fgetc(file))
    {
        /* avoid buffer overflow error */
        if (tempCharIdx == BUFFER_MAX_LENGTH) {
            fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
            return EXIT_FAILURE;
        }

        /* test character value */
        if (tempChar == EOF) {
            line[tempCharIdx] = '\0';
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            break;
        }
        else if (tempChar == '\n') {
            line[tempCharIdx] = '\0';
            tempCharIdx = 0U;
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            continue;
        }
        else
            line[tempCharIdx++] = (char)tempChar;
    }

    return EXIT_SUCCESS;
}
5
Alex Reynolds

他の回答に加えて、最近のCライブラリ(Posix 2008準拠)では、 getline を使用できます。 この回答 (関連する質問へ)を参照してください。