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),继续写下一个文件。

在实际业务中这样一个需求,部署在某台机器上的程序,维护有一个id,这个id在程序内部每次收到请求时会 + 1,当程序崩溃重启时需要知道重启前的这个id值,这样当重启时就知道id应该取什么初始值了。这很明显是一个读少写多的场景。想到的方法有两种,一个是利用redis进行缓存,因为只需要维护一个id,并不需要很大的内存;一个是本地缓存,一个是按照文件进行维护,一个是按照共享内存维护。 起初使用了redis维护,想着方便。但是在测试中发现,会出现一些dail timeout的情况,想着并不稳妥,所以决定本地 对于一些较大文件的上传下载,我们期望的是能够一次就完成,这样不仅节省时间也节省用户流量,用户体验也会更好等等。但是网络环境的不可靠性导致较大文件的传输一次就完成的把握实在不是很大,所以针对这种情况,人们就考虑能否让失败的任务在下次继续时接着传输未传输的部分,而已经传输过的则不再传输,由此,断点续传被创造出来了。 一、Seek介绍 Golang中的断点续传实现最简便的方法是借助S Seek() 设置文件指针偏移 file.Seek 函数 设置文件指针的位置 第一个参数 : 相对偏移 第二个参数 相对哪个位置? 0 :start 1 current 2 end file.Seek(2,0) // 文件开始相对向后偏移 2 个字节 目的: 1 缩短文件传输耗时 2 异常中断,怎样继续从传输断掉的位置继续 3 传输文件的时候,支持暂停和恢复吗?即使暂停和恢复操作分别发生在进程被杀前后? 1 创建一个 临时文件,时刻保存 已经复制好的数据量 即可。 本文介绍了基于文件魔数判断文件类型的方法,主要涉及如何ReadSeek读取文件指定字节内容,然后介绍文件魔数,最后给出示例基于魔数判断文件类型。参考代码:https://github.com/telkomdev/go-filesig。 fp = open(r'test.txt', 'r') print(fp.read(3)) # 输出结果:中国山 print(fp.seek(2)) # 输出结果:2(传入的参数多少 2、ls:当前路径的所有内容 - ls -l :展示当前路径下的所有文件 / 文件夹(不包括隐藏的文件 / 文件夹)及其具体信息; - ls -a:展示当前路径的所有内容(包括隐藏的文件 / 文件夹); - ls -la:(两者结合使用)展示当前路径下的所有内容及其具体信息; - ls *xxx:(*代表0个或者多个字符),该命令用于方便检索,eg: ls *.txt 、 ls *word,表示展示当前路径下以 .txt 结尾的文件,即 txt 格式的文件; 习惯了php中的seek和tell,转到golang时突然发现只有Seek发现,tell方法不见了。google了一下,发现了tell的实现方法:File.Seek(0, os.SEEK_CUR) 或者File.Seek(0,1) 参考解释:先来看下Seek方法func (f *File) Seek(offset int64, whence int) (ret int64, err error)跳... os 包是 Go 语言的一个内置包,用于提供与操作系统进行交互的功能。实例中读取数据,并且可以使用缓冲来减少对底层数据源的直接读取次数,提高性能。实例,并且可以使用缓冲来减少对底层数据源的直接写入次数,提高性能。: 读取一行数据,返回行数据和一个标志,指示是否读取的是行的前缀。包提供了带缓冲的 I/O 操作,可以用于提高读取和写入的性能。: 将字符串写入到缓冲,然后再将缓冲中的数据写入底层数据源。: 将数据写入到缓冲,然后再将缓冲中的数据写入底层数据源。: 将缓冲中的数据写入底层数据源。 os包提供了平台无关的操作系统功能接口。尽管错误处理是 go 风格的,但设计是 Unix 风格的;所以,失败的调用会返回error而非错误码。通常error里会包含更多信息。例如,如果使用一个文件名的调用(如 Open、Stat)失败了,打印错误时会包含该文件名,错误类型将为*PathError,其内部可以解包获得更多信息。os 包规定为所有操作系统实现的接口都是一致的。有一些某个系统特定的功能,需要使用syscall获取。实际上,os依赖于syscall。在实际编程中,我们应该总是优先使用os。