go基础之文件操作命令行参数序列化并发编程(代码片段)

AC_Jobim AC_Jobim     2022-12-04     600

关键词:

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)

	//将切片进行序列化操作
	data查看详情  

go基础之单元测试反射网络编程操作redis操作mysql(代码片段)

Go基础(四)之单元测试、反射、网络编程、操作Redis、操作MySQL一、单元测试二、反射2.1两个重要函数和类型2.2类型(Type)与种类(Kind)2.3通过反射获取值信息2.3.1从反射值对象获取值2.3.2通过反射访问结... 查看详情

19.go语言基础之并发(代码片段)

...秉承的CSP(CommunicationSequentialProcess)并发模式的重要实现基础。1.2goroutine在java/Python中,我们实现并发编程的时候,通常需要自己维护一个线程池,并且需要自己去包装一个又一个的任务,同时需要自己去调度线程执行任务并维... 查看详情

go基础之程序结构数据类型(代码片段)

Go基础(一)之程序结构、数据类型一、简介1.1Go语言的介绍1.2环境配置1.3快速入门1.4Go开发的注意事项二、程序结构2.1标识符2.2变量2.3常量2.4运算符2.5流程控制2.7init函数三、数据类型3.1基本数据类型3.1.1整型3.1.2浮点型3.1... 查看详情

go基础之程序结构数据类型(代码片段)

Go基础(一)之程序结构、数据类型一、简介1.1Go语言的介绍1.2环境配置1.3快速入门1.4Go开发的注意事项二、程序结构2.1标识符2.2变量2.3常量2.4运算符2.5流程控制2.7init函数三、数据类型3.1基本数据类型3.1.1整型3.1.2浮点型3.1... 查看详情

go语言系列之并发编程(代码片段)

...outine是由Go语言的运行时(runtime)调度完成,而线程是由操作系统调度完成。G 查看详情

go语言命令行参数(代码片段)

go命令行操作指令标准go语言项目文件目录格式项目文件夹就是GOPATH指向的文件夹src文件夹是专门用于存放源码文件的main文件夹是专门用于存储packagemain包相关源码文件的其它文件夹是专门用于存储除packagemain包以外源码文件的bin... 查看详情

go语言系统-从文件操作到单元测试(代码片段)

...本介绍应用场景Json数据格式说明Jsnon数据在线解析Json的序列化应用案例Json的反序列化应用案例单元测试先看一个需求传统的方法传统方法的缺点分析单元测试基本介绍快速入门单元测试快速入门总结综合案例单元测试综合案例... 查看详情

go语言学习之旅--并发编程

...Timergolang并发编程之原子变量的引入golang并发编程之原子操作详 查看详情

go语言学习之旅--并发编程

...Timergolang并发编程之原子变量的引入golang并发编程之原子操作详 查看详情

区块链之开发命令行操作模块(代码片段)

...链项目github地址项目目前进度:功能介绍利用命令行操作区块链相较于图形用户界面来说,编写代码简单,同时也可以实现复杂的功能。命令行模块的功能应该满足:1.无任何参数输入:输出命令行支持功能信... 查看详情

go语言之路—博客目录

...零开始搭建Go语言开发环境VSCode配置Go语言开发环境Go语言基础Go语言基础之变量和常量Go语言基础之基本数据类型Go语言基础之运算符Go语言基础之流程控制Go语言基础之数组Go语言基础之切片Go语言基础之mapGo语言基础之函数Go语言... 查看详情

区块链之开发命令行操作模块(代码片段)

...链项目github地址项目目前进度:功能介绍利用命令行操作区块链相较于图形用户界面来说,编写代码简单,同时也可以实现复杂的功能。命令行模块的功能应该满足:1.无任何参数输入:输出命令行支持功能信... 查看详情

go基础之单元测试反射网络编程操作redis操作mysql(代码片段)

Go基础(四)之单元测试、反射、网络编程、操作Redis、操作MySQL一、单元测试二、反射2.1两个重要函数和类型2.2类型(Type)与种类(Kind)2.3通过反射获取值信息2.3.1从反射值对象获取值2.3.2通过反射访问结... 查看详情

go语言学习之旅--并发编程

...Timergolang并发编程之原子变量的引入golang并发编程之原子操作详解golang并发编程之协程https://blog.csdn.net/guolianggsta/article/details/123608249?spm=1001.2014.3001.5501golang并发编程之通道https://blog.csdn.net/guolianggsta/article/details/123608266?spm=100... 查看详情

第36天并发编程之进程篇(代码片段)

目录:  1.基础概念  2.创建进程和结束进程  3. 进程之间内存空间物理隔离  4. 进程的属性方法  5.守护进程  6.互斥锁  7.IPC通信机制  8.生产者消费者模型一.基础概念1.什么叫做程序,什么叫做进程... 查看详情

go并发编程的数据竞争问题

当两个或更多的操作必须以正确的顺序执行时,就会出现竞争状态,但如果程序没有写入,无法使操作顺序得到保持。大多数时候,这出现在所谓的数据竞争中,其中一个并发操作尝试在某些未确定的时间读取变量,而另一个并... 查看详情

go语言学习之路(代码片段)

...9;面对对象编程思想抽象封装继承接口文件命令行参数Json序列化反序列化(unmarshal)单元测试RedisRedis简介Redis基本使用Go连接redisRedis连接池Go面试题goroutine和channel(275-283)协程goroutine管道channel协程配合管道的综... 查看详情