Go文件读写

一、文件读写

1.1.读文件

1.1.1. os.ReadFile

1
2
3
4
5
6
7
func ReadByOSReadFile() {
dat, err := os.ReadFile("./main.go")
// ioutil.ReadFile等同于os.ReadFile
// dat, err := ioutil.ReadFile("./main.go")
check(err)
fmt.Print(string(dat))
}
  • os.ReadFile会把所有文件的所有内容加载到内存,对于简单的小文件可以直接方便地使用
  • 其内部实现就是循环调用file.Read方法将文件内容写入[]byte,直到结束
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
29
30
31
32
33
34
35
36
func ReadFile(name string) ([]byte, error) {
f, err := Open(name)
if err != nil {
return nil, err
}
defer f.Close()

var size int
if info, err := f.Stat(); err == nil {
size64 := info.Size()
if int64(int(size64)) == size64 {
size = int(size64)
}
}
size++ // one byte for final read at EOF

if size < 512 {
size = 512
}

data := make([]byte, 0, size)
for {
if len(data) >= cap(data) {
d := append(data[:cap(data)], 0)
data = d[:len(data)]
}
n, err := f.Read(data[len(data):cap(data)])
data = data[:len(data)+n]
if err != nil {
if err == io.EOF {
err = nil
}
return data, err
}
}
}

1.1.2. file.Read

  • 当然,我们也可以自己通过file.Read去实现完整地读,具体实现参考os.ReadFile。
  • 自己实现就可以实现一些定制需求,例如,需要从文件的某个位置开始读
    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
    func check(e error) {
    if e != nil {
    panic(e)
    }
    }

    // file.Seek(offset, whence)
    // offset:偏移量
    // whence:0:从头开始;1: 从当前位置开始;2:从结尾开始

    func ReadBySeek() {
    f, err := os.Open("./main.go")
    check(err)
    // 将文件指针移动到 从文件头开始,偏移8位
    // 返回偏移的位置
    o2, err := f.Seek(8, 0)
    check(err)

    // 从当前文件指针位置开始读4个字节
    b2 := make([]byte, 4)
    n2, err := f.Read(b2)
    check(err)
    fmt.Printf("%d bytes @ %d: ", n2, o2)
    fmt.Printf("%v\n", string(b2[:n2]))
    }

1.1.3. bufio.NewReader

bufio.NewReader通过缓存去读,对于需要读许多小文件的效率比较高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func ReadByBufio() {
f, err := os.Open("./main.go")
check(err)
defer f.Close()
reader := bufio.NewReader(f)
for {
line, err := reader.ReadString('\n')
if err == io.EOF {
if len(line) != 0 {
fmt.Println(line)
}
break
}
check(err)
fmt.Print(line)
}
}

1.2.写文件

1.2.1. os.OpenFile

os.OpenFile()函数能够以指定模式打开文件,从而实现文件写入。

1
func OpenFile(name string, flag int, perm FileMode) (*File, error) {}

flag常用的几个位如下:

  • os.O_WRONLY
  • os.O_CREATE(文件如果不存在就创建)
  • os.O_RDONLY
  • os.O_RDWR(读写)
  • os.TRUNC(清空文件)
  • os.O_APPEND(追加)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    func WriteByOpenFile() {
    // 如果文件不存在,则创建;写入时先请空文件再开始写
    f, err := os.OpenFile("tmp.txt", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
    check(err)
    defer f.Close()
    // 写入bytes
    f.Write([]byte("f.Write \n"))
    // 写入string
    f.WriteString("f.WriteString")

    // 通过bufio.NewWriter写入(基于缓存)
    writer := bufio.NewWriter(f)
    for i := 0; i < 3; i++ {
    writer.WriteString("bufio.NewWriter\n") // 先写入缓存
    }
    writer.Flush() // 注意,如果没有flush的话,即使文件关闭了也不会写入文件
    }

1.2.2. os.WriteFIle

os.WriteFile内部实现和我们上面写的差不多,OpenFile然后Write

1
2
3
4
5
6
7
8
func WriteByOSWriteFile() {

err := os.WriteFile("tmp.txt", []byte("Write by os.WriteFile\n"), 0644)

// ioutil.WriteFile等同于OSWriteFile
// err := ioutil.WriteFile("tmp.txt", []byte("Write by ioutil.WriteFile\n"), 0644)
check(err)
}

二、josn文件读写

2.1.读json文件

读json文件主要就是用json.Unmarshal方法,通常会读到一个map或者对应的结构体中

1
2
3
4
5
6
7
8
9
func ReadJsonFileToMap(filename string) {
jsonFile, err := os.Open(filename)
check(err)
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var result map[string]interface{}
json.Unmarshal([]byte(byteValue), &result)
fmt.Println(result["users"])
}
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
29
type Social struct {
Facebook string `json:"facebook"`
Twitter string `json:"twitter"`
}

type User struct {
Name string `json:"name"`
Type string `json:"type"`
Age int `json:"Age"`
Social Social `json:"social"`
}
type Users struct {
Users []User `json:"users"`
}

func ReadJsonFileToStruct(filename string) {
jsonFile, err := os.Open(filename)
check(err)
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var users Users
json.Unmarshal(byteValue, &users)
for i := 0; i < len(users.Users); i++ {
fmt.Println("User Type: " + users.Users[i].Type)
fmt.Println("User Age: " + strconv.Itoa(users.Users[i].Age))
fmt.Println("User Name: " + users.Users[i].Name)
fmt.Println("Facebook Url: " + users.Users[i].Social.Facebook)
}
}

users.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"users": [
{
"name": "Alice",
"type": "Reader",
"age": 25,
"social": {
"facebook": "https://facebook.com",
"twitter": "https://twitter.com"
}
},
{
"name": "Bob",
"type": "Author",
"age": 28,
"social": {
"facebook": "https://facebook.com",
"twitter": "https://twitter.com"
}
}
]
}

2.2.写json文件

写json文件就是Marshal后Write

1
2
3
4
func DumpJson(users *Users) {
f, _ := json.MarshalIndent(&users, "", "\t")
ioutil.WriteFile("users_dump.json", f, 0644)
}

三、yaml文件读写

yaml文件的读写和json类似,都是通过Marshal和UnMarshal

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

import(
"gopkg.in/yaml.v3"
"io/ioutil"
)

type Conf struct {
Username string `yaml:"Username"`
Password string `yaml:"Password"`
}

func ReadYaml(string filename){
buf, err := ioutil.ReadFile(filename)
check(err)
var conf Config
yaml.Unmarshal(buf, &conf)
}


func WriteYaml() {
conf := Conf{"test", "test",}
data, _ := yaml.Marshal(&conf)
fmt.Printf("%v\n", string(data))
ioutil.WriteFile("config.yaml", data, 0644)
}

Refs