2012-09-02 83 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)       = ?