1. 获取文件流的读取位置
使用 ftell() 函数可以获取当前文件流的读取位置,其返回值为当前位置距 0 位置的字节数。文件以二进制形式打开后,默认从 0 位置开始读取。读取一定字节后,读取位置会向后推移该字节数。
例如下面的代码。未读取时 position 为 0,读取 128 字节后,位置变化为 128。继续读取 128 字节,位置变化为 256。
void TestFtell(){
FILE *file = fopen("CMakeLists.txt", "rb");
long position = ftell(file);
printf("position: %ld\n", position); // position: 0
#define BUFFER_SIZE 128
char buffer[BUFFER_SIZE];
fread(buffer, 1, BUFFER_SIZE, file);
position = ftell(file);
printf("position: %ld\n", position); // position: 128
fread(buffer, 1, BUFFER_SIZE, file);
position = ftell(file);
printf("position: %ld\n", position); // position: 256
}
2. 设定窄字符下的文件流读取位置
使用 fseek() 函数可以设定窄字符下的文件流的读取位置。可以从开始的 0 位置(SEEK_SET)进行偏移量的设定,也可利用当前文件流的位置(SEEK_CUR)对偏移量进行设定,也可使用末尾位置(SEEK_END)设定偏移量。
void TestfSeek(){
FILE *file = fopen("CMakeLists.txt", "rb");
long position;
fseek(file, 10, SEEK_SET);
position = ftell(file);
printf("position: %ld\n", position); // position: 10
fseek(file, 15, SEEK_CUR);
position = ftell(file);
printf("position: %ld\n", position); // position: 25
fseek(file, -10, SEEK_END); // 从末尾位置前移 10 个字节
position = ftell(file);
printf("position: %ld\n", position); // position: 446, 原因在于 CMakeLists.txt 共 456 个字节
}
注意,上述 ftell() 和 fseek() 函数仅能用于二进制打开的文件,不能用于文本文件,也不能用于宽字符。不能用于文本文件的原因是 C 标准未定义 ftell() 在文本文件下的返回值,即 ftell() 返回值不一定是读取的字节个数。而不能用于宽字符的原因是涉及到编码解码问题。如下图所示,中文的 “中” 字转为 utf-8 为 3 个字节,GBK 为 2 个字节。一旦使用 fseek() 函数设定偏移量,很有可能设定在 utf-8 格式的中间字节或 GBK 的末尾字节,这样就无法读取出来 “中” 这个宽字符。
3. 读取和设定宽字符下的文件流位置
使用 fgetpos() 和 fsetpos() 函数可以读取和设定宽字符下的文件流位置。注意二者的返回值为均为 fpos_t 类型。fgetpos() 函数获取当前文件流位置,传给 position。
void TestFsetpos(){
FILE *file = fopen("file.txt", "w+");
fpos_t position;
fgetpos(file, &position); // 获取当前文件流位置
fputs("hello", file);
fgetpos(file, &position);
fsetpos(file, &position);
printf("position: %lld", position); // 5
}
注意,并非所有文件都支持设定文件流位置。例如 stdout,msvc 编译器下位置默认为 0,而在 WSL 编译器位置为 -1,表示不合法。