Go基础之文件操作命令行参数序列化并发编程
Posted AC_Jobim
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go基础之文件操作命令行参数序列化并发编程相关的知识,希望对你有一定的参考价值。
Go基础(三)之文件操作、命令行参数、序列化、并发编程
一、文件操作
1.1 打开和关闭文件
func Open(name string) (file *File, err error)
,打开文件
- Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
- OpenFile是一个更一般性的文件打开函数,大多数调用者都应用Open或Create代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。
func (f *File) Close() error
,关闭文件
- Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。
func main()
//打开文件
file , err := os.Open("d:/test.txt")
if err != nil
fmt.Println("open file err=", err)
//输出下文件,看看文件是什么, 看出file 就是一个指针 *File
fmt.Printf("file=%v", file)
//关闭文件
err = file.Close()
if err != nil
fmt.Println("close file err=", err)
为了防止由于程序出现异常
,而致使file.close执行不到,我们通常使用defer语句关闭文件
。
1.2 读取文件
1.2.1 按字节读取:file.Read()
func (f *File) Read(b []byte) (n int, err error)
- Read方法从f中读取最多len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取0个字节且返回值err为io.EOF。
循环读:
func main()
// 只读方式打开当前目录下的main.go文件
file, err := os.Open("./golong.txt")
if err != nil
fmt.Println("open file failed!, err:", err)
return
defer file.Close()
// 循环读取文件
var content []byte
var tmp = make([]byte, 128)
for
n, err := file.Read(tmp)
if err == io.EOF
fmt.Println("文件读完了")
break
if err != nil
fmt.Println("read file failed, err:", err)
return
content = append(content, tmp[:n]...)
fmt.Println(string(content))
1.2.2 bufio按行读取文件
bufio是在file的基础上封装了一层API,支持更多的功能。
func NewReader(rd io.Reader) *Reader
- NewReader创建一个具有默认大小缓冲、从r读取的*Reader。
// bufio按行读取示例
func main()
file, err := os.Open("./golong.txt")
if err != nil
fmt.Println("open file failed, err:", err)
return
defer file.Close()
reader := bufio.NewReader(file)
for
line, err := reader.ReadString('\\n') //注意是字符
if err == io.EOF
if len(line) != 0
fmt.Println(line)
fmt.Println("文件读完了")
break
if err != nil
fmt.Println("read file failed, err:", err)
return
fmt.Print(line)
1.2.3 ioutil读取整个文件
io/ioutil包的ReadFile方法
能够读取完整的文件
,只需要将文件名作为参数传入。
func ReadFile(filename string) ([]byte, error)
- ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。
// ioutil.ReadFile读取整个文件
func main()
content, err := ioutil.ReadFile("./main.go")
if err != nil
fmt.Println("read file failed, err:", err)
return
fmt.Println(string(content))
1.3 文件写入
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
-
OpenFile是一个更一般性的文件打开函数,大多数调用者都应用Open或Create代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。
-
name
:要打开的文件名flag
:打开文件的模式。 模式有以下几种:const ( O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件 O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件 O_RDWR int = syscall.O_RDWR // 读写模式打开文件 O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部 O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件 O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在 O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件 )
-
perm:文件权限,一个八进制数
-
创建文件时,我们可以指定文件的权限,文件权限总共分三种:读(r)、写(w)、执行(x),其中:
读(r):4 写(w):2 执行(x):1
-
然后一个文件可被拥有者、拥有者所在组里的其他成员、以及除此以外的其他成员读写或执行(在赋予相应的权限的前提下)。例如 0764 模式:
其中 0 后面第一个位置处的 7 代表 4 + 2 + 1,即 rwx 权限,意思就是:文件拥有者可以读写并执行该文件 7 后面的 6 代表 4 + 2 + 0,即 rw- 权限,意思就是:文件拥有者所在的组里的其他成员可对文件进行读写操作 6 后面的 4 代表 4 + 0 + 0,即 r-- 权限,意思就是:除了上面提到的用户,其余用户只能对该文件进行读操作
-
1.3.1 Write和WriteString
func (f *File) Write(b []byte) (n int, err error)
- Write向文件中写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误
func (f *File) WriteString(s string) (ret int, err error)
- WriteString类似Write,但接受一个字符串参数。
func main()
file, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil
fmt.Println("open file failed, err:", err)
return
defer file.Close()
str := "hello 沙河"
file.Write([]byte(str)) //写入字节切片数据
file.WriteString("hello 小王子") //直接写入字符串数据
1.3.2 bufio.NewWriter
func NewWriter(w io.Writer) *Writer
- NewWriter创建一个具有默认大小缓冲、写入w的*Writer
func main()
file, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil
fmt.Println("open file failed, err:", err)
return
defer file.Close()
writer := bufio.NewWriter(file)
for i := 0; i < 10; i++
writer.WriteString("hello沙河\\n") //将数据先写入缓存
writer.Flush() //将缓存中的内容写入文件
1.3.3 ioutil.WriteFile
func WriteFile(filename string, data []byte, perm os.FileMode) error
- 函数向filename指定的文件中写入数据。如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。
func main()
str := "hello 沙河"
err := ioutil.WriteFile("./xx.txt", []byte(str), 0666)
if err != nil
fmt.Println("write file failed, err:", err)
return
1.4 判断文件是否存在
golang判断文件或文件夹是否存在的方法为使用os.Stat()函数返回的错误值进行判断:
-
如果返回的错误为nil,说明文件或文件夹存在
-
如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
-
如果返回的错误为其它类型,则不确定是否在存在
func IsExist(filename string)
_, err := os.Stat(filename)
if err != nil && !os.IsNotExist(err)
fmt.Println(err)
return
if os.IsNotExist(err)
fmt.Println(filename, "不存在")
else
fmt.Println(filename, "存在")
func main()
IsExist("os.txt")
二、命令行参数
命令行参数(或参数):是指运行程序时提供的参数;
2.1 原生方式解析
os.Args是一个string 的切片,用来存储所有的命令行参数
func main()
fmt.Println("命令行的参数有", len(os.Args))
//遍历os.Args切片,就可以得到所有的命令行输入参数值
for i, v := range os.Args
fmt.Printf("args[%v]=%v\\n", i, v)
执行结果:
2.2 flag包用来解析命令行参数
前面的方式是比较原生的方式,对解析参数不是特别的方便,特别是带有指定参数形式的命令行。
比如: cmd>go run main.go -u root -pwd root -h 192.168.0.2 -port 3306
这样的形式命令行,go 设计者给我们提供了flag包,可以方便的解析命令行参数,而且参数顺序可以随意
flag.Type(flag 名, 默认值, 帮助信息) *Type
- Type 可以是 Int、String、Bool 等,返回值为一个相应类型的指针
flag.TypeVar(Type 指针, flag 名, 默认值, 帮助信息)
- TypeVar 可以是 IntVar、StringVar、BoolVar 等,其功能为将 flag 绑定到一个变量上
flag.Parse()
- 通过以上两种方法定义好命令行 flag 参数后,需要通过调用 flag.Parse() 来对命令行参数进行解析。
支持的命令行参数格式有以下几种:
- -flag:只支持 bool 类型;
- -flag=x;
- -flag x:只支持非 bool 类型。
输入的命令行参数:
代码实现:
func main()
//定义几个变量,用于接收命令行的参数值
var user *string
var pwd string
var host string
var port int
user = flag.String("u", "张三", "姓名")
//&pwd 就是接收用户命令行中输入的 -u 后面的参数值
//"pwd" ,就是 -pwd 指定参数
//"" , 默认值
//"用户名,默认为空" 表示对参数的说明
// flag.StringVar(&user, "u", "", "用户名,默认为空")
flag.StringVar(&pwd, "pwd", "", "密码,默认为空")
flag.StringVar(&host, "h", "localhost", "主机名,默认为localhost")
flag.IntVar(&port, "port", 3306, "端口号,默认为3306")
//这里有一个非常重要的操作,转换, 必须调用该方法
flag.Parse()
//输出结果
fmt.Printf("user=%v\\npwd=%v\\nhost=%v\\nport=%v\\n",
*user, pwd, host, port)
三、序列化与反序列化
参考文章:go语言序列化及反序列化
JSON是一种轻量级的数据交换格式,常用在前后端数据交换,go的encoding/json提供了对json的支持
3.1 序列化
func Marshal(v interface) ([]byte, error)
-
把 Go struct 序列化成 JSON对象
-
举例:
type Message struct Name string Body string Time int64 func main() m := Message"Alice", "Hello", 1294706395881547000 b, _ := json.Marshal(m) fmt.Println(string(b)) //"Name":"Alice","Body":"Hello","Time":1294706395881547000
注意:
- 只支持struct中导出的field才能被序列化,即
首字母大写的field
- GO中不是所有类型都支持序列化,其中key只支持string
- 无法对channel,complex,function序列化
- 数据中如存在循环引用,不支持序列化,因为会递归。
- pointer序列化后是其指向的值或者是nil
Struct Tag:
-
Struct tag 可以决定 Marshal 和 Unmarshal 函数如何序列化和反序列化数据。指定JSON filed name
-
JSON object 中的 name 一般都是小写,我们可以通过 struct tag 来实现:
指定 field 是 empty:
-
使用
omitempty
可以告诉 Marshal 函数如果 field 的值是对应类型的 zero-value,那么序列化之后的 JSON object 中不包含此 field:type MyStruct struct SomeField string `json:"some_field,omitempty"`
如果
SomeField == “”
,序列化之后的对象就是。
跳过 field:
-
Struct tag “-” 表示跳过指定的 filed
type MyStruct struct SomeField string `json:"some_field"` Passwd string `json:"-"`
即序列化的时候不输出,这样可以有效保护需要保护的字段不被序列化。
type Person struct
Name string `json:"name"`
Gender string `json:"gender"`
Age uint32 `json:"age,omitempty"`
Passwd string `json:"-"`
func main()
// 默认初始化
p := Person"a", "male", 23, "mimi"
fmt.Printf("%v\\n", p) //a male 23
// 序列化
b, _ := json.Marshal(p)
fmt.Println(string(b)) //"Name":"a","Gender":"male","Age":23
// 反序列化
var pp Person
err := json.Unmarshal(b, &pp)
if err != nil
fmt.Printf("序列化错误 err=%v\\n", err)
fmt.Printf("%T, %v\\n", pp, pp) //main.Person, a male 23
// 指定成员初始化
p1 := PersonName: "wsq", Gender: "male"
fmt.Println(p1) //wsq male 0
// 指定field是empty时的行为
d, _ := json.Marshal(p1)
fmt.Println(string(d)) //"name":"wsq","gender":"male"
// 跳过指定field
importPerson := PersonName: "wsq", Passwd: "password"
importPersonMar, _ := json.Marshal(importPerson)
fmt.Println(string(importPersonMar)) //"name":"wsq","gender":""
执行结果:
3.2 反序列化
func Unmarshal(data []byte, v interface) error
- 反序列化函数:Unmarshal函数解析json编码的数据并将结果存入v指向的值
b := []byte(`"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]`)
var f interface
err := json.Unmarshal(b, &f)
if err != nil
errors.New("unmarshal error")
fmt.Printf("%T, %v\\n", f, f)
结构体、map和切片的序列化和反序列化:
//定义一个结构体
type Monster struct
Name string
Age int
Birthday string //....
Sal float64
Skill string
//演示将json字符串,反序列化成struct
func unmarshalStruct()
//说明str 在项目开发中,是通过网络传输获取到.. 或者是读取文件获取到
str := "\\"Name\\":\\"牛魔王~~~\\",\\"Age\\":500,\\"Birthday\\":\\"2011-11-11\\",\\"Sal\\":8000,\\"Skill\\":\\"牛魔拳\\""
//定义一个Monster实例
var monster Monster
err := json.Unmarshal([]byte(str), &monster)
if err != nil
fmt.Printf("unmarshal err=%v\\n", err)
fmt.Printf("反序列化后 monster=%v monster.Name=%v \\n", monster, monster.Name)
//将map进行序列化
func testMap() string
//定义一个map
var a map[string]interface
//使用map,需要make
a = make(map[string]interface)
a["name"] = "红孩儿~~~~~~"
a["age"] = 30
a["address"] = "洪崖洞"
//将a这个map进行序列化
//将monster 序列化
data, err := json.Marshal(a)
if err != nil
fmt.Printf("序列化错误 err=%v\\n", err)
//输出序列化后的结果
fmt.Printf("a map 序列化后=%v\\n", string(data))
return string(data)
//演示将json字符串,反序列化成map
func unmarshalMap()
//str := "\\"address\\":\\"洪崖洞\\",\\"age\\":30,\\"name\\":\\"红孩儿\\""
str := testMap()
//定义一个map
var a map[string]interface
//反序列化
//注意:反序列化map,不需要make,因为make操作被封装到 Unmarshal函数
err := json.Unmarshal([]byte(str), &a)
if err != nil
fmt.Printf("unmarshal err=%v\\n", err)
fmt.Printf("反序列化后 a=%v\\n", a)
//演示将json字符串,反序列化成切片
func unmarshalSlice()
var slice []map[string]interface
var m1 map[string]interface
//使用map前,需要先make
m1 = make(map[string]interface)
m1["name"] = "jack"
m1["age"] = "7"
m1["address"] = "北京"
slice = append(slice, m1)
var m2 map[string]interface
//使用map前,需要先make
m2 = make(map[string]interface)
m2["name"] = "tom"
m2["age"] = "20"
m2["address"] = [2]string"墨西哥","夏威夷"
slice = append(slice, m2)
//将切片进行序列化操作
dataGo基础之文件操作命令行参数序列化并发编程