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

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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包进行文件操作后是否需要关闭文件,以及最佳实践是什么。我需要结合自己的知识提供的引用内容来回答这个问题。 首先,明确问题核心:文件操作后是否需要手动关闭文件。根据Golang的标准库文档,当使用os.Open或os.Create打开文件后,返回的*os.File类型确实需要手动调用Close()方法来关闭文件。这是因为操作系统对同时打开的文件数有限制,不关闭会导致资源泄漏,可能影响程序性能甚至崩溃。 然后,用户可能关心的是如何正确关闭文件,比如使用defer语句来确保在函数返回前执行Close,即使在处理过程中发生错误。此外,还要考虑错误处理,比如检查Close返回的错误,尤其是在写入文件时,因为某些系统可能只有在关闭时才会报告写入错误。 提供的引用中没有直接提到文件关闭的内容,但引用[3]中的代码示例展示了使用defer来关闭文件的方式,比如在绘图示例中可能涉及文件操作。不过用户的问题更偏向于标准库中的file包,所以应重点讲解os.File的使用。 另外,用户可能想知道如果不关闭文件会有什么后果,比如资源泄漏、数据丢失等。需要强调即使程序短暂运行,良好的资源管理习惯也是必要的,尤其是在高并发的服务中,文件描述符泄漏会导致严重问题。 最后,总结最佳实践:始终在打开文件后立即使用defer调用Close,处理可能的错误,特别是在写入操作后检查Close的错误。同时,可以提到使用像ioutil.ReadFileioutil.WriteFile这样的辅助函数,它们内部会自动处理文件的关闭,适用于简单场景,减少手动管理的机会。</think>在 Golang 中使用 `os.File` 进行文件操作时,**必须显式调用 `Close()` 方法关闭文件**,这是防止资源泄漏的关键操作。以下是具体的最佳实践: --- ### 一、必须关闭文件的原理 1. **操作系统资源限制**:文件描述符是系统级资源,每个进程有打开文件数量的上限(可通过 `ulimit -n` 查看)。未关闭文件会导致资源耗尽,最终触发 `too many open files` 错误[^1] 2. **数据完整性风险**:写操作可能使用缓冲区,未正确关闭文件会导致数据未完全写入磁盘 3. **跨平台兼容性**:不同系统对文件锁的实现不同,未关闭文件可能引发不可预知的锁定问题 --- ### 二、最佳实践示例 #### 标准操作模式 ```go f, err := os.Open("test.txt") if err != nil { log.Fatal(err) } defer f.Close() // 关键:确保函数退出时关闭 // 文件操作... ``` #### 写入文件特别注意 ```go f, err := os.Create("output.txt") if err != nil { log.Fatal(err) } defer func() { if err := f.Close(); err != nil { // 检查关闭错误 log.Printf("文件关闭失败: %v", err) } }() // 写入操作... _, err = f.WriteString("Hello World") if err != nil { log.Fatal(err) } ``` --- ### 三、特殊情况处理 1. **长期打开的文件**:如日志文件,建议使用 `sync()` 定期刷新缓冲区 ```go f.Sync() // 强制将缓冲区内容写入磁盘 ``` 2. **循环中的文件操作**:避免在循环内使用 `defer`,应显式关闭 ```go for _, filename := range filenames { f, err := os.Open(filename) if err != nil { continue } // 处理文件... f.Close() // 立即关闭 } ``` --- ### 四、辅助工具建议 1. 使用 `ioutil` 包简化操作(自动处理关闭): ```go data, err := ioutil.ReadFile("input.txt") // 自动关闭 err = ioutil.WriteFile("output.txt", data, 0644) // 自动关闭 ``` 2. 结合 `defer` 错误处理(如引用[3]中的模式): ```go defer func() { if cerr := f.Close(); cerr != nil && err == nil { err = cerr // 将关闭错误传递到外层 } }() ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值