2012-09-02 118 views
1

我想了解一個文件位置指示器在從文件中讀取一些字節後如何移動。我有一個名爲「filename.dat」的文件,其中只有一行:「abcdefghijklmnopqrstuvwxyz」(不含引號)。文件描述符,文件指針和文件位置指示符之間的關係

#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,我從文件中沒有字節。另外,當我使用read從文件中讀取內容時,爲什麼文件位置指示器不移動?在第二個fseek,前進4個字節而不是3個也沒有工作。有什麼建議麼?

+1

相關的問題:?什麼是文件描述符之間的差異,文件指針(http://stackoverflow.com/questions/2423628/whats-the-difference-between-a-file-descriptor-and -file指針) –

回答

3

使用fseekfreadlseekread,但不要混用兩種API,它不會工作。

A FILE*有它自己的內部緩衝區。 fseek只能移動或不移動內部緩衝區指針。不能保證真實的文件位置指示器(lseek負責的那個)發生變化,如果確實如此,它不知道有多少。

1

首先要注意的是讀取調用讀取字符到一個原始緩衝區,但printf()期望爲%s參數提交以null結尾的字符串。你沒有明確地添加一個空終止符字節,所以你的程序可能會在緩衝區的前4個字節後打印垃圾,但你很幸運,你的編譯器已經將緩衝區初始化爲零,所以你沒有注意到這個問題。

這個程序中的基本問題是,你將高級緩衝FILE *調用與低級別文件描述符調用混合在一起,這將導致不可預知的行爲。 FILE結構包含一個緩衝區和兩個整數,以支持更高效和方便地訪問文件描述符後面的文件。基本上所有的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 

這是第三個printf。請注意,沒有ftell()調用的證據。 ftell()使用FILE結構中聲明爲準確的信息,因此不需要進行系統調用。

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

這是您的read()調用。現在,操作系統文件句柄是在9位,但文件結構認爲它仍然是在位置5

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

第三和第四的printf與FTELL指示位置5.

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 

第五個printf。

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

這是您的第二次read()調用。由於文件句柄位於eof,它讀取0個字節。它不會改變緩衝區中的任何內容。

read(3, "", 6)       = 0 

第六次和第七次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)       = ?