*本文笔记参考:b站【尚硅谷】Golang入门到实战教程

1、文件的基本介绍

文件是数据源(保存数据的地方)的一种,如word, excel, txt都是文件。文件最主要的作用就是保存数据

:数据在数据源(文件)和程序(内存)之间经历的路径。

输入流(读文件):数据从数据源(文件)到程序(内存)的路径。

输出流(写文件):数据从程序(内存)到数据源(文件)的路径。

2、打开、关闭文件
//打开文件
file, err := os.Open("E:/study/Go_WorkSpace/src/code/test1/test.txt")
if err != nil {
    fmt.Println("open file error,err=", err)
}
//查看文件内容
fmt.Println("file: ", *file)  //file:  &{0xc00007c780}(可以看出,file是个指针)
//关闭文件
err = file.Close()
if err != nil {
    fmt.Println("close file error,err=", err)
}
3、读取文件

1)带缓冲的Reader读取文件

os.Openfile.Close()bufio.NewReader()reader.ReadString
package main
​
import (
    "bufio"
    "fmt"
    "io"
    "os"
)
​
func main() {
    //打开文件
    file, err := os.Open("E:/study/Go_WorkSpace/src/code/test1/test.txt")
    if err != nil {
        fmt.Println("open file error,err=", err)
    }
​
    //当函数退出时,及时关闭file
    defer file.Close() //要及时关闭file句柄,否则会有内存泄漏
​
    //查看文件内容
    reader := bufio.NewReader(file) //创建一个reader,带缓冲的
    for {
        str, err := reader.ReadString('\n') //读到换行符就结束
        if err == io.EOF {                  //表示读到文件的末尾
            break
        }
        fmt.Print(str)
    }
    fmt.Println("文件读取结束")
}
//输出
hello,golang!
文件读取结束

2)一次性读取文件

读取文件的内容并显示在终端(使用ioutil一次将整个文件读入到内存中),这种方式适用于文件不大的情况。相关方法和函数(ioutil.ReadFile)

package main
​
import (
    "fmt"
    "io/ioutil"
)
​
func main() {
    file := "E:/study/Go_WorkSpace/src/code/test1/test.txt"
    //查看文件内容
    content, err := ioutil.ReadFile(file) //创建一个reader,带缓冲的
    if err != nil {
        fmt.Println("读取文件失败,err=", err)
    }
    fmt.Print(string(content))
    fmt.Println("文件读取结束")
    //因为没有显示的Open文件,所以也不需要显示的Close文件
    //文件的Open和Close被封装到 ReadFile 函数内部
}
//输出
hello,golang!
文件读取结束
4、写文件

1)创建一个新文件,写入内容:5句"hello,golang!"

package main
​
import (
    "bufio"
    "fmt"
    "os"
)
​
func main() {
    //1.打开文件
    filePath := "E:/study/Go_WorkSpace/src/code/test1/test01.txt"
    file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
    if err != nil {
        fmt.Println("打开文件失败,err=", err)
    }
    //2.写入5句"hello,golang!"
    str := "hello,golang!\n"
    writer := bufio.NewWriter(file)
    for i := 0; i < 5; i++ {
        writer.WriteString(str)
    }
    //3.关闭文件
    defer file.Close()
    //4.writer是带缓存,在调用WriteString方法时,内容是先写入缓存的
    //需要调用Flush方法将缓存的数据落盘
    writer.Flush()
}
//E:/study/Go_WorkSpace/src/code/test1/test01.txt
hello,golang!
hello,golang!
hello,golang!
hello,golang!
hello,golang!
​

2)打开一个已存在的文件,把原来的内容覆盖为10句“hello, world!”

package main
​
import (
    "bufio"
    "fmt"
    "os"
)
​
func main() {
    //1.打开文件
    filePath := "E:/study/Go_WorkSpace/src/code/test1/test01.txt"
    file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0666)
    if err != nil {
        fmt.Println("打开文件失败,err=", err)
    }
    //2.写入5句"hello,golang!"
    str := "hello, world!\n"
    writer := bufio.NewWriter(file)
    for i := 0; i < 10; i++ {
        writer.WriteString(str)
    }
    //3.关闭文件
    defer file.Close()
    //4.writer是带缓存,在调用WriteString方法时,内容是先写入缓存的
    //需要调用Flush方法将缓存的数据落盘
    writer.Flush()
}
//输出
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!

3)打开一个已存在的文件,在原来的内容后追加"abc! english!"

package main

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

func main() {
	//1.打开文件
	filePath := "E:/study/Go_WorkSpace/src/code/test1/test01.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		fmt.Println("打开文件失败,err=", err)
	}
	//2.写入5句"hello,golang!"
	str := "abc! english!\n"
	writer := bufio.NewWriter(file)
	writer.WriteString(str)
	//3.关闭文件
	defer file.Close()
	//4.writer是带缓存,在调用WriteString方法时,内容是先写入缓存的
	//需要调用Flush方法将缓存的数据落盘
	writer.Flush()
}
//输出
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
abc! english!

4)打开一个已存在的文件,将原来的内容读出显示在终端,并且追加5句"hello,上海!"

package main

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

func main() {
	//1.打开文件
	filePath := "E:/study/Go_WorkSpace/src/code/test1/test01.txt"
	file, err := os.OpenFile(filePath, os.O_RDWR|os.O_APPEND, 0666)
	if err != nil {
		fmt.Println("打开文件失败,err=", err)
	}
	reader := bufio.NewReader(file)
	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF { //读取到文件的末尾
			break
		}
		fmt.Print(str)
	}
	//2.写入5句"hello,golang!"
	str := "hello,上海!\n"
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}
	//3.关闭文件
	defer file.Close()
	//4.writer是带缓存,在调用WriteString方法时,内容是先写入缓存的
	//需要调用Flush方法将缓存的数据落盘
	writer.Flush()
}
//终端输出
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
abc! english!
//test01.txt
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
hello, world!
abc! english!
hello,上海!
hello,上海!
hello,上海!
hello,上海!
hello,上海!

5)将一个已存在的文件的内容,写入到另一个已存在的文件中。

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	//1.将test01.txt的内容读取到内存
	filePath1 := "E:/study/Go_WorkSpace/src/code/test1/test01.txt"
	filePath2 := "E:/study/Go_WorkSpace/src/code/test1/test.txt"
	data, err := ioutil.ReadFile(filePath1)
	if err != nil {
		fmt.Println("读取文件失败,err=", err)
		return
	}

	//2.将内存的数据写到test.txt
	err = ioutil.WriteFile(filePath2, data, 0666)
	if err != nil {
		fmt.Println("write file error", err)
	}
}

6)判断文件是否存在

使用os.Stat()函数返回的错误值进行判断:

  • 返回的错误为nil,说明文件或文件夹存在;

  • 返回的错误类型使用os.IsNotExit()判断为true,说明文件或文件夹不存在;

  • 返回的错误为其他类型,则不确定是否存在。

func PathExits(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil { //文件或目录存在
		return true, nil
	}
	if os.IsNotExist(err) { //文件或目录不存在
		return false, nil
	}
	return false, err //不确定文件或目录是否存在
}
5、应用实例

1)拷贝文件(图片视频音频)

将一张图片/视频/音频拷贝到另一个文件中。

将“C:\Users\lichan\Pictures\背景.jpg”拷贝到“‪E:\study\Go_WorkSpace\src\code\test1\picture.jpg”中

package main

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

//接收两个文件路径srcFileName, dstFileName
func CopyFile(dstFileName string, srcFileName string) (written int64, err error) {
	//打开srcFileName
	srcFile, err := os.Open(srcFileName)
	if err != nil {
		fmt.Println("open srcfile error,err=", err)
	}
	defer srcFile.Close()
	//通过srcFile,获取到reader
	reader := bufio.NewReader(srcFile)

	//打开dstFileName
	dstFile, err := os.OpenFile(dstFileName, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println("open dstfile error,err=", err)
	}
	//通过dstFile,获取到writer
	writer := bufio.NewWriter(dstFile)
	defer dstFile.Close()

	return io.Copy(writer, reader)
}

func main() {
	srcFileName := "C:\\Users\\lichan\\Pictures\\背景.jpg"
	dstFileName := "E:\\study\\Go_WorkSpace\\src\\code\\test1\\picture.jpg"
	_, err := CopyFile(dstFileName, srcFileName)
	if err != nil {
		fmt.Println("copy fail, error=", err)
	} else {
		fmt.Println("copy success!")
	}
}

2)统计不同类型的字符个数

package main

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

//定义一个结构体,用于保存统计结果
type CharCount struct {
	ChCount    int //英文个数
	NumCount   int //数字个数
	SpaceCount int //空格个数
	OtherCount int //其他字符的个数
}

func main() {
	//打开一个文件,创建一个reader
	fileName := "E:\\study\\Go_WorkSpace\\src\\code\\test1\\test.txt"
	file, err := os.Open(fileName)
	if err != nil {
		fmt.Println("open file error, error=", err)
	}
	defer file.Close()
	//定义一个CharCount实例
	var count CharCount
	reader := bufio.NewReader(file)

	//每读取一行,就去统计该行有多少个英文、数字、空格和其他字符
	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF { //读到文件末尾就退出
			break
		}
		//遍历str,统计不同类型的字符个数
		for _, v := range str {
			switch {
			case v >= 'a' && v <= 'z':
				fallthrough //穿透(强行执行下一层)
			case v >= 'A' && v <= 'Z':
				count.ChCount++
			case v == ' ' || v == '\t':
				count.SpaceCount++
			case v >= '0' && v <= '9':
				count.NumCount++
			default:
				count.OtherCount++
			}
		}
	}
	fmt.Printf("字母个数:%v\n空格个数:%v\n数字个数:%v\n其他个数:%v\n",
		count.ChCount, count.SpaceCount, count.NumCount, count.OtherCount)
}
6、命令行参数

编写一段代码,可以获取多个命令行参数。

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println("命令行的参数有:", len(os.Args))
	for i, v := range os.Args {
		fmt.Printf("args[%v]=%v\n", i, v)
	}
}
PS E:\study\Go_WorkSpace\src\code\test1\view> go build -o main.exe

PS E:\study\Go_WorkSpace\src\code\test1\view> .\main.exe 1 2 3 4  
命令行的参数有: 5
args[0]=E:\study\Go_WorkSpace\src\code\test1\view\main.exe
args[1]=1
args[2]=2
args[3]=3
args[4]=4

1)flag包解析命令行参数

可以不用按照顺序输入参数,程序会自动按照顺序来解析。

package main

import (
	"flag"
	"fmt"
)

func main() {
	//定义几个变量,用于接收命令行的参数值
	var user string
	var pwd string
	var host string
	var port int

	//&user,接收用户命令行中输入的 -u 后面的参数值
	flag.StringVar(&user, "u", "", "用户名,默认为空")
	flag.StringVar(&pwd, "p", "", "密码,默认为空")
	flag.StringVar(&host, "h", "localhost", "主机名,默认为localhost")
	flag.IntVar(&port, "port", 3306, "端口号,默认为3306")

	//从os.Args[1:]中解析注册的flag。必须在所有flag都注册好而未访问其值时执行
	flag.Parse()

	fmt.Printf("user=%v pwd=%v host=%v port=%v", user, pwd, host, port)
}
PS E:\study\Go_WorkSpace\src\code\test1\view> go build -o main.exe

PS E:\study\Go_WorkSpace\src\code\test1\view> .\main.exe -u lichan -p 123456 -h 127.0.0.1 -port=8080  
user=lichan pwd=123456 host=127.0.0.1 port=8080

PS E:\study\Go_WorkSpace\src\code\test1\view> .\main.exe -u lichan -port=8080 -p 123456 -h 127.0.0.1  
user=lichan pwd=123456 host=127.0.0.1 port=8080

2)json

json是一种轻量级的数据交换格式。

json易于机器解析和生成,并有效地提升网络传输效率,通常程序在网络传输时会先将数据(结构体、map等)序列化成 json 字符串,等接收方得到 json 字符串时,再反序列化恢复成原来的数据类型(结构体、map等)。

      序列化          网络传输    反序列化
golang----->json字符串------>程序------>其他语言

json通过键值对来保存数据。任何的数据类型都可以通过json来表示,如字符串、数字、对象、数组、map、结构体等。

{"name":"tom","age":18}
{
    "name":"tom",
    "age":18
}
[{"name":"tom","age":18},{"name":"tom","age":18}]
[
    {
        "name":"tom",
        "age":18
    },
    {
        "name":"tom",
        "age":18
    }
]

(1)json的序列化

  • map的序列化

package main

import (
	"encoding/json"
	"fmt"
)

func testMap() {
	var a map[string]interface{}
	a = make(map[string]interface{})
	a["name"] = "牛魔王"
	a["age"] = 8000
	a["address"] = "洪崖洞"

	//将monster序列化
	data, err := json.Marshal(a)
	if err != nil {
		fmt.Println("序列化错误,err=", err)
	}
	//输出序列化后的结果
	fmt.Println("monster序列化后=", string(data))
}

func main() {
	testMap()
}
//输出
monster序列化后= {"address":"洪崖洞","age":8000,"name":"牛魔王"}
  • 切片的序列化

package main

import (
	"encoding/json"
	"fmt"
)

func testSlice() {
	var slice []map[string]interface{}
	var slice1 map[string]interface{}
	slice1 = make(map[string]interface{})
	slice1["name"] = "牛魔王"
	slice1["age"] = 8000
	slice1["address"] = "洪崖洞"
	slice = append(slice, slice1)
	var slice2 map[string]interface{}
	slice2 = make(map[string]interface{})
	slice2["name"] = "孙悟空"
	slice2["age"] = 500
	slice2["address"] = "花果山"
	slice = append(slice, slice2)

	//将monster序列化
	data, err := json.Marshal(slice)
	if err != nil {
		fmt.Println("序列号错误,err=", err)
	}
	//输出序列化后的结果
	fmt.Println("monster序列化后=", string(data))
}

func main() {
	testSlice()
}
//输出
monster序列化后= [{"address":"洪崖洞","age":8000,"name":"牛魔王"},{"address":"花果山","age":500,"name":"孙悟空"}]
  • 结构体的序列化

package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	Name  string `json:monster_name` //反射机制
	Age   int    `json:monster_age`
	Skill string
}

func testStruct() {
	monster := Monster{"牛魔王", 500, "芭蕉扇"}

	//将monster序列化
	data, err := json.Marshal(monster)
	if err != nil {
		fmt.Println("序列号错误,err=", err)
	}
	//输出序列化后的结果
	fmt.Println("monster序列化后=", string(data))
}

func main() {
	testStruct()
}

对于结构体的序列化,如果希望序列化后key的名字由我们自己重新指定,可以给struct指定一个标签。

type Monster struct {
	Name  string `json:monster_name` //反射机制
	Age   int    `json:monster_age`
	Skill string
}

(2)json的反序列化

将json字符串反序列化成对应的数据类型(结构体、map、切片)

注意事项:

  1. 在反序列化一个json字符串时,要确保反序列化后的数据类型和原来序列化前的数据类型一致;

  2. 如果是通过程序获取到的字符串,将其进行反序列化,则不需要进行转义,因为程序内部会进行转义;如果是将自己写的str进行反序列化,记得要进行转义,加转义符\。

  • 将json字符串反序列化成struct

package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	Name  string `json:monster_name` //反射机制
	Age   int    `json:monster_age`
	Skill string
}

//将json字符串反序列化成struct
func unmarshalStruct() {
	//在项目开发中,str是通过网络传输或读取文件得到的
	str := "{\"Name\":\"牛魔王\", \"Age\":500, \"Skill\":\"芭蕉扇\"}"

	//定义一个Monster实例
	var monster Monster
	err := json.Unmarshal([]byte(str), &monster)
	if err != nil {
		fmt.Println("unmarshal fail, err=", err)
	}
	fmt.Println("反序列化后:", monster)
}

func main() {
	unmarshalStruct()
}
//输出
反序列化后: {牛魔王 500 芭蕉扇}
  • 将json字符串反序列化成map

package main

import (
	"encoding/json"
	"fmt"
)

//将json字符串反序列化成map
func unmarshalMap() {
	//在项目开发中,str是通过网络传输或读取文件得到的
	str := "{\"Name\":\"牛魔王\", \"Age\":500, \"Skill\":\"芭蕉扇\"}"

	//定义一个map
	var monster map[string]interface{}
	//反序列化map,不需要make。因为make操作被封装到unmarshal中
	err := json.Unmarshal([]byte(str), &monster)
	if err != nil {
		fmt.Println("unmarshal fail, err=", err)
	}
	fmt.Println("反序列化后:", monster)
}

func main() {
	unmarshalMap()
}
//输出
反序列化后: map[Age:500 Name:牛魔王 Skill:芭蕉扇]
  • 将json字符串反序列化成slice

package main

import (
	"encoding/json"
	"fmt"
)

//将json字符串反序列化成map
func unmarshalSlice() {
	//在项目开发中,str是通过网络传输或读取文件得到的
	str := "[{\"Name\":\"牛魔王\", \"Age\":500, \"Skill\":\"芭蕉扇\"},{\"Name\":\"孙悟空\", \"Age\":50, \"Skill\":\"七十二变\"}]"
	//换行的写法
	// str := "[{\"Name\":\"牛魔王\", \"Age\":500, \"Skill\":\"芭蕉扇\"}," +
	// "{\"Name\":\"孙悟空\", \"Age\":50, \"Skill\":\"七十二变\"}]"

	//定义一个map
	var slice []map[string]interface{}
	//反序列化map,不需要make。因为make操作被封装到unmarshal中
	err := json.Unmarshal([]byte(str), &slice)
	if err != nil {
		fmt.Println("unmarshal fail, err=", err)
	}
	fmt.Println("反序列化后:", slice)
}

func main() {
	unmarshalSlice()
}
//输出
反序列化后: [map[Age:500 Name:牛魔王 Skill:芭蕉扇] map[Age:50 Name:孙悟空 Skill:七十二变]]