2016-01-08 07:36:55 +00:00
|
|
|
|
## 10.5. 包的匿名導入
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
如果隻是導入一個包而併不使用導入的包將會導致一個編譯錯誤。但是有時候我們隻是想利用導入包而産生的副作用:它會計算包級變量的初始化表達式和執行導入包的init初始化函數(§2.6.2)。這時候我們需要抑製“unused import”編譯錯誤,我們可以用下劃線`_`來重命名導入的包。像往常一樣,下劃線`_`爲空白標識符,併不能被訪問。
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```Go
|
|
|
|
|
import _ "image/png" // register PNG decoder
|
|
|
|
|
```
|
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
這個被稱爲包的匿名導入。它通常是用來實現一個編譯時機製,然後通過在main主程序入口選擇性地導入附加的包。首先,讓我們看看如何使用該特性,然後再看看它是如何工作的。
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
標準庫的image圖像包包含了一個`Decode`函數,用於從`io.Reader`接口讀取數據併解碼圖像,它調用底層註冊的圖像解碼器來完成任務,然後返迴image.Image類型的圖像。使用`image.Decode`很容易編寫一個圖像格式的轉換工具,讀取一種格式的圖像,然後編碼爲另一種圖像格式:
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
2016-01-20 15:08:50 +00:00
|
|
|
|
<u><i>gopl.io/ch10/jpeg</i></u>
|
2015-12-09 07:45:11 +00:00
|
|
|
|
```Go
|
|
|
|
|
// 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})
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
如果我們將`gopl.io/ch3/mandelbrot`(§3.3)的輸出導入到這個程序的標準輸入,它將解碼輸入的PNG格式圖像,然後轉換爲JPEG格式的圖像輸出(圖3.3)。
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ go build gopl.io/ch3/mandelbrot
|
|
|
|
|
$ go build gopl.io/ch10/jpeg
|
|
|
|
|
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
|
|
|
|
|
Input format = png
|
|
|
|
|
```
|
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
要註意image/png包的匿名導入語句。如果沒有這一行語句,程序依然可以編譯和運行,但是它將不能正確識别和解碼PNG格式的圖像:
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ go build gopl.io/ch10/jpeg
|
|
|
|
|
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
|
|
|
|
|
jpeg: image: unknown format
|
|
|
|
|
```
|
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
下面的代碼演示了它的工作機製。標準庫還提供了GIF、PNG和JPEG等格式圖像的解碼器,用戶也可以提供自己的解碼器,但是爲了保持程序體積較小,很多解碼器併沒有被全部包含,除非是明確需要支持的格式。image.Decode函數在解碼時會依次査詢支持的格式列表。每個格式驅動列表的每個入口指定了四件事情:格式的名稱;一個用於描述這種圖像數據開頭部分模式的字符串,用於解碼器檢測識别;一個Decode函數用於完成解碼圖像工作;一個DecodeConfig函數用於解碼圖像的大小和顔色空間的信息。每個驅動入口是通過調用image.RegisterFormat函數註冊,一般是在每個格式包的init初始化函數中調用,例如image/png包是這樣註冊的:
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```Go
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
最終的效果是,主程序隻需要匿名導入特定圖像驅動包就可以用image.Decode解碼對應格式的圖像了。
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
數據庫包database/sql也是采用了類似的技術,讓用戶可以根據自己需要選擇導入必要的數據庫驅動。例如:
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
|
|
|
|
```Go
|
|
|
|
|
import (
|
2016-01-11 02:17:54 +00:00
|
|
|
|
"database/sql"
|
2015-12-09 07:45:11 +00:00
|
|
|
|
_ "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"
|
|
|
|
|
```
|
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
**練習 10.1:** 擴展jpeg程序,以支持任意圖像格式之間的相互轉換,使用image.Decode檢測支持的格式類型,然後通過flag命令行標誌參數選擇輸出的格式。
|
2015-12-09 07:45:11 +00:00
|
|
|
|
|
2016-01-08 07:36:55 +00:00
|
|
|
|
**練習 10.2:** 設計一個通用的壓縮文件讀取框架,用來讀取ZIP(archive/zip)和POSIX tar(archive/tar)格式壓縮的文檔。使用類似上面的註冊技術來擴展支持不同的壓縮格式,然後根據需要通過匿名導入選擇導入要支持的壓縮格式的驅動包。
|