Go語言七篇入門教程五文件及包
1. 文件處理
1.1 JSON文件
什么是json?
JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式。
也是在web開發(fā)中的前后端交互的格式。
encoding/json是官方提供的標(biāo)準(zhǔn) json, 實現(xiàn) RFC 7159 中定義的 JSON 編碼和解碼。
使用的時候需要預(yù)定義 struct,原理是通過 reflection 和 interface 來完成工作。
常用的接口:
func Marshal(v interface{}) ([]byte, error) // 生成 JSON
func Unmarshal(data []byte, v interface{}) error // 解析 JSON 到 struct
1.1.1 已知JSON結(jié)構(gòu)
先看例子
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的函數(shù)為Unmarshal
// 使用此函數(shù)可以將JSON文件解析到結(jié)構(gòu)體中
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數(shù)據(jù)對應(yīng)的結(jié)構(gòu)體,數(shù)組對應(yīng)slice,字段名對應(yīng)JSON里面的KEY,
在解析的時候,如何將json數(shù)據(jù)與struct字段相匹配呢?例如JSON的key是Name,那么怎么找對應(yīng)的字段呢?
- 首先查找tag含有Name的可導(dǎo)出的struct字段(首字母大寫)
- 其次查找字段名是Name的導(dǎo)出字段
- 最后查找類似NAME或者NaMe這樣的除了首字母之外其他大小寫不敏感的導(dǎo)出字段
其中需要注意一點:能夠被賦值的字段必須是可導(dǎo)出字段(即首字母大寫)。因為只有首字母大寫才能被外面應(yīng)用,同時JSON解析的時候只會解析能找得到的字段,找不到的字段會被忽略。
這樣的一個好處是:當(dāng)你接收到一個很大的JSON數(shù)據(jù)結(jié)構(gòu)而你卻只想獲取其中的部分?jǐn)?shù)據(jù)的時候,你只需將你想要的數(shù)據(jù)對應(yīng)的字段名大寫,即可輕松解決這個問題。
雖然沒有python直接.json那么方便,但是也還是算不錯的。
1.1.2 未知JSON結(jié)構(gòu)
眾所周知,在Go語言中,interface{}可以用來存儲任意數(shù)據(jù)類型的對象,此數(shù)據(jù)結(jié)構(gòu)正好用于存儲解析的未知結(jié)構(gòu)的json數(shù)據(jù)的結(jié)果。
JSON包中采用map[string]interface{}和[]interface{}結(jié)構(gòu)來存儲任意的JSON對象和數(shù)組。
Go類型和JSON類型的對應(yīng)關(guān)系如下:
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()函數(shù)將一個 JSON 對象解碼
到空接口 r 中,最終 r 將會是一個鍵值對的map[string]interface{}結(jié)構(gòu):
map[string]interface{}{
"Name": "FanOne",
"School": ["FZU", "XCZX", "UUUU", "GuaguaSong", "HanTuo",
"City", "FuZhou"],
"Major": "BigData",
"IsPublished": true,
"Price": 9.99,
"Sales": 1000000
}
要訪問解碼后的數(shù)據(jù)結(jié)構(gòu),需要先判斷目標(biāo)結(jié)構(gòu)是否為預(yù)期的數(shù)據(jù)類型:
gobook, ok := r.(map[string]interface{})
然后,我們可以通過 for 循環(huán)搭配 range 語句一一訪問解碼后的目標(biāo)數(shù)據(jù):
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")
}
}
}
雖然有些煩瑣,但的確是一種解碼未知結(jié)構(gòu)的 JSON 數(shù)據(jù)的安全方式。
1.1.3 Encoder & Decoder
Go 內(nèi)建的 encoding/json 包還提供 Decoder 和 Encoder 兩個類型,用于支持 JSON 數(shù)據(jù)的流式讀寫,并提供 NewDecoder()和 NewEncoder()兩個函數(shù)來便于具體實現(xiàn):
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 對數(shù)據(jù)流進(jìn)行處理可以應(yīng)用得更為廣泛些,比如讀寫 HTTP 連接、WebSocket 或文件等,Go 的標(biāo)準(zhǔn)庫 net/rpc/jsonrpc 就是一個應(yīng)用了 Decoder 和 Encoder的實際例子。
1.2 XML文件
XML 數(shù)據(jù)格式
對于如下的XML:
<Person>
<FirstName>Fan</FirstName>
<LastName>One</LastName>
</Person>
和 JSON 的方式一樣,XML 數(shù)據(jù)可以序列化為結(jié)構(gòu),或者從結(jié)構(gòu)反序列化為 XML 數(shù)據(jù);
encoding/xml包實現(xiàn)了一個簡單的 XML 解析器(SAX),用來解析 XML 數(shù)據(jù)內(nèi)容。下面的例子說明如何使用解析器:
復(fù)制代碼
// 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 標(biāo)簽類型:StartElement,Chardata(這是從開始標(biāo)簽到結(jié)束標(biāo)簽之間的實際文本)EndElement,Comment,Directive 或 ProcInst。
包中同樣定義了一個結(jié)構(gòu)解析器:
NewParser 方法持有一個 io.Reader(這里具體類型是strings.NewReader)并生成一個解析器類型的對象。
還有一個 Token() 方法返回輸入流里的下一個 XML token。在輸入流的結(jié)尾處,會返回(nil,io.EOF)
XML 文本被循環(huán)處理直到 Token() 返回一個錯誤,因為已經(jīng)到達(dá)文件尾部,再沒有內(nèi)容可供處理了。
通過一個 type-switch 可以根據(jù)一些 XML 標(biāo)簽進(jìn)一步處理。Chardata中的內(nèi)容只是一個 []byte,通過字符串轉(zhuǎn)換讓其變得可讀性強(qiáng)一些。
1.3 二進(jìn)制文件
go語言可以在win下進(jìn)行如下的設(shè)置將go程序build成二進(jìn)制文件

set CGO_ENABLED=0 set GOOS=linux set GOARCH=amd64 go build main.go
1.4 zip文件
1.4.1 創(chuàng)建zip
Go語言提供了archive/zip包來處理zip壓縮文件
func createZip(filename string) {
// 緩存壓縮文件內(nèi)容
buf := new(bytes.Buffer)
// 創(chuàng)建zip
writer := zip.NewWriter(buf)
defer writer.Close()
// 讀取文件內(nèi)容
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文檔過程與創(chuàng)建zip文檔過程類似,需要解壓后的文檔目錄結(jié)構(gòu)創(chuàng)建:
func readZip(filename string) {
zipFile, err := zip.OpenReader(filename) // 打開zip文件
if err != nil {
panic(err.Error())
}
defer zipFile.Close()
for _, f := range zipFile.File { // 循環(huán)讀取zip中的內(nèi)容
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 包路徑
每一個包都通過一個唯一的字符串進(jìn)行標(biāo)識,它稱為導(dǎo)入路徑,他們用在import聲明當(dāng)中。
對于準(zhǔn)備共享或公開的包需要全局唯一。當(dāng)然也要保證沒有循環(huán)的導(dǎo)包,循環(huán)的導(dǎo)包會引起報錯,而這也就涉及到了程序項目的整體層次結(jié)構(gòu)上了,這點以后再說。
2.2 包聲明
在每一個Go源文件的路徑的最后一段,需要進(jìn)行聲明。主要目的是當(dāng)該包被其他包引入的時候作為默認(rèn)的標(biāo)識符。
例如在引入 "fmt"之后,可以訪問到它的成員,fmt.Println(),可以注意到這個P是大寫的,說明了,要大寫才能跨包引用。
當(dāng)我們導(dǎo)用的包的名字沒有在文件中引用的時候,就會有一個編譯錯誤。我們可以使用_來代表
表示導(dǎo)入的內(nèi)容為空白標(biāo)識符。

以上就是Go語言七篇入門教程五文件及包的詳細(xì)內(nèi)容,更多關(guān)于Go語言文件及包的資料請關(guān)注腳本之家其它相關(guān)文章!
如何學(xué)習(xí)Go
如果你是小白,你可以這樣學(xué)習(xí)Go語言~
七篇入門Go語言
第一篇:Go簡介初識
第二篇:程序結(jié)構(gòu)&&數(shù)據(jù)類型的介紹
第三篇:函數(shù)方法接口的介紹
第六篇:網(wǎng)絡(luò)編程
第七篇:GC垃圾回收三色標(biāo)記
相關(guān)文章
Go語言開發(fā)環(huán)境搭建與初探(Windows平臺下)
Go是Google開發(fā)的一種編譯型,並發(fā)型,并具有垃圾回收功能的編程語言,可能很多人想學(xué)習(xí)go語言,那么首先就要了解go語言的環(huán)境配置方法2014-10-10
Go語言Swagger實現(xiàn)為項目生成 API 文檔
Swagger 是一個基于 OpenAPI 規(guī)范設(shè)計的工具,用于為 RESTful API 生成交互式文檔,下面小編就來介紹一下如何在 Go 項目中集成 Swagger,特別是結(jié)合 Gin 框架生成 API 文檔2025-03-03

