236

ファイルから行を読み取るために、この関数を作成しました。

const char *readLine(FILE *file) {

    if (file == NULL) {
        printf("Error: file pointer is null.");
        exit(1);
    }

    int maximumLineLength = 128;
    char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);

    if (lineBuffer == NULL) {
        printf("Error allocating memory for line buffer.");
        exit(1);
    }

    char ch = getc(file);
    int count = 0;

    while ((ch != '\n') && (ch != EOF)) {
        if (count == maximumLineLength) {
            maximumLineLength += 128;
            lineBuffer = realloc(lineBuffer, maximumLineLength);
            if (lineBuffer == NULL) {
                printf("Error reallocating space for line buffer.");
                exit(1);
            }
        }
        lineBuffer[count] = ch;
        count++;

        ch = getc(file);
    }

    lineBuffer[count] = '\0';
    char line[count + 1];
    strncpy(line, lineBuffer, (count + 1));
    free(lineBuffer);
    const char *constLine = line;
    return constLine;
}

関数はファイルを正しく読み取り、printf を使用すると、constLine 文字列も正しく読み取られたことがわかります。

ただし、たとえば次のように関数を使用すると:

while (!feof(myFile)) {
    const char *line = readLine(myFile);
    printf("%s\n", line);
}

printf は意味不明な出力をします。なんで?

4

17 に答える 17

365

あなたのタスクが行ごとの読み取り関数を発明することではなく、ファイルを行ごとに読み取ることだけである場合は、関数を含む典型的なコード スニペットを使用できます(こちらgetline()のマニュアル ページを参照してください)。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    fp = fopen("/etc/motd", "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);

    while ((read = getline(&line, &len, fp)) != -1) {
        printf("Retrieved line of length %zu:\n", read);
        printf("%s", line);
    }

    fclose(fp);
    if (line)
        free(line);
    exit(EXIT_SUCCESS);
}
于 2010-08-17T11:03:30.903 に答える
23

readLine関数では、配列へのポインターを返します(line厳密に言えば、最初の文字へのポインターですが、ここでは違いは関係ありません)。これは自動変数 (つまり、「スタック上」) であるため、関数が戻るときにメモリが解放されます。printfスタックに独自のものを置いているため、意味不明に見えます。

関数から動的に割り当てられたバッファを返す必要があります。あなたはすでにそれを持っていlineBufferます。必要な長さに切り詰めるだけです。

    lineBuffer[count] = '\0';
    realloc(lineBuffer, count + 1);
    return lineBuffer;
}

追加(コメント内のフォローアップの質問への応答):readLine行を構成する文字へのポインターを返します。このポインターは、行の内容を操作するために必要なものです。freeまた、これらのキャラクターが消費したメモリを使い終わったときに渡さなければならないものでもあります。readLine関数の使用方法は次のとおりです。

char *line = readLine(file);
printf("LOG: read a line: %s\n", line);
if (strchr(line, 'a')) { puts("The line contains an a"); }
/* etc. */
free(line);
/* After this point, the memory allocated for the line has been reclaimed.
   You can't use the value of `line` again (though you can assign a new value
   to the `line` variable if you want). */
于 2010-08-17T10:20:53.923 に答える
17
//open and get the file handle
FILE* fh;
fopen_s(&fh, filename, "r");

//check if file exists
if (fh == NULL){
    printf("file does not exists %s", filename);
    return 0;
}


//read line by line
const size_t line_size = 300;
char* line = malloc(line_size);
while (fgets(line, line_size, fh) != NULL)  {
    printf(line);
}
free(line);    // dont forget to free heap memory
于 2014-10-22T21:35:47.260 に答える
11

readLine()未定義の動作を引き起こすローカル変数へのポインターを返します。

回避するには、次のことができます。

  1. 呼び出し元関数で変数を作成し、そのアドレスをに渡しますreadLine()
  2. line使用するメモリを割り当てますmalloc()- この場合lineは永続的です
  3. 一般的に悪い習慣ですが、グローバル変数を使用します
于 2010-08-17T10:10:36.753 に答える
11

完全なfgets()解決策:

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

#define MAX_LEN 256

int main(void)
{
    FILE* fp;
    fp = fopen("file.txt", "r");
    if (fp == NULL) {
      perror("Failed: ");
      return 1;
    }

    char buffer[MAX_LEN];
    while (fgets(buffer, MAX_LEN, fp))
    {
        // Remove trailing newline
        buffer[strcspn(buffer, "\n")] = 0;
        printf("%s\n", buffer);
    }

    fclose(fp);
    return 0;
}

出力:

First line of file
Second line of file
Third (and also last) line of file

(この場合のようにファイルではなく) 標準入力から読み取りたい場合は、メソッドstdinの 3 番目のパラメーターとして次のように渡すだけで済みます。fgets()

while(fgets(buffer, MAX_LEN, stdin))

付録

fgets() 入力から末尾の改行文字を削除する

Cでファイルが開かれているかどうかを検出する方法

于 2020-10-17T19:13:46.863 に答える
9

fgets()ファイルハンドルから行を読み取るために使用します。

于 2016-05-11T02:32:37.763 に答える
4

この例にはいくつか問題があります:

  • printfs に \n を追加するのを忘れました。また、エラー メッセージは stderr に移動する必要があります。fprintf(stderr, ....
  • fgetc()(大したことではありませんが)ではなく使用を検討してgetc()ください。 getc()マクロ、fgetc()適切な関数
  • getc()を返すintためch、 として宣言する必要がありintます。との比較EOFが正しく処理されるため、これは重要です。一部の 8 ビット文字セットは0xFF、有効な文字 (ISO-LATIN-1 がその例です) として使用され、EOFこれは -1 であり0xFFchar.
  • 行でバッファ オーバーフローが発生する可能性があります

    lineBuffer[count] = '\0';
    

    行の長さがちょうど 128 文字の場合、count実行される時点で は 128 です。

  • 他の人が指摘したようにline、ローカルで宣言された配列です。ポインターを返すことはできません。

  • strncpy(count + 1)ほとんどの文字をコピーしますが 、count + 1ヒットすると終了します。ただし、そうするとターミネーティングがオンにならないので、それを行う必要があります。次のようなものをよく見かけます。'\0'lineBuffer[count]'\0'count + 1'\0'

    char buffer [BUFFER_SIZE];
    strncpy(buffer, sourceString, BUFFER_SIZE - 1);
    buffer[BUFFER_SIZE - 1] = '\0';
    
  • malloc()(ローカルchar配列の代わりに)行を返す場合、戻り値の型は .char*をドロップする必要がありconstます。

于 2010-08-17T12:20:21.200 に答える
3
const char *readLine(FILE *file, char* line) {

    if (file == NULL) {
        printf("Error: file pointer is null.");
        exit(1);
    }

    int maximumLineLength = 128;
    char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);

    if (lineBuffer == NULL) {
        printf("Error allocating memory for line buffer.");
        exit(1);
    }

    char ch = getc(file);
    int count = 0;

    while ((ch != '\n') && (ch != EOF)) {
        if (count == maximumLineLength) {
            maximumLineLength += 128;
            lineBuffer = realloc(lineBuffer, maximumLineLength);
            if (lineBuffer == NULL) {
                printf("Error reallocating space for line buffer.");
                exit(1);
            }
        }
        lineBuffer[count] = ch;
        count++;

        ch = getc(file);
    }

    lineBuffer[count] = '\0';
    char line[count + 1];
    strncpy(line, lineBuffer, (count + 1));
    free(lineBuffer);
    return line;

}


char linebuffer[256];
while (!feof(myFile)) {
    const char *line = readLine(myFile, linebuffer);
    printf("%s\n", line);
}

note that the 'line' variable is declared in calling function and then passed, so your readLine function fills predefined buffer and just returns it. This is the way most of C libraries work.

There are other ways, which I'm aware of:

  • defining the char line[] as static (static char line[MAX_LINE_LENGTH] -> it will hold it's value AFTER returning from the function). -> bad, the function is not reentrant, and race condition can occur -> if you call it twice from two threads, it will overwrite it's results
  • malloc()ing the char line[], and freeing it in calling functions -> too many expensive mallocs, and, delegating the responsibility to free the buffer to another function (the most elegant solution is to call malloc and free on any buffers in same function)

btw, 'explicit' casting from char* to const char* is redundant.

btw2, there is no need to malloc() the lineBuffer, just define it char lineBuffer[128], so you don't need to free it

btw3 do not use 'dynamic sized stack arrays' (defining the array as char arrayName[some_nonconstant_variable]), if you don't exactly know what are you doing, it works only in C99.

于 2010-08-17T10:17:33.947 に答える
2
void readLine(FILE* file, char* line, int limit)
{
    int i;
    int read;

    read = fread(line, sizeof(char), limit, file);
    line[read] = '\0';

    for(i = 0; i <= read;i++)
    {
        if('\0' == line[i] || '\n' == line[i] || '\r' == line[i])
        {
            line[i] = '\0';
            break;
        }
    }

    if(i != read)
    {
        fseek(file, i - read + 1, SEEK_CUR);
    }
}

これはどうですか?

于 2015-08-12T07:21:44.957 に答える
1

行を読み取るには ANSI 関数を使用する必要があります。fgets。呼び出した後、呼び出しコンテキストで free() が必要です。

...
const char *entirecontent=readLine(myFile);
puts(entirecontent);
free(entirecontent);
...

const char *readLine(FILE *file)
{
  char *lineBuffer=calloc(1,1), line[128];

  if ( !file || !lineBuffer )
  {
    fprintf(stderr,"an ErrorNo 1: ...");
    exit(1);
  }

  for(; fgets(line,sizeof line,file) ; strcat(lineBuffer,line) )
  {
    if( strchr(line,'\n') ) *strchr(line,'\n')=0;
    lineBuffer=realloc(lineBuffer,strlen(lineBuffer)+strlen(line)+1);
    if( !lineBuffer )
    {
      fprintf(stderr,"an ErrorNo 2: ...");
      exit(2);
    }
  }
  return lineBuffer;
}
于 2010-08-17T14:00:43.283 に答える
0

自動変数へのポインターを返すという間違いを犯しました。変数 line はスタックに割り当てられ、関数が存続している間だけ存続します。ポインターを返すことはできません。これは、ポインターが返されるとすぐにメモリが別の場所に渡されるためです。

const char* func x(){
    char line[100];
    return (const char*) line; //illegal
}

これを回避するには、ヒープ上にあるメモリへのポインタを返します。lineBuffer を使い終わったときに free() を呼び出すのはユーザーの責任です。または、行の内容を書き込むメモリアドレスを引数として渡すようにユーザーに依頼することもできます。

于 2013-03-25T23:25:40.720 に答える
0

グラウンド 0 からのコードが必要なので、辞書の単語の内容を 1 行ずつ読み取るためにこれを行いました。

char temp_str[20]; // 必要に応じてバッファ サイズを変更できます。 ファイル内の 1 行の長さ。

行を読み取るたびにヌル文字でバッファを初期化したことに注意してください。この関数は自動化できますが、概念実証が必要で、プログラムをバイトごとに設計したいので

#include<stdio.h>

int main()
{
int i;
char temp_ch;
FILE *fp=fopen("data.txt","r");
while(temp_ch!=EOF)
{
 i=0;
  char temp_str[20]={'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};
while(temp_ch!='\n')
{
  temp_ch=fgetc(fp);
  temp_str[i]=temp_ch;
  i++;
}
if(temp_ch=='\n')
{
temp_ch=fgetc(fp);
temp_str[i]=temp_ch;
}
printf("%s",temp_str);
}
return 0;
}
于 2014-09-25T09:57:23.720 に答える