web-dev-qa-db-ja.com

Cプログラミング:ファイルの内容全体をバッファーに読み込む方法

ファイルの全内容をバッファに書き込みたい。ファイルには、実際に文字列と比較する必要がある文字列のみが含まれています。

Linuxでも移植可能な最も効率的なオプションは何でしょうか。

ENV:Windows

70
Sunny

LinuxはC向けの適切で高品質のツールチェーンを備えたPOSIX準拠のシステムであるため、LinuxとWindowsの間の移植性は大きな頭痛の種です。一方、WindowsはC標準ライブラリに多くの機能すら提供していません。

ただし、標準に固執する場合は、次のように記述できます。

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

FILE *f = fopen("textfile.txt", "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);  /* same as rewind(f); */

char *string = malloc(fsize + 1);
fread(string, 1, fsize, f);
fclose(f);

string[fsize] = 0;

ここでstringには、適切に0で終了するC文字列としてテキストファイルの内容が含まれます。このコードは単なる標準Cであり、POSIX固有ではありません(ただし、Windowsでの動作/コンパイルを保証するものではありません...)

136
user529758

ここに私がお勧めするものがあります。

C89に準拠し、完全に移植可能でなければなりません。特に、POSIXyシステムのパイプとソケットでも機能します。

アイデアは、入力を大規模なチャンク(READALL_CHUNK)で読み取り、必要に応じてバッファーを動的に再割り当てすることです。 realloc()fread()ferror()、およびfree()のみを使用します。

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

/* Size of each input chunk to be
   read and allocate for. */
#ifndef  READALL_CHUNK
#define  READALL_CHUNK  262144
#endif

#define  READALL_OK          0  /* Success */
#define  READALL_INVALID    -1  /* Invalid parameters */
#define  READALL_ERROR      -2  /* Stream error */
#define  READALL_TOOMUCH    -3  /* Too much input */
#define  READALL_NOMEM      -4  /* Out of memory */

/* This function returns one of the READALL_ constants above.
   If the return value is zero == READALL_OK, then:
     (*dataptr) points to a dynamically allocated buffer, with
     (*sizeptr) chars read from the file.
     The buffer is allocated for one extra char, which is NUL,
     and automatically appended after the data.
   Initial values of (*dataptr) and (*sizeptr) are ignored.
*/
int readall(FILE *in, char **dataptr, size_t *sizeptr)
{
    char  *data = NULL, *temp;
    size_t size = 0;
    size_t used = 0;
    size_t n;

    /* None of the parameters can be NULL. */
    if (in == NULL || dataptr == NULL || sizeptr == NULL)
        return READALL_INVALID;

    /* A read error already occurred? */
    if (ferror(in))
        return READALL_ERROR;

    while (1) {

        if (used + READALL_CHUNK + 1 > size) {
            size = used + READALL_CHUNK + 1;

            /* Overflow check. Some ANSI C compilers
               may optimize this away, though. */
            if (size <= used) {
                free(data);
                return READALL_TOOMUCH;
            }

            temp = realloc(data, size);
            if (temp == NULL) {
                free(data);
                return READALL_NOMEM;
            }
            data = temp;
        }

        n = fread(data + used, 1, READALL_CHUNK, in);
        if (n == 0)
            break;

        used += n;
    }

    if (ferror(in)) {
        free(data);
        return READALL_ERROR;
    }

    temp = realloc(data, used + 1);
    if (temp == NULL) {
        free(data);
        return READALL_NOMEM;
    }
    data = temp;
    data[used] = '\0';

    *dataptr = data;
    *sizeptr = used;

    return READALL_OK;
}

上記では、一定のチャンクサイズREADALL_CHUNK == 262144(256*1024)を使用しました。つまり、最悪の場合、最大262145文字が無駄になります(割り当てられますが、使用されません)が、一時的です。最後に、関数はバッファを最適なサイズに再割り当てします。また、これは、読み取りデータのメガバイトごとに4つの再割り当てを行うことを意味します。

上記のコードの262144バイトのデフォルトは控えめな値です。古いミニラップトップ、Raspberry Pi、およびプロセスで使用可能な少なくとも数メガバイトのRAMを備えたほとんどの組み込みデバイスでもうまく機能します。しかし、ほとんどのシステムでは(多くの読み取り呼び出しと多くのバッファー再割り当てのために)操作が遅くなるほど小さくはありません。

現時点(2017)のデスクトップマシンでは、はるかに大きいREADALL_CHUNK、おそらく#define READALL_CHUNK 2097152(2 MiB)をお勧めします。

READALL_CHUNKの定義は保護されている(つまり、コードのその時点で未定義の場合にのみ定義される)ため、(ほとんどのCコンパイラで)-DREADALL_CHUNK=2097152コマンドラインオプションを使用して、コンパイル時にデフォルト値をオーバーライドできます。 -ただし、コマンドラインオプションを使用してプリプロセッサマクロを定義するためのコンパイラオプションを確認してください。

17
Nominal Animal