[英]fseek only working with fread call after rather than read?
我打開一個文件:
FILE *fp = fopen("hello_world.txt", "rb");
只有內容Hello World!
然后我得到大小並重置到開頭:
fseek(fp, 0L, SEEK_END);
size_t sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
當我去執行read
,它似乎不起作用。 read(fileno(fp), buffer, 100)
返回0
。
但是,如果我改為做;
fread(buffer, 100, 1, fp)
這確實正確地讀入了緩沖區。
更奇怪的是,如果我將第一個fseek
調用的偏移量更改為1
,它的工作完全正常(盡管已超過文件末尾)。 我想知道為什么會這樣。 我最初的想法是它與清除EOF
標志有關,但我認為至少應該在fseek
回到開始時重置。 不知道為什么fread
工作。 看起來我正在調用某種未定義的行為,因為在不同的機器上運行時有些東西是變化的,但我不知道為什么。
這是一個MCVE:
#include <stdio.h>
#include <unistd.h>
int main() {
FILE *fp = fopen("hello_world.txt", "rb");
fseek(fp, 0L, SEEK_END); // works fine if offset is 1, but read doesn't get any bytes if offset is 0
size_t sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
char buffer[100];
size_t chars_read = read(fileno(fp), buffer, 100);
printf("Buffer: %s, chars: %lu", buffer, chars_read);
fclose(fp);
return 0;
}
這個問題很棘手,但歸結為:
不要將流級別輸入/輸出和定位調用與底層系統句柄上的低級系統調用混合。
以下是對實際問題的可能解釋:
fseek(fp, 0L, SEEK_END);
使用系統調用lseek(fileno(fp), 0L, 2);
確定與系統句柄關聯的文件的長度。 系統返回的長度為12
,小於流緩沖區大小, fseek()
重置系統句柄位置並將12個字節讀入緩沖區,將系統句柄位置保留為12
,將流的內部文件位置設置為12。 ftell(fp);
返回流的內部文件位置12.這樣做是因為流以二進制模式打開,不推薦用於文本文件,因為行結束序列不會在舊系統上轉換為換行符'\\n'
。 fseek(fp, 0L, SEEK_SET);
將流的內部文件位置設置為0
,它位於當前緩沖的內容中,不會發出lseek()
系統調用。 read(fileno(fp), buffer, 100);
無法讀取任何內容,因為系統句柄的當前位置是12,文件結束。 fread(buffer, 100, 1, fp)
將從緩沖區中讀取文件內容,12個字節,嘗試從文件中讀取更多內容,沒有可用的內容,並返回讀取的字符數,12。 相反,如果你將1
傳遞給fseek()
會發生以下情況:
fseek(fp, 1L, SEEK_END);
使用系統調用lseek(fileno(fp), 0L, 2);
確定與系統句柄關聯的文件的長度。 系統返回的長度為12
,因此請求的位置為13,小於流緩沖區大小, fseek()
重置系統句柄位置並嘗試從文件中讀取13個字節到流緩沖區,但只有12個字節是可從文件中獲得。 fseek
清除緩沖區並發出系統調用lseek(fileno(fp), 1L, 2);
並跟蹤流內部文件位置為13。 ftell(fp);
返回流內部文件位置,即13
。 fseek(fp, 0L, SEEK_SET);
將內部文件位置重置為0
,並發出系統調用lseek(fileno(fp), 0L, 0);
因為該位置在當前流緩沖區之外。 read(fileno(fp), buffer, 100);
從系統句柄當前位置讀取文件內容,該位置也為0
,因此表現如預期。 筆記:
fseek()
和ftell()
的返回值是否失敗。 %zu
作為size_t
參數。 buffer
不一定是null終止的,不要使用%s
用printf
打印其內容,使用%.*s
並傳遞(int)chars_read
作為精度值。 這是一個檢測版本:
#include <stdio.h>
#include <unistd.h>
#ifndef fileno
extern int fileno(FILE *fp); // in case fileno is not declared
#endif
int main() {
FILE *fp = fopen("hello_world.txt", "rb");
if (fp) {
fseek(fp, 0L, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
char buffer[100];
ssize_t chars_read = read(fileno(fp), buffer, 100);
printf("\nread(fileno(fp), buffer, 100) = %zd, Buffer: '%.*s', sz = %zu\n",
chars_read, (int)chars_read, buffer, sz);
fclose(fp);
}
fp = fopen("hello_world.txt", "rb");
if (fp) {
fseek(fp, 1L, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
char buffer[100];
ssize_t chars_read = read(fileno(fp), buffer, 100);
printf("\nread(fileno(fp), buffer, 100) = %zd, Buffer: '%.*s', sz = %zu\n",
chars_read, (int)chars_read, buffer, sz);
fclose(fp);
}
return 0;
}
這是一個關於linux的系統調用的跟蹤符合我的暫定解釋:文件hello_world.txt包含Hello world!
沒有換行符,總共12個字節:
chqrlie$ strace ./rb612-1
...
<removed system calls related to program startup>
...
open("hello_world.txt", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5e356ed000
fstat(3, {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
lseek(3, 0, SEEK_SET) = 0
read(3, "Hello world!", 12) = 12
lseek(3, 12, SEEK_SET) = 12
read(3, "", 100) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5e356ec000
write(1, "\n", 1
) = 1
write(1, "read(fileno(fp), buffer, 100) = "..., 55read(fileno(fp), buffer, 100) = 0, Buffer: '', sz = 12
) = 55
close(3) = 0
munmap(0x7f5e356ed000, 4096) = 0
open("hello_world.txt", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5e356ed000
fstat(3, {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
lseek(3, 0, SEEK_SET) = 0
read(3, "Hello world!", 13) = 12
lseek(3, 1, SEEK_CUR) = 13
lseek(3, 0, SEEK_SET) = 0
read(3, "Hello world!", 100) = 12
write(1, "\n", 1
) = 1
write(1, "read(fileno(fp), buffer, 100) = "..., 68read(fileno(fp), buffer, 100) = 12, Buffer: 'Hello world!', sz =
) = 68
close(3) = 0
munmap(0x7f5e356ed000, 4096) = 0
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.