web-dev-qa-db-ja.com

テキストファイルをCのバッファに読み込む正しい方法は?

私はそれらを処理している間にバッファに読み込む小さなテキストファイルを扱っているので、次のコードを思いつきました:

_...
char source[1000000];

FILE *fp = fopen("TheFile.txt", "r");
if(fp != NULL)
{
    while((symbol = getc(fp)) != EOF)
    {
        strcat(source, &symbol);
    }
    fclose(fp);
}
...
_

これはファイルの内容をバッファに入れる正しい方法ですか?strcat()を悪用していますか?

次に、バッファーを次のように繰り返します。

_for(int x = 0; (c = source[x]) != '\0'; x++)
{
    //Process chars
}
_
24
Gary Willoughby
_char source[1000000];

FILE *fp = fopen("TheFile.txt", "r");
if(fp != NULL)
{
    while((symbol = getc(fp)) != EOF)
    {
        strcat(source, &symbol);
    }
    fclose(fp);
}
_

このコードにはいくつかの問題があります。

  1. 非常に遅い(一度に1文字ずつバッファーを抽出する)。
  2. ファイルサイズがsizeof(source)を超えている場合、これはバッファオーバーフローの傾向があります。
  3. 実際、もっと詳しく見ると、このコードはまったく機能しないはずです。マニュアルページに記載されているとおり:

strcat()関数は、nullで終わる文字列s2のコピーをnullで終わる文字列s1の末尾に追加してから、終了する `\ 0 'を追加します。

NULで終了する場合もしない場合もある文字列に文字(NULで終了する文字列ではありません!)を追加しています。 のみ時間は、ファイル内のすべての文字がNULで終了している場合、マンページの説明に従って動作すると想像できます。その場合、これはかなり無意味です。はい、これは間違いなくstrcat()のひどい乱用です。

代わりに使用することを検討する2つの選択肢を次に示します。

事前に最大バッファサイズがわかっている場合:

_#include <stdio.h>
#define MAXBUFLEN 1000000

char source[MAXBUFLEN + 1];
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
    size_t newLen = fread(source, sizeof(char), MAXBUFLEN, fp);
    if ( ferror( fp ) != 0 ) {
        fputs("Error reading file", stderr);
    } else {
        source[newLen++] = '\0'; /* Just to be safe. */
    }

    fclose(fp);
}
_

または、そうしない場合:

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

char *source = NULL;
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
    /* Go to the end of the file. */
    if (fseek(fp, 0L, SEEK_END) == 0) {
        /* Get the size of the file. */
        long bufsize = ftell(fp);
        if (bufsize == -1) { /* Error */ }

        /* Allocate our buffer to that size. */
        source = malloc(sizeof(char) * (bufsize + 1));

        /* Go back to the start of the file. */
        if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }

        /* Read the entire file into memory. */
        size_t newLen = fread(source, sizeof(char), bufsize, fp);
        if ( ferror( fp ) != 0 ) {
            fputs("Error reading file", stderr);
        } else {
            source[newLen++] = '\0'; /* Just to be safe. */
        }
    }
    fclose(fp);
}

free(source); /* Don't forget to call free() later! */
_
68
Michael

はい-恐らくstrcatのひどい乱用で逮捕されるでしょう!

Getline()を見てください。一度に1行ずつデータを読み取りますが、重要なことは、読み取る文字数を制限できるため、バッファがオーバーフローしないようにします。

Strcatは、文字を挿入するたびに文字列全体を検索する必要があるため、比較的低速です。通常、文字列ストレージの現在の末尾へのポインターを保持し、次の行を読み込む位置としてgetlineに渡します。

4
Martin Beckett

strcatを使用したくない理由については、 JoelOnSoftwareのこの記事 を参照してください。

fread を参照してください。バイトまたは文字を読み取る場合、サイズに1を指定して使用します。

1
Mark Ransom

持っている文字の配列だけを使用してみませんか?これはそれを行うべきです:

   source[i] = getc(fp); 
   i++;
1
Martin Wickman

Linuxシステムを使用している場合、ファイル記述子を取得すると、fstat()を使用してファイルに関する多くの情報を取得できます。

http://linux.die.net/man/2/stat

あなたは持っているかもしれない

#include  <unistd.h> 
void main()
{
    struct stat stat;
    int fd;
    //get file descriptor
    fstat(fd, &stat);
    //the size of the file is now in stat.st_size
}

これにより、ファイルの先頭と末尾を探す必要がなくなります。

1
toweleeele

テストはされていませんが、動作するはずです。そして、はい、freadを使用することでより適切に実装できます。読者への演習として残しておきます。

#define DEFAULT_SIZE 100
#define STEP_SIZE 100

char *buffer[DEFAULT_SIZE];
size_t buffer_sz=DEFAULT_SIZE;
size_t i=0;
while(!feof(fp)){
  buffer[i]=fgetc(fp);
  i++;
  if(i>=buffer_sz){
    buffer_sz+=STEP_SIZE;
    void *tmp=buffer;
    buffer=realloc(buffer,buffer_sz);
    if(buffer==null){ free(tmp); exit(1);} //ensure we don't have a memory leak
  }
}
buffer[i]=0;
1
Earlz

あなたが恐れたいと思う方法:

http://www.cplusplus.com/reference/clibrary/cstdio/fread/

0
wprl