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基础之文件操作命令行参数序列化并发编程

go语言之行--文件操作命令行参数序列化与反序列化详解

Go(day7 [终端读写| 文件操作 | 命令行参数 | Json序列化])

go语言系统-从文件操作到单元测试

Go语言基础之并发

Go语言基础之并发