10

ファイルをバッファーに読み取り、そのバッファーをコンソールに表示する次の単純な C プログラムを考えてみましょう。

#include<stdio.h>

main()
{
  FILE *file;
    char *buffer;
    unsigned long fileLen;
    //Open file
    file = fopen("HelloWorld.txt", "rb");
    if (!file)
    {
        fprintf(stderr, "Unable to open file %s", "HelloWorld.txt");
        return;
    }
    //Get file length
    fseek(file, 0, SEEK_END);
    fileLen=ftell(file);
    fseek(file, 0, SEEK_SET);
    //Allocate memory
    buffer=(char *)malloc(fileLen+1);
    if (!buffer)
    {
        fprintf(stderr, "Memory error!");
        fclose(file);
        return;
    }
    //Read file contents into buffer
    fread(buffer, fileLen, 1, file);
    //Send buffer contents to stdout
    printf("%s\n",buffer);    
    fclose(file);
}

読み取るファイルには、次のものが含まれています。

「こんにちは世界」

出力は次のとおりです。

Hello World!²²²²▌▌▌▌▌▌▌↔☺</p>

C/C++ で何か重要なことをしてからしばらく経ちましたが、通常はバッファーが必要以上に大きく割り当てられていると思いますが、そうではないようです。

fileLen は最終的に 12 になり、これは正確です。

バッファを間違って表示しているに違いないと今考えていますが、何が間違っているのかわかりません。

誰かが私が間違っていることを教えてくれますか?

4

5 に答える 5

40

文字列を NUL で終了する必要があります。追加

buffer[fileLen] = 0;

印刷する前に。

于 2008-11-03T22:45:52.823 に答える
28

JesperE のアプローチは機能しますが、これを処理する別の方法があることに興味があるかもしれません。

printf文字列フィールドの精度として長さを指定することで、NUL ターミネータがない場合でも、常に既知の長さの文字列を出力できます。

printf("%.*s\n", fileLen, buffer);

これにより、バッファを変更せずに文字列を出力できます。

于 2008-11-03T23:14:40.737 に答える
8

JesperE は、あなたの例の nul 終了の問題に関して正しいです。テキスト ファイルを処理している場合は、fgets() または同様のものを使用する方がよいと付け加えます。これは、異なるプラットフォーム間で改行シーケンスを適切に処理し、常に文字列をヌルで終了します。実際にバイナリ データを扱っている場合は、printf() を使用してデータを出力したくありません。printf 関数は文字列を想定しており、データ内の nul バイトによって出力が切り捨てられるからです。

于 2008-11-03T23:13:40.643 に答える
3

ファイルの最後までシークしてから使用してファイルサイズを決定するアプローチftell()は間違っています。

  • 呼び出し"b"の 2 番目のパラメーターを指定せずに開いたテキスト ファイルの場合、ファイルから読み取ることができる文字数がわからない場合があります。たとえば、Windows は行末に 2 バイトを使用しますが、読み取り時には 1です。実際、テキスト モードで開かれたストリームの の戻り値は、 の呼び出しでのみ有用であり、ファイル サイズの決定には役立ちません。fopen()ftell()charftell()fseek()
  • "b"2 番目のパラメータ toで開かれたバイナリ ファイルの場合fopen()、C 標準では次のように規定されています。

    のように、ファイル位置インジケータをファイルの終わりに設定するとfseek(file, 0, SEEK_END)、バイナリ ストリーム (末尾にヌル文字が含まれる可能性があるため)、または初期シフト状態で確実に終了しない状態依存エンコーディングを使用するストリームに対して未定義の動作が発生します。

したがって、あなたがしていることは必ずしも標準 C で動作するとは限りません。最善の策はfread()、読み取りに を使用することです。より多くのメモリが必要な場合は、を使用してくださいrealloc()。システムが を提供するmmap()か、バイナリ ストリームのファイル位置インジケータをファイルの終わりに設定することを保証する場合がありますが、それらに依存することは移植性がありません。

この C-FAQ:テキストとバイナリ I/O の違いは何ですか?も参照してください。.

于 2010-01-16T03:06:35.710 に答える
0

calloc代わりに、malloc既に初期化されているメモリを割り当てるために使用できます。calloc余分な引数を取ります。配列の割り当てに役立ちます。の最初のパラメーターはcalloc、メモリを割り当てたい配列内の要素の数を示し、2 番目の引数は各要素のサイズです。a のサイズは常に 1 であるため、2 番目の引数としてchar渡すことができます。1

 buffer = calloc (fileLen + 1, 1);

mallocC では、 orの戻り値をキャストする必要はありませんcalloc。上記により、ファイルの読み取りが何らかの理由で途中で終了した場合でも、文字列が null で終了することが保証されます。要求したすべてのメモリをゼロにしてから提供する必要があるため、callocよりも時間がかかります。malloc

于 2010-01-16T02:56:06.120 に答える