欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > Go 1.19.4 文件读写操作-Day 14

Go 1.19.4 文件读写操作-Day 14

2024/10/25 2:26:50 来源:https://blog.csdn.net/qq_42515722/article/details/141356420  浏览:    关键词:Go 1.19.4 文件读写操作-Day 14

1. 文件读写操作

在我们对一个文件进行读写操作前,有一个必做步骤,那就是要先打开文件。

打开文件主要使用os模块的 Open 和 OpenFile 。

  • Open:适合读。以只读方式打开文件,权限使用文件默认权限,基本上是对OpenFile的封装。
  • OpenFile:适合读写。更加灵活,可以结合flag对文件进行操作,还可以设置文件权限模式。

2. 打开文件

2.1 Open

作用:

        以只读方式打开文件,权限使用文件默认权限,基本上是对OpenFile的封装。

下面是Open的源码:

func Open(name string) (*File, error) {// O_RDONLY:只读方式打开文件,文件必须存在。// 0:在 os.OpenFile 函数中,传入0作为权限参数意味着使用默认的文件权限。return OpenFile(name, O_RDONLY, 0)
}

语法:

        func os.Open(name string) (*os.File, error)

参数含义:

        name string:文件路径变量。

        *os.File:表示打开的文件对应的指针。

        error:可能发生的错误,只要有异常,error类型就会被赋值。

package mainimport ("fmt""os"
)func main() {filename := "D:/个人/学习/Go/文件与目录操作/test.txt" // 文件内容:abcf, err := os.Open(filename)if err != nil {panic(err)}// 用完后一定要关闭文件,释放文件描述符defer f.Close() // 可结合defer,让文件关闭操作,最后一定会执行fmt.Printf("f的类型=%T\nf的值=%[1]v\nf的值=%#[1]v", f)}
==========调试结果==========
f的类型=*os.File
f的值=&{0xc000004a00}
f的值=&os.File{file:(*os.file)(0xc000004a00)}

上述代码打开文件后得到一个文件操作句柄(&{0xc000004a00}),这是os.File类型的指针,使用它就可以操作文件了。

2.2 Read

作用:

        Read 方法是 os.File 结构体的一个方法,用于从文件中读取数据。

        Read是按字节读取的文件,在文件结束时,Read返回0,io.EOF。

      

语法:

        func (r *File) Read(b []byte) (n int, err error)

参数:

        b []byte:一个字节切片,用来存储读取的数据。

        n int:实际读取到的字节数。

        err error:如果有错误发生,返回错误信息。

注意事项:使用Read方法的前提,一定是已经用Open方法打开文件,并获得了文件指针。

package mainimport ("fmt""os"
)func main() {filename := "D:/个人/学习/Go/文件与目录操作/test.txt"f, err := os.Open(filename)if err != nil {panic(err)}// 用完后一定要关闭文件,释放文件描述符defer fmt.Println("文件关闭完成")defer f.Close() // 可结合defer,确保文件最后一定会关闭。defer fmt.Println("开始关闭文件")fmt.Println("返回的文件指针(文件句柄):", f)// 读取文件// 因为Read方法,是需要一个字节切片来作为参数的,所以必须要先定义一个字节切片。// 正常读取,都是k的整数倍,如1024、2048等。这里写3,只是为了测试buffer := make([]byte, 3) // 创建一个大小为 2 字节的缓冲区(字节切片)n, err := f.Read(buffer)  // 从文件中读取数据到缓冲区,n表示成功读取了n个字节if err != nil {panic(err)} else {fmt.Printf("成功读取的字节数:%d\n读取缓冲区:%d\n字节转换字符串:%s\n", n, buffer, string(buffer))}
}
==========调试结果==========
返回的文件指针(文件句柄): &{0xc000148780}
成功读取的字节数:3
读取缓冲区:[97 98 99]
字节转换字符串:abc
开始关闭文件
文件关闭完成

2.2.1 循环读取文件

package mainimport ("fmt""os"
)func main() {filename := "D:/个人/学习/Go/文件与目录操作/test.txt"f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")fmt.Println("返回的文件指针(文件句柄):", f)// 读取文件buffer := make([]byte, 1)// 无限循环读取,每次读取1个字节for {n, err := f.Read(buffer)if err != nil {// panic(err)fmt.Println(err)} else {fmt.Printf("成功读取的字节数:%d\n读取缓冲区:%d\n字节转换字符串:%s\n", n, buffer, string(buffer))fmt.Println("==================")}}}

通过断点执行发现,文件内容读取完毕后,会返回一个EOF,表示已无内容可读取,字节数也为0。

可以在判断中加上painc或break,捕获到异常就停止运行。

package mainimport ("fmt""os"
)func main() {filename := "D:/个人/学习/Go/文件与目录操作/test.txt"f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")fmt.Println("返回的文件指针(文件句柄):", f)// 读取文件buffer := make([]byte, 1)for {n, err := f.Read(buffer)if err != nil {fmt.Println(err)break// panic(err)} else {fmt.Printf("成功读取的字节数:%d\n读取缓冲区:%d\n字节转换字符串:%s\n", n, buffer, string(buffer))fmt.Println("==================")}}
}
==========调试结果==========
返回的文件指针(文件句柄): &{0xc0000cc780}
成功读取的字节数:1
读取缓冲区:[97]
字节转换字符串:a
==================
成功读取的字节数:1
读取缓冲区:[98]
字节转换字符串:b
==================
成功读取的字节数:1
读取缓冲区:[99]
字节转换字符串:c
==================
EOF
开始关闭文件
文件关闭完成

2.2.2 buffer越界

上述代码还有一个问题,如果读取的字节数大于buffer中剩余可读的字节数,会出现下面这种问题:

这个buffer越界是这样的,如上述代码,每次读取2字节,第一次读取ab放到buferr中,第二次读取到c和没有存储数据的空字节,但是由于第二次读取的结果,是覆盖上一次的结果的,所以我们看到的是cb。

解决办法:buffer[:n],从0开始到n-1,就能避免越界。

2.2.3 文件读取总结

  1. 读取完毕后,一定要关闭文件释放文件描述符,如:f.Close()或defer f.Close()。
  2. 读取文件异常时,一定要panic或break。
  3. 读取文件时,正确的做法应该是buffer[:n],防止buffer越界,特别是string(buffer[:n])。
  4. 建议每次读取大小为KB的整数倍,特别是大文件,如1024字节、2048字节等。

 3. 定位(Seek)

文件是什么?是二进制有序序列,逻辑上理解为字节数组。
也就是说,文件读写都会有一个针,指向二进制序列的索引,正常都是从前向后移动。

那么如何定位到某一个指定的字节呢?这里就要介绍下Seek(定位or寻找)。

3.1 Seek函数介绍

作用:

        在Go语言中,Seek 函数是用来设置文件读写操作中的偏移量的。Seek 函数定义在 os 包的 File 结构体中,其原型如下:

语法:

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

参数:

offset:相对偏移量。

whence:决定这个偏移量是相对于文件的哪个位置。并且whence还有3个选项:

  • 0 表示相对文件开头 (io.SeekStart),offset 只能正,负报错。
  • 1 表示相对当前位置 (io.SeekCurrent),offset 可正可负,但是负指针不能超左边界。
  • 2 表示相对文件结尾 (io.SeekEnd),offset 可正可负,但是负指针不能超左边界,正数的话,会读不到内容,但不会报错。

常用:

  • Seek(0, 0),指针拉到文件开头读写。
  • Seek(0, 2),指针拉到文件结尾读写。

3.2 示例

3.2.1 二次读取文件

注意这里调整了文件内容为:abcdef

package mainimport ("fmt""os"
)func main() {filename := "D:/个人/学习/Go/文件与目录操作/test.txt" // 文件内容:abcdeff, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")fmt.Println("返回的文件指针(文件句柄):", f)// 读取文件buffer := make([]byte, 2)// len=2, cap=2for {n, err := f.Read(buffer)if err != nil {fmt.Println(err)break// panic(err)} else {fmt.Printf("成功读取的字节数:%d\n读取缓冲区:%d\n字节转换字符串:%s\n", n, buffer[:n], string(buffer[:n]))fmt.Println("==================")}}fmt.Println("==================") // 新增内容n, err := f.Read(buffer)fmt.Println(n, err)
}
==========调试结果==========
省略部分输出
==================
0 EOF
开始关闭文件
文件关闭完成

从上面的输出结果可以看到,当我们二次读取一个已经被读取完毕的文件时,只能读取到0字节和EOF,这是因为此时的指针已经指向了文件结尾,已经没有字节可以读取了。

如果想继续读取有效内容,可以调整指针,也就是调整偏移量。

3.2.1.1 调整offset为负数,二次读取文件

错误的方式

package mainimport ("fmt""os"
)func main() {filename := "D:/个人/学习/Go/文件与目录操作/test.txt"f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")fmt.Println("返回的文件指针(文件句柄):", f)// 读取文件buffer := make([]byte, 2)for {n, err := f.Read(buffer)if err != nil {fmt.Println(err)break// panic(err)} else {fmt.Printf("成功读取的字节数:%d\n读取缓冲区:%d\n字节转换字符串:%s\n", n, buffer[:n], string(buffer[:n]))fmt.Println("==================")}}fmt.Println("=======Seek=======")ret, err := f.Seek(-2, 0) // 基于文件起始位置,往左移动2位。错误的方式fmt.Println(ret, err)
}==========调试结果==========
省略部分输出
=======Seek=======
0 seek D:/个人/学习/Go/文件与目录操作/test.txt: An attempt was made to move the file pointer before the beginning of the file.
开始关闭文件
文件关闭完成

可以看到,上述代码执行报错了,为什么?

首先for循环中,每次读取2个字节(buffer切片,长度容量都是指定为2了),后续每次读取的内容,都会覆盖上一次的内容,所以之前最终读取出来了cb,此时的指针是指在c这个位置的。

那么f.Seek(-2, 0),是说基于c这个位置(相当于就是文件内容起始位置),往左再偏移2位,从文件内容开头往左偏移,那肯定不行啊。

3.2.1.2 调整offset为正数,二次读取文件

这里把offset调整为了20,依然能够给正常执行,说明正向超界不会报错,虽然buffer切片的长度和容量都为2。

那这样做有啥意义呢?请继续往下看。

从上述结果来看,从文件末尾,正向读取的话,不会超界,最多没有内容。

3.2.1.3 从文件末尾往前读

3.3 Seek使用注意事项

 在文件读写的过程中,不要随意使用Seek,建议文件内容EOF后,再使用Seek调整指针。

3.4 ReadAt

作用:

        之前如果读取文件时,想调整偏移量,必须结合Seek和Read才行。

        而ReadAt允许指定一个偏移量,然后从这个位置开始读取一定数量的字节到一个给定的缓冲区。

        ReadAt相当于是从文件开头,开始偏移,但它不会影响当前文件指针。

        但是Read和Write都会影响文件指针。

语法:

        func (*os.File).ReadAt(b []byte, off int64) (n int, err error)

参数:

        b []byte:要读取数据的字节切片。

        

4. 自带缓冲读取(bufio)

文件使用Read读取,非常底层,操作起来很不方便,每次还要先创建一个buffer,来存储从磁盘中读取出来的字节序列,且bufio会自带一个指针,所以使用seek调整指针位置,是没用的。
 

Go语言提供了bufio包实现了对文件的二进制或文本处理的方法。

bufio 是 Go 语言标准库中用于缓冲输入输出的包。它提供了数据读写的缓冲机制,可以减少系统调用的次数,从而提高程序的效率。

4.1 bufio.NewReader

bufio.NewReader 是 Go 语言标准库中 bufio 包的一个函数,它接收一个 io.Reader 接口类型的参数,并返回一个 *bufio.Reader 类型的值。这个返回的 *bufio.Reader 包含了一个缓冲区,可以减少对底层输入流的直接调用次数。
 

这里的io.Reader 接口类型的参数,就是我们上边的f.Read(buffer),任何类型只要实现了Read(p []byte) 方法,就可以说它实现了 io.Reader 接口。

4.1.1 Read

package mainimport ("bufio""fmt""os"
)func main() {// 定义文件路径filename := "D:/个人/学习/Go/文件与目录操作/test.txt" // 文件内容:abcdef// 打开文件f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")// 读取文件fmt.Println("=========NewReader=========")reader := bufio.NewReader(f) // 这样就把f包装成了bufio的Readerb1 := make([]byte, 3)        // 新建用于存储文件内容的字节切片n, err := reader.Read(b1)fmt.Println(n, err, b1, string(b1[:n]))
}
=========NewReader=========
3 <nil> [97 98 99] abc
开始关闭文件
文件关闭完成

4.1.2 ReadByte(按单字节读取)

ReadByte读取并返回单个字节。如果没有可用的字节,则返回错误。

package mainimport ("bufio""fmt""os"
)func main() {// 定义文件路径filename := "D:/个人/学习/Go/文件与目录操作/test.txt" // 文件内容:abcdef// 打开文件f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")// 读取文件reader := bufio.NewReader(f) // 这样就把f包装成了bufio的Readerfmt.Println("=========ReadByte=========")b, err2 := reader.ReadByte()fmt.Println(b, err2, string(b))
}
=========ReadByte=========
97 <nil> a
开始关闭文件
文件关闭完成

4.1.3 ReadBytes(指定分隔符读取)

作用:

    ReadBytes 函数读取输入直到遇到 delim(分隔符 ,返回一个包含数据直到(包括)分隔符的切片。
 

        如果 ReadBytes 在找到分隔符之前遇到错误,它将返回读取的数据和错误本身(通常是 io.EOF)。
 

        如果返回的数据不以 delim(分隔符 结尾,ReadBytes 将返回 err != nil。对于简单用途,使用 Scanner 可能更方便。
 

语法:

        func (*bufio.Reader).ReadBytes(delim byte) ([]byte, error)

参数:

        delim byte:delim 是一个 byte 类型的变量,表示你想要读取到的分隔符。

返回值:

       []byte: 包含了从 bufio.Reader 读取到的、直到分隔符 delim 出现之前的所有数据。

        error:如果读取过程中遇到错误(例如,文件结束或读取错误),则会返回该错误。

package mainimport ("bufio""fmt""os"
)func main() {// 定义文件路径filename := "D:/个人/学习/Go/文件与目录操作/test.txt" // 文件内容:abcdef// 打开文件f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")// 读取文件reader := bufio.NewReader(f) // 这样就把f包装成了bufio的Readerfmt.Println("=========ReadBytes=========")b, err2 := reader.ReadBytes('d') // 我文件中的内容:abcdef测试,这里我用d作为分隔符fmt.Printf("返回的字节切片=%v\n切片转字符串=%s\nerr=%v\n", b, string(b), err2)}
=========ReadBytes=========
返回的字节切片=[97 98 99 100]
切片转字符串=abcd
err=<nil>
开始关闭文件
文件关闭完成

4.1.4 ReadRune(读取rune字符)

作用:

        从输入流中读取并返回一个 UTF-8 编码的 Unicode 字符,直到遇到错误或文件结束(EOF)。。

         注意:使用rune读取,一定要保证文件的编码是UTF-8。
 

语法:

        func (*bufio.Reader).ReadRune() (r rune, size int, err error)

返回值:

        r rune:返回一个rune类型的字符。

        size int:返回值的字节大小。

        error:如果读取过程中遇到错误(例如,文件结束或读取错误),则会返回该错误。

package mainimport ("bufio""fmt""os"
)func main() {// 定义文件路径filename := "D:/个人/学习/Go/文件与目录操作/test.txt" // 文件内容:abcdef// 打开文件f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")// 读取文件reader := bufio.NewReader(f) // 这样就把f包装成了bufio的Readerfmt.Println("=========ReadRune=========")r, size, err2 := reader.ReadRune()fmt.Printf("rune字符=%v\nrune字符大小=%d\nrune转str=%s\nerr=%v\n", r, size, string(r), err2)
}
=========ReadRune=========
rune字符=97
rune字符大小=1
rune转str=a
err=<nil>
开始关闭文件
文件关闭完成

4.1.5 ReadSlice(指定分隔符读取)

作用:

        简单来说,就是指定一个byte分隔符,然后ReadSlice读取到第一个分隔符为止,返回读取到的切片,包含了分隔符和它之前的数据。
 

语法:

        func (*bufio.Reader).ReadSlice(delim byte) (line []byte, err error)

参数:

        delim byte:分隔符

返回值:

        line []byte:读取到的数据切片

        err error:错误

package mainimport ("bufio""fmt""os"
)func main() {// 定义文件路径filename := "D:/个人/学习/Go/文件与目录操作/test.txt"// 打开文件f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")// 读取文件reader := bufio.NewReader(f)fmt.Println("=========ReadSlice=========")line, err := reader.ReadSlice('\n') // 文件内容是:abcdef\n测试fmt.Println(line, string(line), err)
}
=========ReadSlice=========
[97 98 99 100 101 102 13 10] abcdef<nil>
开始关闭文件
文件关闭完成

上述代码指定了分隔符为'\n',然后把截止到\n(包含\n)的所有数据都读出来了。

4.1.6 ReadString(指定分隔符读取,返回string)

作用:

        ReadString读取,直到输入中第一次出现分隔符,返回一个字符串,其中包含分隔符之前的数据并包括分隔符。

        如果ReadString在找到分隔符之前遇到错误,它将返回在错误之前读取的数据和错误本身(通常是io.EOF)。

        ReadString返回err != nil当且仅当返回的数据不以delim结尾时。

语法:

        func (*bufio.Reader).ReadString(delim byte) (string, error)

参数:

        delim byte:分隔符

返回值:

        string:包含了从 bufio.Reader 读取到的、直到分隔符 delim 出现之前的所有数据,包括分隔符本身。

        error:如果读取过程中遇到错误(例如,文件结束或读取错误),则会返回该错误。

package mainimport ("bufio""fmt""os"
)func main() {// 定义文件路径filename := "D:/个人/学习/Go/文件与目录操作/test.txt"// 打开文件f, err := os.Open(filename)if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")// 读取文件reader := bufio.NewReader(f)fmt.Println("=========ReadString=========")s, err := reader.ReadString('\n') // 文件内容是:abcdef\n测试fmt.Println(s, err)
}
=========ReadString=========
abcdef<nil> // 注意这里是包含了\n的
开始关闭文件
文件关闭完成

4.1.7 bufio.NewReader使用注意事项

上面演示的几种读取方法,不要混用,而且每种方法都会影响指针的位置。

5. flag

5.1 基本介绍

        flag 是一个非常有用的包,它允许你定义命令行参数,这样用户可以通过命令行来控制程序的行为。

        flag包提供了一组函数和类型,让你可以定义命令行参数,比如开关选项、字符串、整数等。这些参数可以在程序启动时通过命令行指定。

5.2 可使用的模式

O_RDONLY:只读打开文件,用的较少,因为Open方法用的就是它。

O_WRONLY:只写

O_RDWR:读写

注意上面这三个,是互斥的,不可以同时使用。

O_APPEND:追加写入

O_CREATE:文件不存在则创建,存在就不管。

O_EXCL:文件存在则报错。配合O_CREATE使用,如果创建的文件已存在就报错。
O_SYNC:同步IO,等待上一次IO完成。
O_TRUNC:对于可写的文件,打开时清空内容。

6. 常用文件操作

这里结合上面的flag,就可以进行文件读写操作了。

// 只写打开文件,文件必须存在。
flag := os.O_WRONLY// 这里有2层含义:
// (1)只写权限打开文件,指针在文件头部,写入的内容会覆盖后面存在的内容。
// (2)如果文件不存在,先创建,如果文件存在,不作操作。
flag = os.O_WRONLY | os.O_CREATE// 2层含义:
// (1)只写权限打开文件,指针在文件头部,写入的内容会覆盖后面存在的内容。
// (2)如果文件不存在,先创建。如果文件存在,先把原有内容清空,再从头写入内容。
flag = os.O_WRONLY | os.O_CREATE | os.O_TRUNC// 这里有2层含义:
// (1)只写权限打开文件,文件不存在就先创建,指针在文件头部。
// (2)APPEND调整文件指针到文件末尾,新写入的内容在文件尾部追加进去。
flag = os.O_WRONLY | os.O_APPEND | os.O_CREATE// 文件存在就报错
// 注意:不能单独使用,必须配合os.O_CREATE
flag = os.O_EXCL// 只写权限打开文件,如果文件存在,则报错。文件不存在就创建文件,并从头开始写入。
flag = os.O_WRONLY | os.O_EXCL | os.O_CREATE// 文件可读写(从文件头部开始),但要求文件存在
flag = os.O_RDWR

6.1 写入文件

6.1.1 WriteString

package mainimport ("fmt""os"
)func main() {filename := "D:/个人/学习/Go/文件与目录操作/test.txt"// 读写方式打开文件f, err := os.OpenFile(filename, os.O_RDWR, 0) // 0表示使用系统默认权限if err != nil {panic(err)}defer fmt.Println("文件关闭完成")defer f.Close()defer fmt.Println("开始关闭文件")// flag操作// 读取文件buffer := make([]byte, 3)n2, err2 := f.Read(buffer)fmt.Println("读取文件:", n2, string(buffer[:n2]), err2)// 写入文件f.WriteString("rst") // 写入缓冲区f.Sync() // 刷新缓冲中的内容到磁盘
}
==========调试结果==========
读取文件: 3 abc <nil>
开始关闭文件
文件关闭完成

上述代码,使用writestring写入了一个rst,如下图:

这里可以看到,WriteString写入的rst居然在abc的后面,为什么?

由于没有指定写入的位置,WriteString 方法将会把字符串写入到文件的当前读取/写入位置。

并且如果在读取操作之后没有移动文件的读写指针,那么写入的内容将会覆盖掉原来的位置,而不是追加到文件的末尾。

使用WriteString写入前,一定要注意指针的位置。

默认读、写打开,指针位置一定是在文件的开头,也就是索引0的位置。

6.1.2 flag使用总结

  1. O_WRONLY:只读,从头读,文件要存在
  2. O_WRONLY:只写,从头写,文件要存在。如果文件已存在有内容,从头覆盖
  3. O_CREATE | O_TRUNC:没有文件创建新文件,从头写;有文件清空内容从头写
  4. O_APPEND:追加写,文件要存在
  5. O_CREATE:文件存在,从头写;文件不存在创建新文件,从头写(单纯的create不含指针操作)
  6. O_EXCL | O_CREATE:文件不存在创建新文件,从头写;文件存在报错
  7. O_RDWR:既能读又能写,从头开始

6.2 带缓冲读写

package mainimport ("bufio""fmt""os"
)func main() {// 文件内容:abcdef测试filename := "D:/个人/学习/Go/文件与目录操作/test.txt"// 提前配置flag// 读写方式打开文件,如文件不存在,就创建,如果存在,就先清空内容。指针在文件头部。flag := os.O_RDWR | os.O_CREATE | os.O_TRUNC// os.ModePerm默认权限=0777,只影响新建文件f, err := os.OpenFile(filename, flag, os.ModePerm)//上面的openfile,还可以用create替代,create内部的flag设置和我们的flag配置一摸一样。//f, err := os.Create(filename)if err == nil {defer f.Close()// 定义读写打开文件r := bufio.NewReader(f)w := bufio.NewWriter(f)// 写入内容w.WriteString("0123456789\n")w.WriteString("abc\n")// 刷新缓冲区中的内容到磁盘w.Flush()fmt.Println("==========分割线==========")// 经过上面的写入操作,此时的指针已经到了文件末尾,所以需要调整指针到文件开头f.Seek(0, 0)// 读取刚刚写入的内容,用换行符做分隔符。fmt.Println(r.ReadString('\n'))fmt.Println(r.ReadString('\n'))} else {fmt.Println("文件打开异常:", err)}}
==========分割线==========
0123456789<nil>
abc<nil>

6.3 文件写入总结

更加推荐bufio这种自带缓冲的方式,更加简单。

注意:追加和清空这俩flag,会改变文件指针位置,实际使用时需要注意。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com