七天入门Go语言】 文件 && 包 | 第五天

网友投稿 547 2022-05-29

目录

1. 文件处理

1.1 JSON文件

1.1.1 已知JSON结构

1.1.2 未知JSON结构

1.1.3 Encoder & Decoder

1.2 XML文件

1.3 二进制文件

1.4 zip文件

1.4.1 创建zip

1.4.2 读取zip文件

2. 包管理

2.1 包路径

2.2 包声明

最后

本章节主要介绍go语言的文件处理与包管理

1. 文件处理

1.1 JSON文件

什么是json?

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。

也是在web开发中的前后端交互的格式。

encoding/json是官方提供的标准 json, 实现 RFC 7159 中定义的 JSON 编码和解码。

使用的时候需要预定义 struct,原理是通过 reflection 和 interface 来完成工作。

常用的接口:

func Marshal(v interface{}) ([]byte, error) // 生成 JSON func Unmarshal(data []byte, v interface{}) error // 解析 JSON 到 struct

1

2

1.1.1 已知JSON结构

先看例子

package main import ( "encoding/json" "fmt" ) type Person struct { Name string Age string } type PersonSlice struct { Persons []Person } func main() { var s PersonSlice str := `{"persons":[{"Name":"FanOne","Age":"17"},{"Name":"FanOne2","Age":"18"},{"Name":"FanOne3","Age":"19"}]}` _ = json.Unmarshal([]byte(str), &s) // Golang中提供软件包"encoding/json"可以直接用来处理JSON文件,此包中解析JSON的函数为Unmarshal // 使用此函数可以将JSON文件解析到结构体中 fmt.Println(s.Persons)//[{FanOne 17} {FanOne2 18} {FanOne3 19}] for _,item:=range s.Persons{ fmt.Println("Name",item.Name,"Age",item.Age) //Name FanOne Age 17 //Name FanOne2 Age 18 //Name FanOne3 Age 19 } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

【七天入门Go语言】 文件 && 包 | 第五天

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

上例中,首先定义了与json数据对应的结构体,数组对应slice,字段名对应JSON里面的KEY,

在解析的时候,如何将json数据与struct字段相匹配呢?例如JSON的key是Name,那么怎么找对应的字段呢?

首先查找tag含有Name的可导出的struct字段(首字母大写)

其次查找字段名是Name的导出字段

最后查找类似NAME或者NaMe这样的除了首字母之外其他大小写不敏感的导出字段

其中需要注意一点:能够被赋值的字段必须是可导出字段(即首字母大写)。因为只有首字母大写才能被外面应用,同时JSON解析的时候只会解析能找得到的字段,找不到的字段会被忽略。

这样的一个好处是:当你接收到一个很大的JSON数据结构而你却只想获取其中的部分数据的时候,你只需将你想要的数据对应的字段名大写,即可轻松解决这个问题。

虽然没有python直接.json那么方便,但是也还是算不错的。

1.1.2 未知JSON结构

众所周知,在Go语言中,interface{}可以用来存储任意数据类型的对象,此数据结构正好用于存储解析的未知结构的json数据的结果。

JSON包中采用map[string]interface{}和[]interface{}结构来存储任意的JSON对象和数组。

Go类型和JSON类型的对应关系如下:

bool 代表 JSON booleans,

float64 代表 JSON numbers,

string 代表 JSON strings,

nil 代表 JSON null.

b := []byte(`{ "Name": "FanOne", "School": ["FZU", "XCZX", "UUUU", "GuaguaSong", "HanTuo", "City", "FuZhou"], "Major": "BigData", "IsPublished": true, "Price": 9.99, "Sales": 1000000 }`) var r interface{} err := json.Unmarshal(b, &r)

1

2

3

4

5

6

7

8

9

10

11

在上述代码中,r 被定义为一个空接口。

json.Unmarshal()函数将一个 JSON 对象解码

到空接口 r 中,最终 r 将会是一个键值对的map[string]interface{}结构:

map[string]interface{}{ "Name": "FanOne", "School": ["FZU", "XCZX", "UUUU", "GuaguaSong", "HanTuo", "City", "FuZhou"], "Major": "BigData", "IsPublished": true, "Price": 9.99, "Sales": 1000000 }

1

2

3

4

5

6

7

8

9

要访问解码后的数据结构,需要先判断目标结构是否为预期的数据类型:

gobook, ok := r.(map[string]interface{})

然后,我们可以通过 for 循环搭配 range 语句一一访问解码后的目标数据:

if ok { for k, v := range gobook { switch v2 := v.(type) { case string: fmt.Println(k, "is string", v2) case int: fmt.Println(k, "is int", v2) case bool: fmt.Println(k, "is bool", v2) case []interface{}: fmt.Println(k, "is an array:") for i, iv := range v2 { fmt.Println(i, iv) } default: fmt.Println(k, "is another type not handle yet") } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

虽然有些烦琐,但的确是一种解码未知结构的 JSON 数据的安全方式。

1.1.3 Encoder & Decoder

Go 内建的 encoding/json 包还提供 Decoder 和 Encoder 两个类型,用于支持 JSON 数据的流式读写,并提供 NewDecoder()和 NewEncoder()两个函数来便于具体实现:

func NewDecoder(r io.Reader) *Decoder func NewEncoder(w io.Writer) *Encoder

1

2

func main() { dec := json.NewDecoder(os.Stdin) enc := json.NewEncoder(os.Stdout) for { var v map[string]interface{} if err := dec.Decode(&v); err != nil{ log.Println(err) return } for k := range v { if k != "Name" { v[k] = nil,false } } if err := enc.Encode(&v); err != nil{ log.Println(err) } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

使用 Decoder 和 Encoder 对数据流进行处理可以应用得更为广泛些,比如读写 HTTP 连接、WebSocket 或文件等,Go 的标准库 net/rpc/jsonrpc 就是一个应用了 Decoder 和 Encoder的实际例子。

1.2 XML文件

XML 数据格式

对于如下的XML:

Fan One

1

2

3

4

和 JSON 的方式一样,XML 数据可以序列化为结构,或者从结构反序列化为 XML 数据;

encoding/xml包实现了一个简单的 XML 解析器(SAX),用来解析 XML 数据内容。下面的例子说明如何使用解析器:

复制代码 // xml.go package main import ( "encoding/xml" "fmt" "strings" ) var t, token xml.Token var err error func main() { input := "FanOne" inputReader := strings.NewReader(input) p := xml.NewDecoder(inputReader) for t, err = p.Token(); err == nil; t, err = p.Token() { switch token := t.(type) { case xml.StartElement: name := token.Name.Local fmt.Printf("Token name: %s\n", name) for _, attr := range token.Attr { attrName := attr.Name.Local attrValue := attr.Value fmt.Printf("An attribute is: %s %s\n", attrName, attrValue) } case xml.EndElement: fmt.Println("End of token") case xml.CharData: content := string([]byte(token)) fmt.Printf("This is the content: %v\n", content) // ... default: // ... } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

输出:

Token name: Person Token name: FirstName This is the content: Fan End of token Token name: LastName This is the content: One End of token End of token

1

2

3

4

5

6

7

8

包中定义了若干XML 标签类型:StartElement,Chardata(这是从开始标签到结束标签之间的实际文本)EndElement,Comment,Directive 或 ProcInst。

包中同样定义了一个结构解析器:

NewParser 方法持有一个 io.Reader(这里具体类型是strings.NewReader)并生成一个解析器类型的对象。

还有一个 Token() 方法返回输入流里的下一个 XML token。在输入流的结尾处,会返回(nil,io.EOF)

XML 文本被循环处理直到 Token() 返回一个错误,因为已经到达文件尾部,再没有内容可供处理了。

通过一个 type-switch 可以根据一些 XML 标签进一步处理。Chardata中的内容只是一个 []byte,通过字符串转换让其变得可读性强一些。

1.3 二进制文件

go语言可以在win下进行如下的设置将go程序build成二进制文件

set CGO_ENABLED = 0 set GOOS = linux set GOARCH = amd64 go build main.go

1

2

3

4

1.4 zip文件

1.4.1 创建zip

Go语言提供了archive/zip包来处理zip压缩文件

func createZip(filename string) { // 缓存压缩文件内容 buf := new(bytes.Buffer) // 创建zip writer := zip.NewWriter(buf) defer writer.Close() // 读取文件内容 content, _ := ioutil.ReadFile(filepath.Clean(filename)) // 接收 f, _ := writer.Create(filename) f.Write(content) filename = strings.TrimSuffix(filename, path.Ext(filename)) + ".zip" ioutil.WriteFile(filename, buf.Bytes(), 0644) }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

1.4.2 读取zip文件

读取zip文档过程与创建zip文档过程类似,需要解压后的文档目录结构创建:

func readZip(filename string) { zipFile, err := zip.OpenReader(filename) // 打开zip文件 if err != nil { panic(err.Error()) } defer zipFile.Close() for _, f := range zipFile.File { // 循环读取zip中的内容 info := f.FileInfo() if info.IsDir() { err = os.MkdirAll(f.Name, os.ModePerm) if err != nil { panic(err.Error()) } continue } srcFile, err := f.Open() // 打开文件 if err != nil { panic(err.Error()) } defer srcFile.Close() newFile, err := os.Create(f.Name) if err != nil { panic(err.Error()) } defer newFile.Close() io.Copy(newFile, srcFile) } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

2. 包管理

2.1 包路径

每一个包都通过一个唯一的字符串进行标识,它称为导入路径,他们用在import声明当中。

对于准备共享或公开的包需要全局唯一。当然也要保证没有循环的导包,循环的导包会引起报错,而这也就涉及到了程序项目的整体层次结构上了,这点以后再说。

2.2 包声明

在每一个Go源文件的路径的最后一段,需要进行声明。主要目的是当该包被其他包引入的时候作为默认的标识符。

例如在引入 "fmt"之后,可以访问到它的成员,fmt.Println(),可以注意到这个P是大写的,说明了,要大写才能跨包引用。

当我们导用的包的名字没有在文件中引用的时候,就会有一个编译错误。我们可以使用_来代表

表示导入的内容为空白标识符。

最后

小生凡一,期待你的关注。

Go JSON

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Docker之快速搞定容器数据卷
下一篇:Lucene高性能索引之道
相关文章