Go 文件操作

1、os 包

1.1、创建文件

1
2
3
4
5
6
7
f, err := os.Create("test.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(f) // 打印文件指针
f.Close() // 打开的资源在不使用时必须关闭

使用 Create() 创建文件时:

  • 如果文件不存在,则创建文件。
  • 如果文件存在,则清空文件内内容。
  • Create创建的文件任何人都可以读写。

1.2、打开文件,写入内容

打开文件有两种方式:

  • Open():以只读的方式打开文件,若文件不存在则会打开失败
  • OpenFile():打开文件时,可以传入打开方式,该函数的三个参数:
    • 参数1:要打开的文件路径
    • 参数2:文件打开模式,如 O_RDONLYO_WRONGLYO_RDWR,还可以通过管道符来指定文件不存在时创建文件
    • 参数3:文件创建时候的权限级别,在0-7之间,常用参数为6
1
2
3
4
5
6
f, err := os.OpenFile("test.txt", os.O_APPEND | os.O_RDWR, os.ModeAppend)
if err != nil {
fmt.Println("open file err: ", err)
return
}
f.Close()

常用的文件打开模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
	O_RDONLY 	int = syscall.O_RDONLY		// 只读
O_WRONGLY int = syscall.O_WRONGLY // 只写
O_RDWR int = syscall.O_RDWR // 读写
O_APPEND int = syscall.O_APPEND // 写操作时将数据追加到文件末尾
O_CREATE int = syscall.O_CREATE // 如果不存在则创建一个新文件
O_EXCL int = syscall.O_EXCL // 打开文件用于同步I/O
O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件
```


## 1.3、写文件

### 写入字节 `Write()`

```go
// 写入文件内容
n, err := f.Write([]byte("hello"))
if err != nil {
fmt.Println("write err: ", err)
return
}
fmt.Println("write number = ", n)

按字符串写 WriteString()

1
2
3
4
5
6
7
// 写入文件内容
n, err := f.WriteString(["hello") // 会将前5个字符替换为 hello
if err != nil {
fmt.Println("write err: ", err)
return
}
fmt.Println("write number = ", n)

修改文件的读写指针位置 Seek(),包含两个参数:

  • 参数1:偏移量,为正数时向文件尾偏移,为负数时向文件头偏移
  • 参数2:偏移的开始位置,包括:
  • io.SeekStart:从文件起始位置开始
  • io.SeekCurrent:从文件当前位置开始
  • io.SeekEnd:从文件末尾位置开始

Seek()函数返回

1
2
3
4
5
6
f, _ := os.OpenFile("test.txt",os.O_RDWR, 6)
off, _ := f.Seek(5, io.SeekStart)
fmt.Println(off) // 5
n, _ := f.WriteAt([]byte("111"), off)
fmt.Println(n)
f.Close()

1.4、文件读取

read() 实现的是按字节数读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
readByte := make([]byte, 128)			// 指定要读取的长度
for {
n, err := f.Read(readByte) // 将数据读取如切片,返回值 n 是实际读取到的字节数
if err != nil && err != io.EOF{ // 如果读到了文件末尾:EOF 即 end of file
fmt.Println("read file : ", err)
break
}

fmt.Println("read: ", string(readByte[:n]))
if n < 128 {
fmt.Println("read end")
break
}
}

1.5、文件读取偏移量

文件读取时,是可以控制光标位置的:

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
28
f, err := os.OpenFile("test.txt", os.O_RDWR, os.ModePerm)
if err != nil {
fmt.Println("open err:", err)
return
}
defer f.Close()

// 读取前五个字节,假设读取的文件内容为: hello world!
bs := []byte{0} // 创建1个字节的切片
_, err = f.Read(bs)
if err != nil {
fmt.Println("read err:", err)
return
}
fmt.Println("读到的数据是:", string(bs)) // h

// 移动光标
_, err = f.Seek(4, io.SeekStart) // 光标从开始位置(h之前),移动4位,到达o之前
if err != nil {
fmt.Println("seek err:", err)
return
}
_, err = f.Read(bs)
if err != nil {
fmt.Println("read err:", err)
return
}
fmt.Println("读到的数据是:", string(bs)) // o

通过记录光标的位置,可以实现断点续传:假设已经下载了1KB文件,即本地临时文件存储了1KB,此时断电,重启后通过本地文件大小、Seek()方法获取到上次读取文件的光标位置即可实现继续下载!

2、io/ioutil 包

2.1、io/ioutil 包文件读取

ioutil直接读取文件:

1
2
3
4
5
6
ret, err := ioutil.ReadFile("test.txt")
if err != nil {
fmt.Println("read err :", err)
return
}
fmt.Println(string(ret))

2.2、io/ioutil 包文件写入

1
2
s := "你好世界"
err := ioutil.WriteFile("test.txt", []byte(s), os.ModePerm)

3、bufio 包

3.1、bufio 的读操作

bufio封装了io.Reader、io.Writer接口对象,并创建了另一个也实现了该接口的对象:bufio.Reader、bufio.Writer。通过该实现,bufio实现了文件的缓冲区设计,可以大大提高文件I/O的效率。

使用bufio读取文件时,先将数据读入内存的缓冲区(缓冲区一般比要比程序中设置的文件接收对象要大),这样就可以有效降低直接I/O的次数。

bufio.Read([]byte)相当于读取大小len(p)的内容:

  • 当缓冲区有内容时,将缓冲区内容全部填入p并清空缓冲区
  • 当缓冲区没有内容且len(p)>len(buf),即要读取的内容比缓冲区还要大,直接去文件读取即可
  • 当缓冲区没有内容且len(p)<len(buf),即要读取的内容比缓冲区小,读取文件内容并填满缓冲区,并将p填满
  • 以后再次读取时,缓冲区有内容,将缓冲区内容全部填入p并清空缓冲区(和第一步一致)

示例:

1
2
3
4
5
6
// 创建读对象
reader := bufio.NewReader(f)

// 读一行数据
byt, _ := reader.ReadBytes('\n')
fmt.Println(string(byt))

ReadString() 函数也具有同样的功能,且能直接读取到字符串数据,无需转换,示例:读取大文件的全部数据

1
2
3
4
5
6
7
8
9
10
11
12
13
reader := bufio.NewReader(f)
for { // 按照缓冲区读取:读取到特定字符结束
str, err := reader.ReadString('\n') // 按行读取
if err != nil && err != io.EOF {
fmt.Println("read err: ", err)
break
}
fmt.Println("str = ", str)
if err == io.EOF {
fmt.Print("read end")
break
}
}

在Unix设计思想中,一切皆文件,命令行输入也可以作为文件读入:

1
2
reader := bufio.NewReader(os.Stdin)
s, _ := reader.ReadString("-") // 假设命令行以 - 开始

缓冲的思想:通过bufio,数据被写入用户缓冲,再进入系统缓冲,最后由操作系统将系统缓冲区的数据写入磁盘。

3.2、bufio 的写操作

1
2
3
4
5
6
7
writer := bufio.NewWriter(f)
_, err = writer.WriteString("hello world!")
if err != nil {
fmt.Println("write err:", err)
return
}
writer.Flush() // 必须刷新缓冲区:将缓冲区的内容写入文件中。如果不刷新,则只会在内容超出缓冲区大小时写入

Go 文件操作
https://flepeng.github.io/021-Go-36-脚本-Go-文件操作/
作者
Lepeng
发布于
2024年12月6日
许可协议