03 - 查詢重複行1 深一步瞭解go
阿新 • • 發佈:2019-01-14
思路
- 對檔案做拷貝、列印、搜尋、排序、統計或類似事情的程式都有一個差不多的程式結構:一個處理輸入的迴圈,在每個元素上執行計算處理,在處理的同時或最後產生輸出。
- 接下來根據 Linux 的 uniq 命令,其尋找響鈴的重複行,我們將會用 go 語言編寫三個版本的 dup, 方便我們更加詳細的詳解 go 語言的結構
例如:
// 輸入檔案
xiaomi
hello
world
xiaomi
hello
結果程式篩選後, 將獲得 xiaomi 出現了2次,hello 出現了2次,world出現了1次
// 輸出檔案 2 xiaomi 2 hello 1 world
一、dup1
根據我們的思路,我們首先通過標準輸入匯入程式當中,在每一輪迴圈當中,利用容器 map 的 key value結構形式,把字串作為 key, 其key出現的次數作為 value,然後累計統計。
- 而在 C/C++ 當中我們將會使用 std::map<std::string, int> 結構來統計,在 C/C++ 當中 map 的底層實現是使用了紅黑樹的資料結構特性,但是在 go 語言當中我們也使用 map[string]int 容器,但是其底層的資料結構使用的雜湊的原理,而每一次使用 for 訪問 map[string]int 容器而打印出來的值均不一樣。關於 go
- 該程式當中引入了 標準輸入,因此我們需要引入 bufio 包和 os 包,關於 bufio 包和 os 包的相關 api 可以參考英文文件 https://golang.org/pkg/ 和中文文件 https://studygolang.com/pkgdoc
我需要了解的原始碼如下:
package main
import (
"bufio"
"fmt"
"os"
)
func main(){
counts := make(map[string]int)
input := bufio.NewScanner (os.Stdin)
for input.Scan(){
counts[input.Text()]++
}
for line, n := range counts {
fmt.Printf("%d\t%s\n", n, line)
}
}
展示
- 路徑: $GOPATH/src/gopl/ch1/dup
$ go run dup1.go < input.txt
我們發現在第二次執行的時候列印的值的順序和其他的不一樣, 這是 go 的 map[string]int 容器的內部實現有關
二、程式分析
1. map
-
map 儲存了鍵/值(key/value)的集合,對集合元素,提供常數時間的存、取或測試操作。鍵可以是任意型別,只要其值能用==運算子比較,值則可以是任意型別。上述例子中的 key 是字串,value 是整數。內建函式make建立空map,此外,它還有別的作用。
-
在 C/C++ 和 go 中map的定義方式是不同的,在 C++ 中為std::map<std::string, int> 而在 go 中,則是map[string]int
-
關於 map[string]int 我們一般實用make 建立一個內建型別,並且返回 map[string]int 的 slice 型別,如果使用 var mp map[string]int方式宣告的變數只是一個 nil map, 將會產生 panic:assignment to entry in nil map
// 以下程式碼若放入 go 語言中執行將會產生panic 錯誤 var mp map[string]int mp["xiaomi"] = 0
2. bufio
- bufio包實現了有緩衝的 I/O*。它包裝一個io.Reader或io.Writer介面物件,建立另一個也實現了該介面,且同時還提供了緩衝和一些文字I/O的幫助函式的物件。
- func NewScanner(r io.Reader) *Scanner,傳入標準輸入 os.Stdin建立一個能重 r 中讀取資料的 Scanner,並且返回其中指標型別
- Scanner 型別提供了方便的讀取資料的介面,如從換行符分隔的文本里讀取每一行。成功呼叫的 Scan方法 會逐步提供檔案的 token,跳過 token 之間的位元組。token 由 SplitFunc型別 的分割函式指定;預設的分割函式會將輸入分割為多個行,並去掉行尾的換行標誌。本包預定義的分割函式可以將檔案分割為行、位元組、unicode碼值、空白分隔的word。 呼叫者可以定製自己的分割函式。
input := bufio.NewScanner(os.Stdin) // 建立一個能重 r 中讀取資料的 Scanner for input.Scan(){ counts[input.Text()]++ }
總結
- 充分利用好 go 語言的官方文件
- map 的使用
- https://golang.org/pkg/ 沒事多看看它