《golang筆記》第三篇-輸入輸出

BackSlash發表於2018-10-27

    處理輸入的簡單程式都有一個差不多的設計結構:一個處理輸入的迴圈,在每個元素上執行計算處理,在處理的同時或最後產生輸出。

示例程式碼(以stdin作為輸入):

// Dup prints the text of each line that appears more than
// once in the standard input, preceded by its count.
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	//分行讀取標準輸入s,將s放入map統計出現次數
	input := bufio.NewScanner(os.Stdin)
	statistic := make(map[string]int)
	for input.Scan() {
		line := input.Text()
		statistic[line]++
	}
	//列印出現重複的輸入
	for line, num := range statistic {
		if num > 1 {
			fmt.Printf("%s\t%d\n", line, num)
		}
	}
}
複製程式碼

input := bufio.NewScanner(os.Stdin) os.Stdin是標準輸入描述結構,類似C語言中的FILE。處理輸入的時候將該結構使用bufio包中的Scanner型別進行包裝。每次呼叫input.Scan(),即讀入下一行,並移除行末的換行符,讀取的內容可以使用input.Text()獲取。

statistic := make(map[string]int) 這條語句定義了一個map變數。map是儲存了鍵/值(key/value)的集合,對集合元素,提供常數時間的存、取或測試操作。鍵可以是任意型別,只要其值能用==運算子比較,最常見的例子是字串。值可以是任意型別。這個例子中的鍵是字串,值是整數。內建函式make建立空map,此外還有別的作用。make有些神奇,它的引數是什麼形式的?以後觀察。

示例程式碼(以檔案作為輸入):

// Dup2 prints the count and text of lines that appear more than once
// in the input.  It reads from stdin file
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	//讀取檔名
	var file string
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, "Paramater number error!")
		return
	} else {
		file = os.Args[1]
	}
	//開啟檔案
	f, err := os.Open(file)
	if err != nil {
		fmt.Fprintf(os.Stderr, "open file error: %v\n", err)
		return
	}

	//分行讀取檔案,將內容放入map統計出現次數
	input := bufio.NewScanner(f)
	statistic := make(map[string]int)
	for input.Scan() {
		line := input.Text()
		statistic[line]++
	}
	f.Close()

	//列印出現重複的輸入
	for line, num := range statistic {
		if num > 1 {
			fmt.Printf("%s\t%d\n", line, num)
		}
	}
}
複製程式碼

os.Open 函式返回兩個值。第一個值是被開啟的檔案(*os.File),其後被Scanner讀取。

os.Open 返回的第二個值是內建error型別的值。如果err等於內建值nil,那麼檔案被成功開啟。讀取檔案,直到檔案結束,然後呼叫Close關閉該檔案,並釋放佔用的所有資源。相反的話,如果err的值不是nil,說明開啟檔案時出錯了。這種情況下,錯誤值描述了所遇到的問題。我們的錯誤處理非常簡單,只是使用Fprintf與表示任意型別預設格式值的動詞%v,向標準錯誤流列印一條資訊。

Go中錯誤處理在所有的高階語言中簡直是一股清流啊,這其實貼合Go的開發思想,簡單、直觀。事實上,我們在設計程式碼邏輯的時候,本來就是在考慮兩種情況,成功了往下走,失敗了怎麼辦?我們在開發產品的時候,本質上在不斷取捨,在可能出現錯誤的地方用分支語句處理就可以了,既符合邏輯又直白簡單。

以上兩種讀取輸入的方式都是使用的“流”模式,並根據需要拆分成多個行。理論上,這些程式可以處理任意數量的輸入資料。還有另一個方法,就是一口氣把全部輸入資料讀到記憶體中,一次分割為多行,然後處理它們。

示例程式碼(以檔案作為輸入):

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

func main() {
	//讀取檔名
	var file string
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, "Paramater number error!")
		return
	} else {
		file = os.Args[1]
	}

	//讀取檔案所有內容
	data, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Fprintf(os.Stderr, "open file error: %v\n", err)
		return
	}

	//分行統計資料
	content := string(data)
	statistic := make(map[string]int)
	for _, line := range strings.Split(content, "\r\n") {
		statistic[line]++
	}

	//列印出現重複的輸入
	for line, num := range statistic {
		if num > 1 {
			fmt.Printf("%s\t%d\n", line, num)
		}
	}
}
複製程式碼

ReadFile 函式返回一個位元組切片(byte slice),必須把它轉換為string,才能用strings.Split分割。實現上,bufio.Scannerioutil.ReadFile ioutil.WriteFIle 都使用 *os.FileReadWrite 方法,但是,大多數程式設計師很少需要直接呼叫那些低階函式。高階函式,像 bufioio/ioutil 包中所提供的那些,用起來要容易點


相關文章