golang file操作,Seek(偏移量)和Truncate(删除文件字节)

本文深入讲解Go语言中文件操作的核心方法Seek与Truncate。通过示例代码,详细解析如何使用Seek定位文件读写位置,以及Truncate调整文件大小。特别讨论了在自定义文件格式处理中的应用策略。

Seek

func (f *File) Seek(offset int64, whence int) (ret int64, err error)

官方注释:Seek设置下一次读/写的位置。offset为相对偏移量,而whence决定相对位置:0为相对文件开头,1为相对当前位置,2为相对文件结尾。它返回新的偏移量(相对开头)和可能的错误。

whence参数

io.SeekStart // 0
io.SeekCurrent // 1
io.SeekEnd // 2

具体使用方法

1.txt 文件内容: 0123456789

func tSeek_1() {
	f, err := os.OpenFile(`1.txt`, os.O_RDWR, os.ModePerm)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	end, err := f.Seek(0, io.SeekEnd)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("end: ", end) // end: 10
	fs, err := f.Stat()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("size: ", fs.Size()) 

	// 都是含前不含后的概念
	// offset是从0开始的, 可以比当前的文件内容长度大,多出的部分会用空(0)来代替
	start, err := f.Seek(12, io.SeekStart)
	if err != nil {
		log.Fatal(err)
	}
	
	fmt.Println("start: ", start)
	_, err = f.WriteString("a")
	if err != nil {
		log.Fatal(err)
	}
	b := make([]byte, 102)
	n, err := f.Read(b)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(b[:n]) // [48 49 50 51 52 53 54 55 56 57 0 0 97]
}

Truncate

func (f *File) Truncate(size int64) error

官方注释:Truncate改变文件的大小,它不会改变I/O的当前位置。 如果截断文件,多出的部分就会被丢弃。如果出错,错误底层类型是*PathError。

还有一个os.Truncate函数,和该方法类似

func Truncate(name string, size int64) error

具体使用方法

func tTruncate_1() {
	f, err := os.OpenFile(`1.txt`, os.O_RDWR, os.ModePerm)
	if err != nil {
		log.Fatal(err)
	}

	s, err := f.Seek(4, io.SeekStart)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(s)

	// Truncate方法截取长度为size,即删除后面的内容,不管当前的偏移量在哪儿,都是从头开始截取
	// 但是其不会影响当前的偏移量
	err = f.Truncate(1)
	if err != nil {
		log.Fatal(err)
	}

	_, err = f.WriteString("32")
	if err != nil {
		log.Fatal(err)
	}
	b := make([]byte, 102)
	n, err := f.Read(b)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(b[:n])
}

简单谈下我的使用场景

我现在有一个自定义格式的文件(包含多文件),不断的写头写体进去,现在有可能头已经写好了,并标明了体的大小了,但是在写体的时候出错了,这时候需要将file回退到未写该文件的状态,即抹除掉刚刚写的头和一部分体。
我的做法是:在写之前,先Seek当前的偏移量(SeekEnd),若出错,先Truncate截取到刚刚记录的(SeekEnd)量,然后再Seek到(SeekEnd),继续写下一个文件。

<think>嗯,用户问的是在Golang中使用file包进行读取操作后是否需要关闭文件。首先,我需要回忆一下Go语言的文件操作相关函数。记得Go里有几种不同的方法来读取文件,比如使用os.Open、os.OpenFile,还有ioutil.ReadFile(现在可能已经被os.ReadFile取代了)。不同的方法处理文件句柄的方式可能不同。 根据引用中的内容,比如引用[1]提到ioutil.ReadFile一次性读取文件,并且不需要手动关闭,因为打开关闭都被隐藏在方法里了。而引用[3]中的例子使用os.OpenFile,然后有defer fileHeader.Close(),这说明当使用os.Open或os.OpenFile显式打开文件时,必须手动关闭,否则会导致资源泄漏。另外,引用[5]中的os.ReadFile可能也是内部处理了关闭,所以用户不需要操心。 所以关键点在于区分不同的读取方法。如果用户是直接调用os.Open或os.OpenFile获得文件对象,那么必须调用Close,通常用defer来确保。而像os.ReadFile这样的高级封装函数,内部已经处理了关闭,所以不需要用户手动处理。 还需要确认是否有例外情况,比如如果在读取过程中发生错误,是否会影响关闭。不过一般来说,defer会在函数返回前执行,无论是否有错误,所以用defer是安全的做法。 总结起来,答案应该是:当使用显式打开文件的方法(如os.Open)时,必须手动关闭;而使用一次性读取函数(如os.ReadFile)则不需要。同时要提醒用户如果不关闭可能导致的资源泄漏问题,比如文件描述符耗尽。</think>### 回答 在 Golang 中,文件读取操作后是否需要关闭文件,取决于**文件打开的方式**: 1. **通过 `os.Open` 或 `os.OpenFile` 显式打开文件时**: 必须调用 `Close()` 方法手动关闭文件,否则会导致文件描述符泄漏,可能引发程序性能问题或资源耗尽。例如: ```go file, err := os.Open("test.txt") if err != nil { log.Fatal(err) } defer file.Close() // 必须关闭 ``` 若未关闭文件操作系统会持续占用资源,长期运行的程序可能崩溃[^4][^3]。 2. **通过 `os.ReadFile` 或 `ioutil.ReadFile` 一次性读取文件时**: 无需手动关闭,因为这两个函数内部已自动处理了文件的打开关闭操作[^1][^5]。例如: ```go content, err := os.ReadFile("test.txt") // 内部已关闭文件 ``` #### 总结 - 显式调用 `os.Open` → **必须关闭**。 - 使用高级封装函数(如 `os.ReadFile`) → **无需关闭**。 --- ### 相关问题 1. **Golang 中如何按行读取大文件以避免内存溢出?** (提示:结合 `bufio.NewReader` 逐行读取[^2]) 2. **如何通过 `os.OpenFile` 实现文件的追加写入模式?** (提示:设置 `os.O_APPEND` 标志) 3. **Golang文件读取函数 `os.ReadFile` `ioutil.ReadFile` 有何区别?** (提示:版本兼容性与包路径变化) : 一次性读取文件时无需手动关闭。 [^2]: 使用缓冲读取器处理大文件。 : 显式打开文件需配合 `defer Close()`。 : `os.OpenFile` 的模式参数控制文件行为。 : `os.ReadFile` 替代了旧版 `ioutil.ReadFile`。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值