1

ファイルから数バイトを読み取った後、ファイル位置インジケーターがどのように移動するかを理解しようとしています。「filename.dat」という名前のファイルがあり、「abcdefghijklmnopqrstuvwxyz」(引用符なし) という 1 行が含まれています。

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main () {

    int fd = open("filename.dat", O_RDONLY);
    FILE* fp = fdopen(fd,"r");
    printf("ftell(fp): %ld, errno = %d\n", ftell(fp), errno);

    fseek(fp, 5, SEEK_SET); // advance 5 bytes from beginning of file
    printf("file position indicator: %ld, errno = %d\n", ftell(fp), errno);

    char buffer[100];
    int result = read(fd, buffer, 4); // read 4 bytes 
    printf("result = %d, buffer = %s, errno = %d\n", result, buffer, errno);
    printf("file position indicator: %ld, errno = %d\n", ftell(fp), errno);

    fseek(fp, 3, SEEK_CUR); // advance 3 bytes 
    printf("file position indicator: %ld, errno = %d\n", ftell(fp), errno);
    result = read(fd, buffer, 6);  // read 6 bytes 
    printf("result = %d, buffer = %s, errno = %d\n", result, buffer, errno);

    printf("file position indicator: %ld\n", ftell(fp));

    close(fd);
    return 0;
}


ftell(fp): 0, errno = 0
file position indicator: 5, errno = 0
result = 4, buffer = fghi, errno = 0
file position indicator: 5, errno = 0
file position indicator: 8, errno = 0
result = 0, buffer = fghi, errno = 0
file position indicator: 8

readを 2 回目に使用しようとすると、ファイルからバイトが得られない理由がわかりません。また、 を使用してファイルから内容を読み取ると、ファイル位置インジケータが移動しないのはなぜreadですか? 2 番目fseekでは、3 バイトではなく 4 バイト進めても機能しませんでした。助言がありますか?

4

2 に答える 2

3

fseekandfread または lseekandを使用しreadますが、2つのAPIを混在させないでください。機能しません。

AFILE*には独自の内部バッファがあります。fseek内部バッファポインタのみを移動する場合と移動しない場合があります。実際のファイル位置インジケーター(責任を負うもの)が変更されることは保証されていませんlseek。変更された場合、どの程度変更されたかはわかりません。

于 2012-09-02T15:25:43.730 に答える
1

最初に注意すべきことは、read 呼び出しは char を raw バッファに読み込みますが、printf() は %s パラメータに対して null で終わる文字列が渡されることを想定していることです。null ターミネータ バイトを明示的に追加していないため、プログラムはバッファの最初の 4 バイトの後にガベージを出力する可能性がありますが、幸運であり、コンパイラがバッファをゼロに初期化したため、この問題に気付かなかったのです。

このプログラムの本質的な問題は、高レベルのバッファリング FILE * 呼び出しと低レベルのファイル記述子呼び出しを混在させていることです。これにより、予期しない動作が発生します。FILE 構造体には、ファイル記述子の背後にあるファイルへのより効率的で便利なアクセスをサポートするために、バッファーといくつかの int が含まれています。

基本的に、すべての f*() 呼び出し (fopen()、fread()、fseek()、fwrite()) は、すべての I/O が FILE 構造体を使用した f*() 呼び出しによって行われることを想定しているため、バッファーとFILE 構造体のインデックス値は有効になります。低レベルの呼び出し (read()、write()、open()、close()、seek()) は、FILE 構造体を完全に無視します。

あなたのプログラムで strace を実行しました。strace ユーティリティは、プロセスによって行われたすべてのシステム コールをログに記録します。open() 呼び出しまで、興味のないものはすべて省略しました。

公募は次のとおりです。

open("filename.dat", O_RDONLY)          = 3

ここで fdopen() が発生しています。brk 呼び出しは、おそらく malloc(sizeof(FILE)) などのメモリ割り当ての証拠です。

fcntl64(3, F_GETFL)                     = 0 (flags O_RDONLY)
brk(0)                                  = 0x83ea000
brk(0x840b000)                          = 0x840b000
fstat64(3, {st_mode=S_IFREG|0644, st_size=26, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7728000

これは ftell() の影響か、fdopen の最後の部分の影響かもしれませんが、よくわかりません。

_llseek(3, 0, [0], SEEK_CUR)            = 0

これが最初のprintfです。

write(1, "ftell(fp): 0, errno = 0\n", 24) = 24

これは最初の fseek です。ファイル内の位置 5 に到達する最も簡単な方法は、5 バイトを読み取って無視することです。

_llseek(3, 0, [0], SEEK_SET)            = 0
read(3, "abcde", 5)                     = 5

これが3番目のprintfです。ftell() 呼び出しの形跡がないことに注意してください。ftell() は、正確であると主張する FILE 構造体の情報を使用するため、システム コールは必要ありません。

write(1, "file position indicator: 5, errn"..., 38) = 38

これが read() 呼び出しです。現在、オペレーティング システムのファイル ハンドルは位置 9 にありますが、FILE 構造体はまだ位置 5 にあると認識しています。

read(3, "fghi", 4)                      = 4

ftell 指示位置 5 を持つ 3 番目と 4 番目の printf。

write(1, "result = 4, buffer = fghi, errno"..., 37) = 37
write(1, "file position indicator: 5, errn"..., 38) = 38

これが fseek(fp, 3, SEEK_CUR) 呼び出しです。fseek() は、SEEK_SET をファイルの先頭に戻し、全体を FILE 構造体の 4k バッファに読み込むことにしました。位置が 5 であることが「わかっていた」ため、現在は位置が 8 である必要があることを「認識」しています。ファイルの長さは 26 バイトしかないため、os ファイルの位置は eof になります。

_llseek(3, 0, [0], SEEK_SET)            = 0
read(3, "abcdefghijklmnopqrstuvwxyz", 4096) = 26

5番目のprintf。

write(1, "file position indicator: 8, errn"..., 38) = 38

これが 2 番目の read() 呼び出しです。ファイル ハンドルは eof にあるため、0 バイトを読み取ります。バッファ内は何も変更されません。

read(3, "", 6)                          = 0

6 番目と 7 番目の printf 呼び出し。

write(1, "result = 0, buffer = fghi, errno"..., 37) = 37
write(1, "file position indicator: 8\n", 27) = 27

あなたの close() 呼び出し、およびプロセスの終了。

close(3)                                = 0
exit_group(0)                           = ?
于 2012-09-02T16:28:55.197 に答える