之前有個要把打開的文件清空,然後重新寫入的需求,但是使用 ftruncate(fd, 0)後,並沒有達到效果,反而文件頭部有了'\0',長度比預想的大了。究其原因是沒有使用 lseek 重置文件偏移量,是我太天真了,以為清空文件就會從頭開始寫入。
------------------------------------- 我是解釋分割線 --------------------------------------
首先 man ftruncate 看下幫助手冊
NAME
truncate, ftruncate - truncate a file to a specified length
SYNOPSIS
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
DESCRIPTION
The truncate() and ftruncate() functions cause the regular file named by path or referenced by fd to be truncated to a size of precisely length bytes.
If the file previously was larger than this size, the extra data is lost. If the file previously was shorter, it is extended, and the extended part reads as null bytes ('\0').
The file offset is not changed.
If the size changed, then the st_ctime and st_mtime fields (respectively, time of last status change and time of last modification; see stat(2)) for the file are updated, and the set-user-ID and
set-group-ID permission bits may be cleared.
With ftruncate(), the file must be open for writing; with truncate(), the file must be writable.
之前就是因為沒有看到紅色那行字,導致我產生了文件開頭的錯誤,都說了文件偏移量是不會改變的!
實驗如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd;
const char *s1 = "0123456789";
const char *s2 = "abcde";
fd = open("test.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
/* if error */
write(fd, s1, strlen(s1));
ftruncate(fd, 0);
// lseek(fd, 0, SEEK_SET);
write(fd, s2, strlen(s2));
close(fd);
return 0;
}
運行效果:
去掉 lseek(fd, 0, SEEK_SET); 的注釋後,效果如下:
結論:
從以上兩張圖中,可以看出,不用 lseek 的文件大小為15,用 xxd 查看16進制格式看到 文件頭有10個 '\0' 填充。
而重置文件偏移量後,文件大小為5,內容也正確。
因此,在用 ftruncate 函數時,再次寫入一定要重新設置文件偏移量(在 ftruncate 之前或之後都行,用 lseek 或 rewind 都可以)。