<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()
- 创建一个 *Reader,其缓冲区默认为 4096
- 直到遇到指定字符前,读取字符
- EOF 是文件结束标记。err == EOF 时表明文件已经结束
读取文件内容(一次性读完):
需要导入 ioutil 包。
str, err := ioutil.ReadFile("D:\\1.txt") // [1]
if err == nil {
fmt.Println(string(str))
} else {
fmt.Println(err)
}
-
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()
- 创建一个 *Writer
- 写入字符。此时,字符存在于缓存,还未真正写入文件
- 将缓存内容写入文件
写入文件内容(一次性写入):
需要导入 ioutil 包。
err := ioutil.WriteFile("D:\\1.txt", []byte("刻晴\n刻晴\n刻晴\n"), 0666) // [1]
if err != nil {
fmt.Println(err)
}
-
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 }