W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
原文鏈接:https://gopl-zh.github.io/ch10/ch10-05.html
如果只是導(dǎo)入一個(gè)包而并不使用導(dǎo)入的包將會導(dǎo)致一個(gè)編譯錯(cuò)誤。但是有時(shí)候我們只是想利用導(dǎo)入包而產(chǎn)生的副作用:它會計(jì)算包級變量的初始化表達(dá)式和執(zhí)行導(dǎo)入包的init初始化函數(shù)(§2.6.2)。這時(shí)候我們需要抑制“unused import”編譯錯(cuò)誤,我們可以用下劃線_
來重命名導(dǎo)入的包。像往常一樣,下劃線_
為空白標(biāo)識符,并不能被訪問。
import _ "image/png" // register PNG decoder
這個(gè)被稱為包的匿名導(dǎo)入。它通常是用來實(shí)現(xiàn)一個(gè)編譯時(shí)機(jī)制,然后通過在main主程序入口選擇性地導(dǎo)入附加的包。首先,讓我們看看如何使用該特性,然后再看看它是如何工作的。
標(biāo)準(zhǔn)庫的image圖像包包含了一個(gè)Decode
函數(shù),用于從io.Reader
接口讀取數(shù)據(jù)并解碼圖像,它調(diào)用底層注冊的圖像解碼器來完成任務(wù),然后返回image.Image類型的圖像。使用image.Decode
很容易編寫一個(gè)圖像格式的轉(zhuǎn)換工具,讀取一種格式的圖像,然后編碼為另一種圖像格式:
gopl.io/ch10/jpeg
// The jpeg command reads a PNG image from the standard input
// and writes it as a JPEG image to the standard output.
package main
import (
"fmt"
"image"
"image/jpeg"
_ "image/png" // register PNG decoder
"io"
"os"
)
func main() {
if err := toJPEG(os.Stdin, os.Stdout); err != nil {
fmt.Fprintf(os.Stderr, "jpeg: %v\n", err)
os.Exit(1)
}
}
func toJPEG(in io.Reader, out io.Writer) error {
img, kind, err := image.Decode(in)
if err != nil {
return err
}
fmt.Fprintln(os.Stderr, "Input format =", kind)
return jpeg.Encode(out, img, &jpeg.Options{Quality: 95})
}
如果我們將gopl.io/ch3/mandelbrot
(§3.3)的輸出導(dǎo)入到這個(gè)程序的標(biāo)準(zhǔn)輸入,它將解碼輸入的PNG格式圖像,然后轉(zhuǎn)換為JPEG格式的圖像輸出(圖3.3)。
$ go build gopl.io/ch3/mandelbrot
$ go build gopl.io/ch10/jpeg
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
Input format = png
要注意image/png包的匿名導(dǎo)入語句。如果沒有這一行語句,程序依然可以編譯和運(yùn)行,但是它將不能正確識別和解碼PNG格式的圖像:
$ go build gopl.io/ch10/jpeg
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
jpeg: image: unknown format
下面的代碼演示了它的工作機(jī)制。標(biāo)準(zhǔn)庫還提供了GIF、PNG和JPEG等格式圖像的解碼器,用戶也可以提供自己的解碼器,但是為了保持程序體積較小,很多解碼器并沒有被全部包含,除非是明確需要支持的格式。image.Decode函數(shù)在解碼時(shí)會依次查詢支持的格式列表。每個(gè)格式驅(qū)動列表的每個(gè)入口指定了四件事情:格式的名稱;一個(gè)用于描述這種圖像數(shù)據(jù)開頭部分模式的字符串,用于解碼器檢測識別;一個(gè)Decode函數(shù)用于完成解碼圖像工作;一個(gè)DecodeConfig函數(shù)用于解碼圖像的大小和顏色空間的信息。每個(gè)驅(qū)動入口是通過調(diào)用image.RegisterFormat函數(shù)注冊,一般是在每個(gè)格式包的init初始化函數(shù)中調(diào)用,例如image/png包是這樣注冊的:
package png // image/png
func Decode(r io.Reader) (image.Image, error)
func DecodeConfig(r io.Reader) (image.Config, error)
func init() {
const pngHeader = "\x89PNG\r\n\x1a\n"
image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
}
最終的效果是,主程序只需要匿名導(dǎo)入特定圖像驅(qū)動包就可以用image.Decode解碼對應(yīng)格式的圖像了。
數(shù)據(jù)庫包database/sql也是采用了類似的技術(shù),讓用戶可以根據(jù)自己需要選擇導(dǎo)入必要的數(shù)據(jù)庫驅(qū)動。例如:
import (
"database/sql"
_ "github.com/lib/pq" // enable support for Postgres
_ "github.com/go-sql-driver/mysql" // enable support for MySQL
)
db, err = sql.Open("postgres", dbname) // OK
db, err = sql.Open("mysql", dbname) // OK
db, err = sql.Open("sqlite3", dbname) // returns error: unknown driver "sqlite3"
練習(xí) 10.1: 擴(kuò)展jpeg程序,以支持任意圖像格式之間的相互轉(zhuǎn)換,使用image.Decode檢測支持的格式類型,然后通過flag命令行標(biāo)志參數(shù)選擇輸出的格式。
練習(xí) 10.2: 設(shè)計(jì)一個(gè)通用的壓縮文件讀取框架,用來讀取ZIP(archive/zip)和POSIX tar(archive/tar)格式壓縮的文檔。使用類似上面的注冊技術(shù)來擴(kuò)展支持不同的壓縮格式,然后根據(jù)需要通過匿名導(dǎo)入選擇導(dǎo)入要支持的壓縮格式的驅(qū)動包。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: