1 lseek 函数
1.1 lseek 函数简介
lseek
函数的作用是 重新定位文件的读写偏移量。
每个打开的文件都有一个关联的 当前文件的读写偏移量。这个偏移量是一个非负整数,用于指示从 文件开始处到当前读写位置的字节数。读(read)和写(write)操作都从这个偏移量开始,并在成功完成后自动更新这个偏移量,增加实际读写的字节数。
lseek
函数可以设置这个偏移量到文件的任意位置。实现了 随机访问(Random Access) 文件的能力。
1.2 lseek 函数原型
1 2 3 4
| #include <sys/types.h> #include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
|
off_t
全称是 Offset Type,它是一个用于表示文件大小和文件偏移量(位置)的数据类型。通常是 64 位的有符号整数。
-
fd
:通过 open 函数打开文件时返回的 文件描述符。
-
offset
:一个 偏移量,其含义取决于第三个参数 whence
。
-
whence
:一个 定位基准点。可以取以下三个值之一:
-
SEEK_SET
:将文件的读写偏移量设置为 从文件开始处 向后偏移 offset
个字节。
-
SEEK_CUR
:将文件的读写偏移量设置为 当前值 加上 offset
个字节。offset
可以是正数(向后移动),也可以是负数(向前移动)。
-
SEEK_END
:将文件的读写偏移量设置为从 文件末尾 向后偏移 offset 个字节。offset
同样可以是正数或负数。
1.3 lseek 函数返回值
成功:返回 新的文件偏移量(从文件开始处计算的字节数)。
失败:返回 -1
,并设置全局变量 errno 以指示错误类型。
2 lseek 使用
lseek
函数的主要使用场景:
- 文件的 “读”、“写” 使用同一偏移位置。
- 使用
lseek
获取文件大小。
lseek
在文件末尾之后移动偏移量(创建“空洞文件”)。最后需要进行写的操作。
2.1 lseek 基本使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <errno.h>
#define BUFFER_SIZE 64
int main(){ int fd; char str[] = "Alice Margatroid and Kirisame Marisa\n"; char buf[BUFFER_SIZE];
fd = open("marisa.txt", O_RDWR | O_CREAT | O_TRUNC, 0644); if(fd == -1){ perror("open error"); return 1; }
write(fd, str, strlen(str));
lseek(fd, 0, SEEK_SET);
int nbytes; while((nbytes = read(fd, buf, sizeof(buf)))){ if(nbytes == -1){ perror("read error"); return 1; } write(STDOUT_FILENO, buf, nbytes); }
close(fd);
return 0; }
|

2.2 lseek 获取文件大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <stdio.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/types.h>
int main(){ int fd = open("Geometry.cpp", O_RDWR); if(fd == -1){ perror("open error"); return 1; }
off_t file_size = lseek(fd, 0, SEEK_END); if(file_size == -1){ perror("lseek error"); return 1; }
printf("file size = %lld bytes\n", (long long)file_size);
close(fd);
return 0; }
|

这种方法获取文件大小后,文件的当前偏移量已经位于文件末尾,后续的读操作会直接读到 EOF。如果需要从别的地方读写,需要再次使用 lseek
调整位置。
2.3 lseek 拓展文件大小
使用 lseek
在文件末尾之后移动偏移量(创建“空洞文件”)。
将偏移量移动到文件末尾之后的某个位置,然后进行写入操作,中间的空隙会被填充为 \0
(空字节),从而在文件中创建一个“空洞”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <stdio.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/types.h>
int main(){ int fd = open("marisa.txt", O_RDWR); if(fd == -1){ perror("open error"); return 1; } off_t file_size = lseek(fd, 233, SEEK_END); if(file_size == -1){ perror("lseek error"); return 1; }
printf("file size = %lld bytes\n", (long long)file_size);
close(fd);
return 0; }
|


文件大小原先为 37 字节,加上 233 个空字节(\0
),最后再加上写入的 “suki” 占 4 个字节,所以表面上文件经过拓展后的大小为 274 字节。
但是,磁盘上可能不会为这些 \0
分配实际的物理块,所以这种文件看起来很大,但实际占用的磁盘空间可能很小。数据库和虚拟化软件常利用这个特性创建稀疏文件。