web-dev-qa-db-ja.com

Cファイルを1行ずつ読み込む

ファイルから1行読み込むためにこの関数を書きました。

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は意味のない文字を出力します。どうして?

152
lron

あなたの仕事が行ごとの読み込み関数を発明するのではなく、単に行ごとにファイルを読み込むことであるなら、あなたは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);
}
258
mbaitoff

readLine関数では、line配列へのポインタを返します(厳密には、その最初の文字へのポインタですが、違いはここでは関係ありません)。これは自動変数なので(つまり、「スタック上」にあるため)、関数が戻ったときにメモリが回収されます。 printfが独自のものをスタックに置いているので、あなたはちんぷんかんぷんしているようです。

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

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

ADDED(コメントのフォローアップ質問に対する回答):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). */
20
Gilles
FILE* fp;
char buffer[255];

fp = fopen("file.txt", "r");

while(fgets(buffer, 255, (FILE*) fp)) {
    printf("%s\n", buffer);
}

fclose(fp);
17
Rob
//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
13
RevoLab

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

回避するには、次のようにします。

  1. 呼び出し元の関数に変数を作成し、そのアドレスをreadLine()に渡します。
  2. malloc()を使用してlineにメモリを割り当てます - この場合、lineは永続的になります
  3. グローバル変数を使用しますが、一般的には悪い習慣ですが
9
qrdl

ファイルハンドルから行を読み込むには、 fgets() を使用します。

5
Raku Escape

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

  • あなたのprintfに\ nを追加するのを忘れました。エラーメッセージも標準エラーに送られるべきです。すなわちfprintf(stderr, ....
  • (大きくはありませんが)fgetc()ではなくgetc()の使用を検討してください。 getc()はマクロ、fgetc()は適切な関数です
  • getc()intを返すので、chintとして宣言する必要があります。 EOFとの比較は正しく処理されるため、これは重要です。 8ビット文字セットの中には有効な文字として0xFFを使用し(ISO-LATIN-1がその例です)、EOF(-1)がcharに割り当てられている場合は0xFFになります。
  • ラインに潜在的なバッファオーバーフローがあります

    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';
    
  • (ローカルのchar配列の代わりに)返す行をmalloc()にした場合、戻り型はchar*になります - constを削除します。

4
JeremyP

これが私の数時間です...ファイル全体を1行ずつ読み取る。

char * readline(FILE *fp, char *buffer)
{
    int ch;
    int i = 0;
    size_t buff_len = 0;

    buffer = malloc(buff_len + 1);
    if (!buffer) return NULL;  // Out of memory

    while ((ch = fgetc(fp)) != '\n' && ch != EOF)
    {
        buff_len++;
        void *tmp = realloc(buffer, buff_len + 1);
        if (tmp == NULL)
        {
            free(buffer);
            return NULL; // Out of memory
        }
        buffer = tmp;

        buffer[i] = (char) ch;
        i++;
    }
    buffer[i] = '\0';

    // Detect end
    if (ch == EOF && (i == 0 || ferror(fp)))
    {
        free(buffer);
        return NULL;
    }
    return buffer;
}

void lineByline(FILE * file){
char *s;
while ((s = readline(file, 0)) != NULL)
{
    puts(s);
    free(s);
    printf("\n");
}
}

int main()
{
    char *fileName = "input-1.txt";
    FILE* file = fopen(fileName, "r");
    lineByline(file);
    return 0;
}
3
Sam
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);
    }
}

これはどうですか?

2
Taner Mansur
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);
}

'line'変数は呼び出し元の関数で宣言されてから渡されるので、あなたのreadLine関数は事前定義されたバッファをいっぱいにして返します。これがほとんどのCライブラリの動作方法です。

私が知っている他の方法があります。

  • char line[]をstaticとして定義します(static char line[MAX_LINE_LENGTH] - >それは、関数から戻った後にその値を保持します)。 - >悪い、関数が再入可能ではなく、競合状態が発生する可能性がある - > 2つのスレッドから2回呼び出した場合、結果が上書きされる
  • char line []をmalloc()し、関数を呼び出すことで解放する - >多すぎる高価なmallocs、そしてバッファを別の関数に解放する責任を委任する(最も洗練された解決策は同じ関数内の任意のバッファに対してmallocfreeを呼び出すことです)

ところで、char*からconst char*への「明示的」キャストは冗長です。

ところで、lineBufferをmalloc()する必要はありません。単にchar lineBuffer[128]と定義するだけなので、解放する必要はありません。

btw3は '動的サイズのスタック配列'(char arrayName[some_nonconstant_variable]として配列を定義する)を使用しません、あなたが何をしているのか正確に分からない場合、それはC99でのみ機能します。

1
nothrow

行を読むためにはANSI関数を使うべきです。ばかだ。呼び出した後は、呼び出しコンテキストに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;
}
1
user411313

ファイル(input1.txt)からコンテンツを読み、取得するメソッドを実装する

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

void testGetFile() {
    // open file
    FILE *fp = fopen("input1.txt", "r");
    size_t len = 255;
    // need malloc memory for line, if not, segmentation fault error will occurred.
    char *line = malloc(sizeof(char) * len);
    // check if file exist (and you can open it) or not
    if (fp == NULL) {
        printf("can open file input1.txt!");
        return;
    }
    while(fgets(line, len, fp) != NULL) {
        printf("%s\n", line);
    }
    free(line);
}

この助けを願っています。ハッピーコーディング!

1
Nhat Dinh

ポータブルで汎用のread_line関数を提供すると、行のコンテンツを行ごとに処理できます。

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

void
read_line(const char *filename,
          size_t linecap,
          int delimiter,
          void (*process_line)(const char *line, ssize_t linelen)) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        perror(filename);
        return;
    }
    int c;
    char *line = malloc(linecap);
    if (0 == line) {
        perror("linecap");
        goto close_exit;
    }
    char *p = line;
    ssize_t linelen;
    while (EOF != (c = fgetc(file))) {
        if (delimiter == c) {
            *p = delimiter, *++p = 0;
            process_line(line, linelen+1);
            p = line;
            linelen = 0;
        } else {
            *p++ = c;
            linelen++;
        }
    }
    free(line);
    if (ferror(file)) {
        perror(filename);
        goto close_exit;
    }
 close_exit:
    fclose(file);
}

void
print_line(const char *line, ssize_t linelen) {
    fwrite(line, 1, linelen, stdout);
    fflush(stdout);
}

int
main(int argc, char **argv) {
  read_line("/a/b/c/some.txt", 16, '\n', print_line);
  return 0;
}
0
南山竹

ゼロから私の道具:

FILE *pFile = fopen(your_file_path, "r");
int nbytes = 1024;
char *line = (char *) malloc(nbytes);
char *buf = (char *) malloc(nbytes);

size_t bytes_read;
int linesize = 0;
while (fgets(buf, nbytes, pFile) != NULL) {
    bytes_read = strlen(buf);
    // if line length larger than size of line buffer
    if (linesize + bytes_read > nbytes) {
        char *tmp = line;
        nbytes += nbytes / 2;
        line = (char *) malloc(nbytes);
        memcpy(line, tmp, linesize);
        free(tmp);
    }
    memcpy(line + linesize, buf, bytes_read);
    linesize += bytes_read;

    if (feof(pFile) || buf[bytes_read-1] == '\n') {
        handle_line(line);
        linesize = 0;
        memset(line, '\0', nbytes);
    }
}

free(buf);
free(line);
0
tjeubaoit

あなたは、自動変数へのポインタを返すのを間違えます。変数行はスタック内に割り当てられ、関数が存続する限り存続します。ポインタを返すことは許可されていません。メモリが返されるとすぐに、メモリが他の場所に渡されるためです。

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

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

0
Lefteris E

グラウンド0からコードが欲しいので、辞書のWordの内容を1行ずつ読むためにこれを行いました。

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

ラインを読み込むたびにバッファをNull文字で初期化しました。この機能は自動化できますが、概念実証が必要なので、プログラムを設計するByte By Byte

#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;
}
0
Mohit Dabas