新聞中心
對一個程序非常普遍的需求包括維護內(nèi)部 數(shù)據(jù)結(jié)構(gòu),為數(shù)據(jù)交換提供導入導出功能,也支持使用外部工具來處理數(shù)據(jù)。

蘇家屯網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)公司!從網(wǎng)頁設計、網(wǎng)站建設、微信開發(fā)、APP開發(fā)、響應式網(wǎng)站設計等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)公司公司2013年成立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設就選創(chuàng)新互聯(lián)公司。
由于我們這里的關(guān)注重點是文件處理,因此我們純粹只關(guān)心如何從程序內(nèi)部數(shù)據(jù)結(jié)構(gòu)中讀取數(shù)據(jù)并將其寫入標準和自定義格式的文件中,以及如何從標準和自定義格式文件中讀取數(shù)據(jù)并寫入程序的內(nèi)部數(shù)據(jù)結(jié)構(gòu)中。
本節(jié)中,我們會為所有的例子使用相同的數(shù)據(jù),以便直接比較不同的文件格式。所有的代碼都來自 invoicedate 程序(在 invoicedata 目錄中的 invoicedata.go > gob.go、inv.go、jsn.go、txt.go 和 xml.go 等文件中)。大家可以從我的網(wǎng)盤(鏈接: https://pan.baidu.com/s/1j22QfIScihrauVCVFV6MWw 提取碼: ajrk)下載相關(guān)的代碼。
該程序接受兩個文件名作為命令行參數(shù),一個用于讀,另一個用于寫(它們必須是不同的文件)。程序從第一個文件中讀取數(shù)據(jù)(以其后綴所表示的任何格式),并將數(shù)據(jù)寫入第二個文件(也是以其后綴所表示的任何格式)。
由 invoicedata 程序創(chuàng)建的文件可跨平臺使用,也就是說,無論是什么格式,Windows 上創(chuàng)建的文件都可在 Mac OS X 以及 Linux 上讀取,反之亦然。Gzip 格式壓縮的文件(如 invoices.gob.gz)可以無縫讀寫。
這些數(shù)據(jù)由一個 []invoice 組成,也就是說,是一個保存了指向 Invoice 值的指針的切片。每一個發(fā)票數(shù)據(jù)都保存在一個 invoice 類型的值中,同時每一個發(fā)票數(shù)據(jù)都以 []*Item 的形式保存著 0 個或者多個項。
type Invoice struct {
Id int
Customerld int
Raised time.Time
Due time.Time
Paid bool
Note string
Items []*Item
}
type Item struct {
Id st ring
Price float64
Quantity int
Note string
}
這兩個結(jié)構(gòu)體用于保存數(shù)據(jù)。下表給出了一些非正式的對比,展示了每種格式下讀寫相同的 50000 份隨機發(fā)票數(shù)據(jù)所需的時間,以及以該格式所存儲文件的大小。
計時按秒計,并向上舍入到最近的十分之一秒。我們應該把計時結(jié)果認為是無絕對單位的,因為不同硬件以及不 同負載情況下該值都不盡相同。大小一欄以千字節(jié)(KB)算,該值在所有機器上應該都是相同的。
對于該數(shù)據(jù)集,雖然未壓縮文件的大小千差萬別,但壓縮文件的大小都驚人的相似。而代碼的 函數(shù)不包括所有格式通用的代碼(例如,那些用于壓縮和解壓縮以及定義結(jié)構(gòu)體的代碼)。
表:各種格式的速度以及大小對比
| 后綴 | 讀取 | 寫入 | 大小(KiB) | 讀/寫LOC | 格式 |
|---|---|---|---|---|---|
| .gob | 0.3 | 0.2 | 7948 | 21 + 11 =32 | Go二進制 |
| .gob.gz | 0.5 | 1.5 | 2589 | ||
| jsn | 4.5 | 2.2 | 16283 | 32+17 = 49 | JSON |
| .jsn.gz | 4.5 | 3.4 | 2678 | ||
| .xml | 6.7 | 1.2 | 18917 | 45 + 30 = 75 | XML |
| .xml.gz | 6.9 | 2.7 | 2730 | ||
| ..txt | 1.9 | 1.0 | 12375 | 86 + 53 = 139 | 純文本(UTF-8) |
| .txt.gz | 2.2 | 2.2 | 2514 | ||
| .inv | 1.7 | 3.5 | 7250 | 128 + 87 = 215 | 自定義二進制 |
| .inv.gz | 1.6 | 2.6 | 2400 |
這些讀寫時間和文件大小在我們的合理預期范圍內(nèi),除了純文本格式的讀寫異??熘?。這得益于 fmt 包優(yōu)秀的打印和掃描函數(shù),以及我們設計的易于解析的自定義文本格式。
對于 JSON 和 XML 格式,我們只簡單地存儲了日期部分而非存儲默認的 time.Time 值(一個 ISO-8601 日期/時間字符串),通過犧牲一些速度和增加一些額外代碼稍微減小了文件的大小。
例如,如果讓JSON代碼自己來處理time.Time值,它能夠運行得更快,并且其代碼行數(shù)與 Go語言二進制編碼差不多。
對于二進制數(shù)據(jù),Go語言的二進制格式是最便于使用的。它非??烨覙O端緊湊,所需的代碼非常少,并且相對容易適應數(shù)據(jù)的變化。然而,如果我們使用的自定義類型不原生支持 gob 編碼,我們必須讓該類型滿足 gob.Encoder 和 gob. Decoder 接口,這樣會導致 gob 格式的 讀寫相當?shù)寐?,并且文件大小也會膨脹?
對于可讀的數(shù)據(jù),XML 可能是最好使用的格式,特別是作為一種數(shù)據(jù)交換格式時非常有用。與處理 JSON 格式相比,處理 XML 格式需要更多行代碼。這是因為 Go [沒有一個 xml.Marshaler 接口,也因為我們這里使用了并行的數(shù)據(jù)類型 (XMLInvoice 和 XMLItem)來幫助映射 XML 數(shù)據(jù)和發(fā)票數(shù)據(jù)(invoice 和 Item)。
使用 XML 作為外部存儲格式的應用程序可能不需要并行的數(shù)據(jù)類型或者也不需要 invoicedata 程序這樣的 轉(zhuǎn)換,因此就有可能比 invoicedata 例子中所給出的更快,并且所需的代碼也更少。
除了讀寫速度和文件大小以及代碼行數(shù)之外,還有另一個問題值得考慮:格式的穩(wěn)健性。例如,如果我們?yōu)?Invoice 結(jié)構(gòu)體和 Item 結(jié)構(gòu)體添加了一個字段,那么就必須再改變文件的格式。我們的代碼適應讀寫新格式并繼續(xù)支持讀舊格式的難易程度如何?如果我們?yōu)槲募袷蕉x版本,這樣的變化就很容易被適應(會以本章一個練習的形式給岀),除了讓 JSON 格式同時適應讀寫新舊格式稍微復雜一點之外。
除了 Invoice 和 Item 結(jié)構(gòu)體之外,所有文件格式都共享以下常量:
const (
fileType = "INVOICES" //用于純文本格式
magicNumber = 0xl25D // 用于二進制格式
fileVersion = 100 //用于所有的格式
dataFormat = "2006-01-02" //必須總是使用該日期
)
magicNumber 用于唯一標記發(fā)票文件。fileVersion 用于標記發(fā)票文件的版本,該標記便于之后修改程序來適應數(shù)據(jù)格式的改變。dataFormat 稍后介紹,它表 示我們希望數(shù)據(jù)如何按照可讀的格式進行格式化。
同時,我們也創(chuàng)建了一對接口。
type InvoiceMarshaler interface {
Marshallnvoices(writer io.Writer, invoices []*Invoice) error
}
type InvoiceUnmarshaler interface {
Unmarshallnvoices(reader io.Reader) ([]*Invoice, error)
}
這樣做的目的是以統(tǒng)一的方式針對特定格式使用 reader 和 writer。例如,下列函數(shù)是 invoicedata 程序用來從一個打開的文件中讀取發(fā)票數(shù)據(jù)的。
func readinvoices(reader io.Reader, suffix string)([]*Invoice, error) {
var unmarshaler InvoicesUnmarshaler
switch suffix {
case ".gobn:
unmarshaler = GobMarshaler{}
case H.inv":
unmarshaler = InvMarshaler{}
case ,f. jsn", H. jsonn:
unmarshaler = JSONMarshaler{}
case ".txt”:
unmarshaler = TxtMarshaler{}
case ".xml":
unmarshaler = XMLMarshaler{}
}
if unmarshaler != nil {
return unmarshaler.Unmarshallnvoices(reader)
}
return nil, fmt.Errorf("unrecognized input suffix: %s", suffix)
}
其中,reader 是任何能夠滿足 io.Reader 接口的值,例如,一個打開的文件 ( 其類型為 *os . File)> 一個 gzip 解碼器 ( 其類型為 *gzip. Reader) 或者一個 string. Readero 字符串 suffix 是文件的后綴名 ( 從 .gz 文件中解壓之后)。
在接下來的小節(jié)中我們將會看到 GobMarshaler 和 InvMarshaler 等自定義的類型,它們提供了 MarshmlTnvoices() 和 Unmarshallnvoices() 方法 (因此滿足 InvoicesMarshaler 和 InvoicesUnmarshaler 接口)。
文章題目:創(chuàng)新互聯(lián)GO教程:Go語言自定義數(shù)據(jù)文件
網(wǎng)頁URL:http://www.fisionsoft.com.cn/article/dposohh.html


咨詢
建站咨詢
