Go (一) 基础部分4 -- 文件处理,命令行参数,json序列化

一、文件基本介绍

1.1、打开一个文件

基本介绍:打开一个文件用于读取,如果操作成功,返回的文件对象的方法可用于读取文件数据。如果出错,错误底层类型是"*.PathError"

func Open(name string) (*File, error)

name string:打开的文件路径

*File:返回值1,文件对象

error:返回值2,错误err


1.2、关闭一个文件

基本介绍:语法如下

func (f *File) Close() error

(f *File) Close():文件对象的Close方法

error:返回值1,错误err

打开文件,关闭文件,快速入门案例:

package main

import (
	"fmt"
	"os"
)

func main() {
	// 打开一个文件(默认有2个返回值:文件对象,错误)
	file, err:=os.Open("sudada.log")
	// 如果err有值,则输出错误
	if err != nil {
		fmt.Println("打开文件失败,错误:",err)
	}

	// 通过文件对象file的值
	fmt.Println(file) // 返回值:&{0xc000100a00}
	fmt.Println(file.Name()) // 返回值:sudada.log

	// 关闭文件(默认有1个返回值:错误)
	close_err := file.Close()
	if close_err != nil {
		fmt.Println("打开文件失败,错误:",close_err)
	}
}

1.3、读文件内容

1.读取文件内容并显示在终端(带缓冲区的方式):使用bufio.NewReader(),reader.ReadString函数和方法。

步骤:先打开文件,然后读文件,最后关闭文件

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	// 打开一个文件(有2个返回值:文件对象,错误)
	file, err:=os.Open("sudada.log")
	// 如果err有值,则输出错误
	if err != nil {
		fmt.Println("打开文件失败,错误:",err)
	}

	// 在函数要退出时,关闭文件
	defer file.Close()

	// 创建一个 *Reader,是带缓冲的(默认4096字节)
	reader := bufio.NewReader(file)
	// 循环读取文件的内容: reader.ReadString (有2个返回值:文件内容,错误)
	for {
		str,err := reader.ReadString('\n') // 读到"换行"就结束
		// 读到文件结尾时,就break
		if err == io.EOF {
			break
		}
		// 打印读取到的文件内容
		fmt.Print(str)
		// 返回值:hello world
		// 返回值:sudada
		// 返回值:beijing
	}
}

2.读取文件内容并显示在终端(使用ioutil一次将整个文件读入到内存中),这种方式适合小文件:使用ioutil.ReadFile函数

步骤:一次将文件读取到位

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	// 使用ioutil.ReadFile一次性将文件读取到位
	file := "sudada.log"
	content,err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println(err)
	}
	// 显示读取的内容
	fmt.Printf("%v",string(content))
	// hello world
	// sudada
	// beijing
	// shanghai
}

1.4、常用的4种写文件方式

基本介绍:语法如下

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

name string:打开的文件

flag int:文件打开的模式

os.O_WRONLY 只写模式

os.O_RDWR 读写模式

os.O_RDONLY 只读模式

os.O_CREATE 如果文件不存在则创建

os.O_EXCL 和os.O_CREATE配合使用,文件必须不存在
os.O_SYNC 打开文件用于同步IO
os.O_TRUNC 如果可能,打开是清空文件

os.O_APPEND 追加内容

perm FileMode:文件的权限控制(linux)

file *File:返回值1,文件对象

err error:返回值2,错误err

快速入门案例:

1.创建一个新文件,写入内容:hello world(模式 os.O_WRONLY | os.O_CREATE

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// 创建一个新文件,写入内容:hello world
	// 1.文件名"test.log"
	filename := "test.log"
	// 打开文件"test.log"
	file,err :=os.OpenFile(filename, os.O_WRONLY | os.O_CREATE, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}

	// 2.写入到文件的内容(\r\n表示换行)
	str:="hello world sudada \r\n"
	// 写入文件时,使用带缓存的 *Writer
	writer:=bufio.NewWriter(file)
	writer.WriteString(str)
	// writer带缓存,在调用WriteString方法写入时,需要flush到磁盘。
	writer.Flush()

	// 3.关闭文件句柄
	defer file.Close()
}

2.打开一个存在的文件,覆盖文件的内容(模式 os.O_WRONLY | os.O_TRUNC

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// 1.文件名"test.log"
	filename := "test.log"
	// 打开文件"test.log"
	file,err :=os.OpenFile(filename, os.O_WRONLY | os.O_TRUNC, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}

	// 2.写入到文件的内容(\r\n表示换行)
	str:="hello world sudada \r\n"
	// 写入文件时,使用带缓存的 *Writer
	writer:=bufio.NewWriter(file)
	writer.WriteString(str)
	// writer带缓存,在调用WriteString方法写入时,需要flush到磁盘。
	writer.Flush()

	// 3.关闭文件句柄
	defer file.Close()
}

3.打开一个存在的文件,追加内容(模式 os.O_WRONLY | os.O_APPEND

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// 1.文件名"test.log"
	filename := "test.log"
	// 打开文件"test.log"
	file,err :=os.OpenFile(filename, os.O_WRONLY | os.O_APPEND, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}
	// 2.写入到文件的内容(\r\n表示换行)
	str:="hello world new \r\n"
	// 写入文件时,使用带缓存的 *Writer
	writer:=bufio.NewWriter(file)
	writer.WriteString(str)
	// writer带缓存,在调用WriteString方法写入时,需要flush到磁盘。
	writer.Flush()

	// 3.关闭文件句柄
	defer file.Close()
}

4.打开一个存在的文件,读取原来的内容,然后在追加内容(模式 os.O_RDWR | os.O_APPEND

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	// 1.文件名"test.log"
	filename := "test.log"
	// 打开文件"test.log"
	file,err :=os.OpenFile(filename, os.O_RDWR | os.O_APPEND, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}
	// 2.先读文件
	reader:=bufio.NewReader(file)
	for{
		str,err := reader.ReadString('\n')
		// 如果读取到文件的末尾,就break
		if err == io.EOF {
			break
		}
		// 显示文件已有的内容
		fmt.Print(str)
	}
	// 3.然后再追加文件内容
	str:="hello shanghai \r\n"
	// 写入文件时,使用带缓存的 *Writer
	writer:=bufio.NewWriter(file)
	writer.WriteString(str)
	// writer带缓存,在调用WriteString方法写入时,需要flush到磁盘。
	writer.Flush()
	// 4.关闭文件句柄
	defer file.Close()
}

5.将文件a复制到文件b(使用ioutil.ReadFile() 和 ioutil.WriteFile()方法)

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	file_test := "test.log"
	file_new_test := "new_test.log"

	// 1.读文件"test.log"的内容
	data, read_err := ioutil.ReadFile(file_test)
	if read_err != nil {
		fmt.Println("read file test.log error: ",read_err)
		return
	}
	// 2.将"test.log"的文件内容写到"new_test.log"
	write_err := ioutil.WriteFile(file_new_test, data, 0666)
	if write_err != nil {
		fmt.Println("writer file new_test.log error: ",write_err)
		return
	}
}

1.5、判断文件或目录是否存在

os.Stat(file_path) 方法的返回值说明:
1.返回的错误为nil,说明文件或文件夹存在
2.返回的错误使用os.IsNotExist()为true,说明文件或文件夹不存在
3.返回的错误为其他类型,则不确定是否存在

package main

import (
	"fmt"
	"os"
)

func main() {
	file_path := "test.log"
	_, err := os.Stat(file_path)
	// os.Stat(file_test) 的用法:
	// 1.返回的错误为nil,说明文件或文件夹存在
	// 2.返回的错误使用os.IsNotExist()为true,说明文件或文件夹不存在
	// 3.返回的错误为其他类型,则不确定是否存在

	// err == nil 时,文件存在
	if err == nil {
		fmt.Printf("file %v exist !", file_path)
	}

	// os.IsNotExist()为true时,文件不存在
	if os.IsNotExist(err) {
		fmt.Printf("file %v is not exist !", file_path)
	}
}

// 封装的一个函数判断文件是否存在
func PathExist(path string) (bool, error) {
	_, err := os.Stat(path)
	// os.Stat(file_test) 的用法
	// 1.返回的错误为nil,说明文件或文件夹存在
	// 2.返回的错误使用os.IsNotExist()为true,说明文件或文件夹不存在
	// 3.返回的错误为其他类型,则不确定是否存在

	// err == nil 时,文件存在
	if err == nil {
		return true, nil
	}
	// os.IsNotExist()为true时,文件不存在
	if os.IsNotExist(err) {
		return false, err
	}
	return false, err
}

func main() {
	file_path := "1test.log"
	flg, msg:=PathExist(file_path)
	if flg {
		fmt.Printf("file %v exist !", file_path)
	} else {
		fmt.Printf("file %v is not exist, msg: %v\n", file_path, msg)
	}
}

1.6、文件拷贝

Copy函数是io包提供的

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func CopyFile(dstFileName string, srcFileName string)(written int64, err error)  {
	// 获取源文件
	srcFile,err := os.Open(srcFileName)
	if err != nil {
		fmt.Println("open file error: ",err)
	}
	// 关闭文件句柄
	defer srcFile.Close()
	// 通过srcFile,获取到Reader
	reader := bufio.NewReader(srcFile)

	// 打开目标文件
	dstFile, err := os.OpenFile(dstFileName,os.O_WRONLY | os.O_CREATE,0666)
	if err != nil {
		fmt.Println("open file error: ",err)
		return
	}
	writer := bufio.NewWriter(dstFile)
	// 关闭文件句柄
	defer dstFile.Close()
	// 通过io.Copy将源文件copy到目标文件
	return io.Copy(writer, reader)
}

func main() {
	// 将文件funingla.png, copy到fufu.png
	srcFileName := "funingla.png"
	dstFileName := "fufu.png"
	_,err := CopyFile(dstFileName, srcFileName)
	if err == nil {
		fmt.Println("copy success")
	} else {
		fmt.Println("error: ",err)
	}
}

二、命令行参数

2.1、获取命令行参数

方法1:os.Args是一个string切片,用来存储所有的命令行参数。

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println("命令行的参数有",len(os.Args))
	// 编译os.Args切片就拿到所有的命令行参数
	for i,v := range os.Args {
		fmt.Printf("args[%v]=%v\n",i,v)
	}
	// 执行命令(空格分隔):go run main.go test aa abc
	// 第1个参数(文件名):main.exe
	// 第2个参数(实际的参数1):test
	// 第3个参数(实际的参数2):aa
	// 第4个参数(实际的参数3):abc
}

方法2:flag包解析命令行参数

package main

import (
	"flag"
	"fmt"
)

func main() {
	var user string
	var port int
	var pwd string
	var host string

	// &user:接收输入的"-u"的值,默认值为"",参数解释:"用户名"
	flag.StringVar(&user,"u","","用户名")
	// &port:接收输入的"-port"的值,默认值为3306,参数解释:"端口,默认为3306"
	flag.IntVar(&port,"port",3306,"端口,默认为3306")
	// &pwd:接收输入的"-p"的值,默认值为"",参数解释:"端口,默认为空"
	flag.StringVar(&pwd,"p","","密码,默认为空")
	// &host:接收输入的"-h"的值,默认值为"localhost",参数解释:"主机地址,默认为localhost"
	flag.StringVar(&host,"h","localhost","主机地址,默认为localhost")

	// 必须转换
	flag.Parse()

	// 输出结果
	fmt.Printf("user=%v,port=%v,pwd=%v,host=%v\n",user,port,pwd,host)

	// 执行命令(必须以空格分隔):go run main.go -u root -p asd!@#%^&( -port 3306 -h 127.0.0.1
	// 返回结果:user=root,port=3306,pwd=asd!@#%&(,host=127.0.0.1
}

三、Json序列化

序列化快速入门:data, err := json.Marshal(monster)

data == 拿到序列化后的json格式数据

monster == 被序列化的格式(结构体,map,切片)

3.1、结构体-序列化

package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	Name string
	Age int
	Birthday string
	Skill string
}

// 结构体Json序列化
func testStruct()  {
	var monster = Monster{
		Name:"sudada",
		Age:18,
		Birthday:"2000-06-11",
		Skill:"牛牛冲击",
	}
	data, err := json.Marshal(monster)
	if err != nil {
		fmt.Println("序列化失败:",err)
	}
	// struct序列化结果
	fmt.Println(string(data))
	// 返回值(key的值被大写了,因为结构体的字段是大写的):{"Name":"sudada","Age":18,"Birthday":"2000-06-11","Skill":"牛牛冲击"}
}

func main() {
	testStruct()
}

结构体json序列化时tag的使用(可以将结构体的字段小写)

package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	name string `json:"name"`  // 反射机制
	Age int `json:"age"`  // 反射机制
	Birthday string `json:"birthday"`  // 反射机制
	Skill string `json:"skill"`  // 反射机制
}

// 结构体Json序列化
func testStruct()  {
	var monster = Monster{
		name: "sudada",
		Age:18,
		Birthday:"2000-06-11",
		Skill:"牛牛冲击",
	}

	// 结构体字段不能小写,因为在json包内无法识别小写的"结构体的字段名"
	// 如果"结构体字段小写"且进行序列化,那么"序列化后的数据"不包含这个"小写字段"的值
	// 这里小写name字段为例,序列化后得到的值:{"age":18,"birthday":"2000-06-11","skill":"牛牛冲击"}
	data, err := json.Marshal(monster)
	if err != nil {
		fmt.Println("序列化失败:",err)
	}
	// struct序列化结果
	fmt.Println(string(data))
	// 返回值(字段小写):{"name":"sudada","age":18,"birthday":"2000-06-11","skill":"牛牛冲击"}
}

func main() {
	testStruct()
}

3.2、map-序列化

package main

import (
	"encoding/json"
	"fmt"
)

// map序列化
func mapTest()  {
	var a map[string]interface{}
	a = make(map[string]interface{})
	a["name"]="sudada"
	a["age"]="18"
	a["skill"]="丢丢"

	// map序列化
	data, err := json.Marshal(a)
	if err != nil {
		fmt.Println("序列化失败:",err)
	}
	fmt.Println(string(data))
	// 返回值:{"age":"18","name":"sudada","skill":"丢丢"}
}

func main() {
	mapTest()
}

3.3、切片-序列化

package main

import (
	"encoding/json"
	"fmt"
)

// 切片序列化
func testSlice()  {
	// 切片内有多个map
	var slice []map[string]interface{}
	var m1 map[string]interface{}
	m1 = make(map[string]interface{})
	m1["name"]="sudada"
	m1["age"]="18"
	m1["skill"]="丢丢"
	slice = append(slice, m1)
	fmt.Println(slice)
	// 返回值:[map[age:18 name:sudada skill:丢丢]]

	// 切片序列化
	data, err := json.Marshal(slice)
	if err != nil {
		fmt.Println("序列化失败:",err)
	}
	fmt.Println(string(data))
	// 返回值:[{"age":"18","name":"sudada","skill":"丢丢"}]
}

func main() {
	testSlice()
}

3.4、反序列化(将json格式的数据,转换为"结构体,map,切片")

在反序列化一个json数据时,要确保反序列化后的数据类型原来序列化前的数据类型一致。

反序列化快速入门:err := json.Unmarshal([]byte(str),&slice)

str == json格式的数据

slice == 被反序列化的格式(结构体,map,切片)

3.4.1、结构体-反序列化

json数据的key,需要和结构体字段保持完全一致

package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	Name string
	Age int
	Birthday string
	Skill string
}

func test()  {
    // 结构体变量
	var monster Monster
	// 将json格式的数据,反序列化为struct
	str := "{\"name\":\"sudada\",\"age\":18,\"birthday\":\"2000-06-11\",\"skill\":\"牛牛冲击\"}"
	
    // 反序列化
	err := json.Unmarshal([]byte(str), &monster)
	if err != nil {
		fmt.Println("反序列化失败",err)
	}
	fmt.Println(monster) // 返回值:{sudada 18 2000-06-11 牛牛冲击}
}

func main() {
	test()
}

3.4.2、map-反序列化

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	// 将json格式的数据,反序列化为map
	str := "{\"age\":\"18\",\"name\":\"sudada\",\"skill\":\"丢丢\"}"

	// 定义map
	var a map[string]interface{}

	// 反序列化map时,不需要make,因为make操作在Unmarshal函数内已经自动执行了
	err := json.Unmarshal([]byte(str),&a)
	if err != nil {
		fmt.Println("反序列化失败",err)
	}
	fmt.Println(a) // 返回值:map[age:18 name:sudada skill:丢丢]
}

3.4.2、切片-反序列化

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	// 将json格式的数据,反序列化为切片
	str := "[{\"age\":\"18\",\"name\":\"sudada\",\"skill\":\"丢丢\"}]"

	// 定义切片
	var slice []map[string]interface{}

	// 反序列化切片
	err := json.Unmarshal([]byte(str),&slice)
	if err != nil {
		fmt.Println("反序列化失败",err)
	}
	fmt.Println(slice) // 返回值:[map[age:18 name:sudada skill:丢丢]]
}

四、反射

4.1、什么是反射?

1.反射可以在运行时,动态获取变量的各种信息,比如变量的类型,类别。

你可能感兴趣的:(golang)