<Go>7 文件操作

本文最后更新于:2023年6月27日 上午

7 文件操作

文件是……是什么大家都懂的吧。

在程序中,以流的形式对文件来进行操作

流:数据在文件和程序间经历的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源(文件)的路径

7.1 打开、关闭文件

在 os 包中,封装了所有文件相关的操作。

os 包的结构体 File 代表一个打开的文件对象

var file *os.File

/* 打开文件 */
if f, err := os.Open("D:/1.txt"); err == nil {	// [1]
    fmt.Println("打开成功\t", f)
    file = f
} else {
    fmt.Println("打开失败\t", err)
}

/* 关闭文件 */
if err := file.Close(); err == nil {			// [2]
    fmt.Println("关闭文件")
} else {
    fmt.Println("关闭失败\t", err)
}

#常用函数

以下函数存在于 os 包

打开文件

  • func Open(name string) (*File, error):打开文件

    打开一个文件用于读取。成功时,返回一个文件指针

  • OpenFile(name string, flag int, perm FileMode) (file *File, err error)

    使用指定的选项打开文件。其中 flag 是文件打开模式(可组合使用),perm 为权限控制

    flag 有以下选择:

    • os.O_RDONLY:以只读模式打开文件
    • os.O_WRONLY:以只写方式打开文件
    • os.O_RDWR:读写模式打开文件
    • os.O_APPEND:以追加模式打开文件
    • os.O_CREATE:文件不存在时,会创建该文件
    • os.O_EXCL:和 os.O_CREATE 配合使用。要求文件必须不存在
    • os.O_SYNC:打开文件用于同步 I/O
    • os.O_TRUNC:打开时清空文件

    一定要慎重选择 flag 哟~

    另外,perm 权限控制在 Windows 系统下无效。仅 Linux 系统有效。

文件状态

  • Stat(name string) (fi FileInfo, err error):返回文件状态

    err 为 nil 时,说明文件或文件夹存在。os.IsNotExist(err) 返回 true 时,说明文件不存在。

    if _, err := os.Stat(`d:\1.txt`); err == nil {
        fmt.Println("文件存在")
    } else if os.IsNotExist(err) {
        fmt.Println("文件不存在")
    } else {
        fmt.Println(`优质答案:"我不知道"`)
    }

关闭文件

  • func (f *File) Close() error:关闭文件

    **注意:**当函数退出时,要及时关闭 File,否则会有内存泄漏

    可以使用 defer 进行延迟关闭

7.2 读、写文件

#读文件

读取文件内容(带缓冲区):

需要导入 bufio 包。该包下有一个 Reader 结构体

file, _ := os.Open("d:\\1.txt")
reader := bufio.NewReader(file)			// [1]
for {
    str, err := reader.ReadString('\n')	// [2]
    fmt.Print(str)
    if err == io.EOF {					// [3]
        break
    }
}
file.Close()
  1. 创建一个 *Reader,其缓冲区默认为 4096
  2. 直到遇到指定字符前,读取字符
  3. EOF 是文件结束标记。err == EOF 时表明文件已经结束

读取文件内容(一次性读完):

需要导入 ioutil 包。

str, err := ioutil.ReadFile("D:\\1.txt")	// [1]
if err == nil {
    fmt.Println(string(str))
} else {
    fmt.Println(err)
}
  1. ReadFile(filename string) (byte[], error)

    读取文件全部内容,返回字节数组

    这个方法不需要额外对 File 进行打开或关闭

    **注意:**该方法仅适用于较小文件

#写文件

写入文件内容(带缓冲区):

需要导入 bufio 包。

f, _ := os.OpenFile("D:\\1.txt", os.O_APPEND|os.O_RDWR|os.O_CREATE, 0)
writer := bufio.NewWriter(f)		// [1]
writer.Write([]byte("\n★"))		// [2]
writer.Flush()					// [3]
f.Close()
  1. 创建一个 *Writer
  2. 写入字符。此时,字符存在于缓存,还未真正写入文件
  3. 将缓存内容写入文件

写入文件内容(一次性写入):

需要导入 ioutil 包。

err := ioutil.WriteFile("D:\\1.txt", []byte("刻晴\n刻晴\n刻晴\n"), 0666)	// [1]
if err != nil {
    fmt.Println(err)
}
  1. WriteFile(filename string, data []byte, perm FileMode) error

    向文件写入指定全部内容。

    文件存在时,删除原本内容。不存在时,创建该文件。

7.3 命令行参数

#os.Args 获取命令行参数

os.Args 是一个 string 的切片,其存放所有的命令行参数

******> test.exe ☆ 123

上述指令的命令行参数分别是

fmt.Println(os.Args[0])		// ******\test.exe
fmt.Println(os.Args[1])		// ☆
fmt.Println(os.Args[2])		// 123

第一个参数是文件名(* 是我省略的部分),之后以空格为间隔区分其他参数

#flag 包解析命令行参数

os.Args 必须严格按照输入顺序解析命令行参数。解析参数不方便,也不便于指定参数。

******> test.exe -c ☆ -d 123

上述指令的命令行参数分别是

var i int
var s string
var lack string

/* 进行注册 */
flag.IntVar(&i, "d", -1, "命令行参数 -d,该项输入值是 123,默认值是 -1")
flag.StringVar(&s, "c", "--", "命令行参数 -c,该项输入值是 ☆,默认值是 --")
flag.StringVar(&lack, "l", "--", "命令行参数 -l,该项没有输入值,默认值是 --")

fmt.Println(i)    // -1
fmt.Println(s)    // --
fmt.Println(lack) // --

i = -1000
s = "s"
lack = "lack"

flag.Parse()
fmt.Println(i)    // 123
fmt.Println(s)    // ☆
fmt.Println(lack) // lack
  • StringVar(p *string, name string, value string, usage string)

    用指定的 名称 name、默认值 value、使用信息 usage 注册一个 string 类型的 flag,将其值保存到 p 指向的变量。

    在注册时,即向 p 中填充默认值。之后调用 Parse 方法时,再将解析到的参数填入

    IntVar(p *int, name string, value int, usage string)

    注册一个 int类型的 flag,将其值保存到 p 指向的变量。

  • Parse():转换

    从 os.Args[1:] 中解析注册的 flag。未解析到时,不进行任何赋值

    该函数建议在上述注册函数注册完成后,对变量进行调用前调用

7.4 JSON

JSON(JavaScript Object Notation)是一种轻量级数据交换格式。

JSON 易于阅读和编写,也易于机器解析和生成

JSON 自 2001 年起推广使用,目前已成为主流的数据格式。

JSON 易于机器解析和生成,并能有效地提升网络传输效率。通常程序在网络传输时会先将数据序列化为 JSON 字符串。接收方得到 JSON 字符串时,再将其反序列化恢复为原先数据。该方式已然成为各个语言的标准。

graph LR
a(Golang) --序列化--> b(JSON 字符串) --网络传输--> c(程序) --反序列化--> d(其他语言)

在 JS 语言中,一切都是对象。任何数据都能通过 JSON 来表示。

JSON 是用键值对来保存数据的方式。键名在前,值在后,内容用引号 " " 包裹。键值间以冒号 : 间隔。多个值时用方括号 [ ] 包裹。不同项间以逗号 , 间隔。

比如:

{"name":"Heruin","age":6,"address":["H.M.J","I.M"]}

#JSON 序列化

JSON 序列化是指将具有 key-value 结构的数据类型(如结构体、切片、map)序列化。

在 json 包下提供了序列化方法

  • Marshal(v interface) ([]byte, error):将给定接口序列化。

    要注意结构体中字段的访问权限(首字母大写)

    type A struct {
    	Name string `json:"A_Name"`
    	Age  int
    	add  string
    }
    
    func main() {
        /* 结构体的序列化 */
    	a := A{"Heruin", 6, "H.M.J"}
    	s, _ := json.Marshal(a)
    	fmt.Println(string(s))			// {"A_Name":"Heruin","Age":6}
        
        /* 切片的序列化 */
    	b := []string{"☆", "★"}
    	s, _ = json.Marshal(b)
    	fmt.Println(string(s))			// ["☆","★"]
        
        /* map的序列化 */
        c := map[int]string{0: "A", 1: "B", 2: "C"}
    	s, _ = json.Marshal(c)
    	fmt.Println(string(s))			// {"0":"A","1":"B","2":"C"}
        
        /* 对基本数据类型序列化 */
    	d := 1000
    	s, _ = json.Marshal(d)
    	fmt.Println(string(s))			// 1000
    }

#JSON 反序列化

JSON 序列化是指将 JSON 字符串转换成指定的数据类型

在 json 包下提供了反序列化方法

  • Unmarshal(str []byte, v *interface) error:将字符串反序列化,结果放在 v 指示的变量中

    反序列化时,应确保序列化前后的类型一致

    type A struct {
    	Name string `json:"A_Name"`
    	Age  int
    	add  string
    }
    
    func main() {
    	s1 := `{"A_Name":"Heruin","Age":6}`
    	var a A
    	json.Unmarshal([]byte(s1), &a)		// 除了 add 字段外,值等于前面的 a
    
    	s2 := `["☆","★"]`
    	var b []string
    	json.Unmarshal([]byte(s2), &b)		// 值等于前面的 b
    
    	s3 := `{"0":"A","1":"B","2":"C"}`
    	var c map[int]string
    	json.Unmarshal([]byte(s3), &c)		// 值等于前面的 c
    
    	s4 := `1000`
    	var d int
    	json.Unmarshal([]byte(s4), &d)		// 值等于前面的 d
    }

<Go>7 文件操作
https://i-melody.github.io/2022/06/22/Go/7 文件操作/
作者
Melody
发布于
2022年6月22日
许可协议