主題 達人專欄

用 Golang 掃描圖片裡的 QR code,同時學習套件管理

解凍豬腳 | 2021-09-21 19:15:01 | 巴幣 3698 | 人氣 722

 
假如你是一名建築工,也許你會需要鋼筋、水泥、板模、磁磚,還有各式各樣的建築材料。這些材料總是從特定的廠商買回來,而不是由你自己來製造的。平常寫程式的時候也是一樣,只要遇上了稍微複雜一點的需求,我們通常都不會希望所有的環節都僅僅憑著一己之力從頭做起。

比如說,我們想要用 Golang 寫一個讀取網頁內容的工具(網路爬蟲),那麼我們除了向目標網址送出 HTTP request、接收對方回傳的 response 以外,我們還會需要專門用來解析 HTML 格式內容的工具,才能快速地拆解並得到我們需要的內容。這種時候我們就可以引用網路上各個熱心的神人製作並提供的套件,也就是所謂的「第三方函式庫」。

不過畢竟網路爬蟲會花費比較多時間,有機會再專門寫一篇。今天就先來講講 Golang 的 package 機制,然後弄個簡單的小程式來讀取電腦裡現有圖片的 QR code 吧。


► Golang package

在 Golang,我們在撰寫任何程式碼之前都必須事先規定好它們所屬的 package(包)。上次示範 Hello, world! 的時候,你有沒有注意到程式碼的第一行寫著「package main」呢?沒有錯,它的意義就代表了這個檔案的程式碼屬於「main」這個包。

首先建立一個簡單的專案吧!隨意新增一個資料夾,用 VS Code 打開資料夾,並且新增兩個檔案:
main.go
my_math.go

接著簡單寫個把兩數相加的函式,試著執行:


可以從這邊觀察到,即便它們被寫在不同檔案裡,只要是屬於同一個包的函式,我們仍然可以直接引用。換句話說,我們可以為求乾淨而把同一個 package 的東西分類成好幾個不同的部分,分別寫在不同的檔案裡面,只要確保它們屬於同一個包就可以了。

由於 Golang 本身規定,當我們在編譯、執行程式碼的時候會找 main 包裡面的 main 函式來執行,所以我們這種小專案裡面一定都是先定義 package main,至於 package 的其他用法,我們就留到以後再慢慢講。


► 建立 go module

剛才隨手寫的 my_math.go 可以砍了。接下來我們使用 go module 的 init 功能來替這個專案建立第三方函式庫的引用列表,執行指令:
go mod init go_example


你會注意到系統幫你產生了 go.mod 檔案,負責記錄你引用了哪些第三方的 package(以及它們本身的版本)。這裡的 go_example 你可以粗略地理解成專案的名稱,以現在而言這個名字不算重要,想取什麼都可以。建立好 go module 以後,我們就可以下載別人做的 package 來用了。

執行指令:
go get "github.com/makiuchi-d/gozxing"

我們就成功從 makiuchi-d 這個網友的 GitHub 下載了他提供的 QR code 函式庫。你通常可以在 GOPATH 底下的 pkg\mod\ 或者是 GOPATH 底下的 src 資料夾找到你剛利用 go get 從別人的遠端儲存庫下載下來的 package 原始碼內容。


► 照著說明文件做

下一步就是直接到他的 repository 頁面:

照著作者提供的說明文件或範例做做看:

func main() {
    // open and decode image file
    file, _ := os.Open("qrcode.jpg")
    img, _, _ := image.Decode(file)

    // prepare BinaryBitmap
    bmp, _ := gozxing.NewBinaryBitmapFromImage(img)

    // decode image
    qrReader := qrcode.NewQRCodeReader()
    result, _ := qrReader.Decode(bmp, nil)

    fmt.Println(result)
}

這裡要注意,go module 管理的是「整個專案」所用到的第三方 package,而我們除了把 package 利用 go get 安裝、整理進去以外,還要在程式原始碼檔案裡再經過一步 import,才能正式地被引用。

你的開發環境偶爾笨一點,它也許沒辦法辨認程式碼裡面的 gozxing 和 qrcode 是分別來自於:
github.com/makiuchi-d/gozxing
github.com/makiuchi-d/gozxing/qrcode

這導致它不會在你 Ctrl+S 的時候自動把引用到的函式庫完全加到 main.go 的 import 列表裡,我們就需要手動添加上去:

package main

import (
    "fmt"
    "image"
    "os"
    
    _ "image/jpeg"

    "github.com/makiuchi-d/gozxing"
    "github.com/makiuchi-d/gozxing/qrcode"
)

func main() {
    // open and decode image file
    file, _ := os.Open("qrcode.jpg")
    img, _, _ := image.Decode(file)

    // prepare BinaryBitmap
    bmp, _ := gozxing.NewBinaryBitmapFromImage(img)

    // decode image
    qrReader := qrcode.NewQRCodeReader()
    result, _ := qrReader.Decode(bmp, nil)

    fmt.Println(result)
}

除此之外,使用任何函式庫的時候,也要稍微瞭解一下函式庫本身的特性,才能避免踩到不必要的坑。像是 Golang 官方提供的 image 包本身只是一個圖片解析的框架,我們需要再利用 image/jpeg 來讓程式有能力去解析 jpeg 格式(即 jpg 檔案)的圖片。

當我們 import 函式庫的時候,如果 package 名稱前面加上底線,代表我們只是想執行這個 package 裡面的 init() 函數而已,沒有要完全引入。以 image/jpeg 本身的性質來說,我們只要讓程式執行過它裡面的 init() 就可以讓 image 的包有能力去解析 jpg 圖片了,如果有需要用到 image/jpeg 裡其他的功能,才需要把底線拿掉。

如果這個情境下沒有加上底線,而你又沒有真的用到 image/jpeg 裡的其他函式,那系統就有可能會在 Ctrl+S 的時候誤認為你沒有要使用這個 package 而把它從 import 列表拿掉,所以底線在這個範例裡是必要的。如果仍然沒能搞懂的話,先照著別人的範例,跟著做就對了。


► 實際執行

我們先隨便準備一個 jpeg 格式的 QR code:


儲存下來取名為 qrcode.jpg,放到專案的資料夾裡面,接著執行:


我們就成功地利用第三方函式庫把一個 QR code 圖片裡的內容解析出來了。


► Go module 小技巧

有的時候我們可能不是像這樣一步一步來新增,而是直接複製別人現有的程式碼。

當我們使用 go mod init 建立第三方函式庫依賴列表之後,只要再接著執行指令:
go mod tidy

Go module 工具就會自動幫你把專案資料夾底下把程式碼需要用到但還沒 get 的包通通都 get 進來,並且把目前函式庫列表裡多餘的包都拿掉,替你省掉很多步。假如你的開發環境告訴你 go.mod 有問題,tidy 就對了。


► 碎碎唸

其實早期的 Golang 版本,官方根本沒有提供像 go module 套件管理的功能,這導致我們當時還得用 govendor 或是 godep 來管理專案引用的 package 列表。我記得那時候用 govender 遇上的坑還一堆,至於是什麼樣的問題我則是連想都不敢回想了,對我這種菜雞來說要排除那些問題簡直是地獄。在 go module 逐漸成熟之後,起碼使用體驗比以前還要好上許多,不必再搞一堆麻煩的參數。

Golang 的社群還算活躍,只要不是太冷門的功能,應該不太容易有遇上問題卻求助無門的情形。基本上只要有了 go module 的基本概念,那麼下一步大概就只剩下怎麼把 code 寫得漂亮了。




縮圖素材原作者:Renée French(CC BY-SA 3.0)
送禮物贊助創作者 !
0
留言

創作回應

梧優
大佬
2021-09-21 20:59:40
追蹤 創作集

作者相關創作

更多創作