如何正确使用 lseek() 来扩展文件大小?

How to properly use lseek() to extend file size?

我试图在创建所需大小的文件时真正理解 lseek() 的用法。所以我写了这段代码,其 唯一目标是创建一个文件,其大小在输入 .

中给出

运行 例如:

$ ./lseek_test myFile 5

我希望它创建一个名为 myFile 的 5 字节文件,其最后一个字节由数字 5 占用。我得到的是一个我什至无法访问的文件。 怎么了? 我是不是理解错了 lseek() 用法?

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

#define abort_on_error(cond, msg) do {\
    if(cond) {\
        int _e = errno;\
        fprintf(stderr, "%s (%d)\n", msg, _e);\
        exit(EXIT_FAILURE);\
    }\
} while(0)

/* Write an integer with error control on the file */
void write_int(int fd, int v) {
    ssize_t c = write(fd, &v, sizeof(v));
    if (c == sizeof(v))
        return;
    abort_on_error(c == -1 && errno != EINTR, "Error writing the output file");
    abort_on_error(1, "Write operation interrupted, aborting");
}

int main(int argc, char *argv[]) {
    // Usage control
    abort_on_error(argc != 3, "Usage: ./lseek_test <FileName> <FileSize>");

    // Parsing of the input
    int size = strtol(argv[2], NULL, 0);
    // Open file
    int fd = open(argv[1], O_RDWR|O_CREAT, 0644);
    abort_on_error(fd == -1, "Error opening or creating file");

    // Use lseek() and write() to create the file of the needed size
    abort_on_error(lseek(fd, size, SEEK_SET) == -1, "Error in lseek");
    write_int(fd, size); // To truly extend the file 

    //Close file
    abort_on_error(close(fd) == -1, "Error closing file");
    return EXIT_SUCCESS;
}

在某些(非常旧的?)系统上 lseek 不允许您搜索超过文件末尾的位置,如果您尝试这样做,您将收到 EINVAL 错误。

相反,您想先使用 ftruncate 更改文件大小,然后使用 lseek 查找文件中您要读取(或写入)的位置。例如:

ftruncate(fd, 5);         // set file size to 5
lseek(fd, SEEK_END, 0);   // reposition to new end
write(fd, &v, sizeof(v);  // write data (extending the file)

根据其实现,您的程序完全符合我的预期:

  • 假设命名文件最初不存在,则创建它
  • 它将值为 5 的 int (sizeof(int)) 的 4 个字节写入文件,从偏移量 5
  • 开始
  • 它在偏移量 0 - 4 处不写入任何内容;这些都是用空字节填充的。

结果是一个九字节文件,字节值(不可打印位数):

0 0 0 0 0 5 0 0 0

(我的系统是 little-endian。)请特别注意,该文件在任何意义上都不是 text 文件。如果您期望的是文本文件(似乎是这种情况),您可能确实会看到与它相关的意外行为,您可能将其描述为无法访问它。

一些注意事项,然后:

  • 文件的第五个字节从开头开始偏移 4,而不是 5。
  • 如果你想写数字'5'然后将它存储在char中并写char;不要写它的 int 表示。或者,将文件描述符包装在流中并使用流 I/O 函数,例如 fputc().
  • 如果您想用空字节以外的任何内容填充另一个 space,那么您需要手动执行此操作。
  • 据我判断,这都是POSIX所要求的。特别是,它说的是 lseek:

The lseek() function shall allow the file offset to be set beyond the end of the existing data in the file. If data is later written at this point, subsequent reads of data in the gap shall return bytes with the value 0 until data is actually written into the gap.

(POSIX 1003.1-2008, 2016 Edition)