首页 Go 截断文件
文章
取消

Go 截断文件

有时候我们需要从某个位置截断一个文件,仅保留文件的后半部分。Go 标准库中有个 file.Truncate 函数可以帮助我们进行截断,但问题是它是保留前半部分。

要实现保留后半部分,我们可以把后半部分的内容移到前面来,再使用 file.Truncate 截断即可。

如果将文件内容全部读出来,修改,再写回去不失为一个方案,但还有一个更优的方式:使用内存映射 mmap ,可以减少内存拷贝的次数。

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
// TruncateFile truncate file,discard [0,startOffset), and keep [startOffset,)
func TruncateFile(file *os.File, startOffset int64) error {
	f, err := file.Stat()
	if err != nil {
		return err
	}
	if f.Size() <= startOffset {
		// don't need to truncate
		return nil
	}
	mem, err := syscall.Mmap(int(file.Fd()), 0, int(f.Size()), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		return err
	}
	// mem[startOffset:] override mem[0:]
	copy(mem[0:], mem[startOffset:])
	err = syscall.Munmap(mem)
	if err != nil {
		return err
	}
	err = file.Truncate(f.Size() - startOffset)
	if err != nil {
		return err
	}
	_, err = file.Seek(startOffset, 0)
	return err
}

我们创建一个文件测试下:

1
$ echo 1234567890 > nums.txt

从第8字节开始截断:

1
2
3
4
5
6
7
8
9
10
11
func main() {
	f, err := os.OpenFile("nums.txt", os.O_RDWR, 0)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	if err := TruncateFile(f, 8); err != nil {
		panic(err)
	}
	fmt.Println("truncate file done")
}

结果:

1
2
$ cat nums.txt
90
本文由作者按照 CC BY 4.0 进行授权