【七天入门Go语言】 文件 && 包 | 第五天-阿里云开发者社区

开发者社区> 小生凡一> 正文

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

简介: 目录 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 包声明 最后
+关注继续查看

目录

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 包声明

最后

image.png


本章节主要介绍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.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
    }
}

上例中,首先定义了与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)

在上述代码中,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
    }

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

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")
            }
        }
    }

虽然有些烦琐,但的确是一种解码未知结构的 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


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)
        }
    }
}

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


1.2 XML文件

XML 数据格式

对于如下的XML:


<Person>
    <FirstName>Fan</FirstName>
    <LastName>One</LastName>
</Person>

和 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 := "<Person><FirstName>Fan</FirstName><LastName>One</LastName></Person>"
    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:
            // ...
        }
    }
}


输出:


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


包中定义了若干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.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.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)
        }
}

2. 包管理

2.1 包路径

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

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


2.2 包声明

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


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


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


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

image.png


最后

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

image.png


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
9206 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
2902 0
阿里云服务器ECS远程登录用户名密码查询方法
阿里云服务器ECS远程连接登录输入用户名和密码,阿里云没有默认密码,如果购买时没设置需要先重置实例密码,Windows用户名是administrator,Linux账号是root,阿小云来详细说下阿里云服务器远程登录连接用户名和密码查询方法
11117 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
10762 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
11998 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
11784 0
腾讯云服务器 设置ngxin + fastdfs +tomcat 开机自启动
在tomcat中新建一个可以启动的 .sh 脚本文件 /usr/local/tomcat7/bin/ export JAVA_HOME=/usr/local/java/jdk7 export PATH=$JAVA_HOME/bin/:$PATH export CLASSPATH=.
4599 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
3956 0
+关注
小生凡一
你好呀!
157
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载