Go語言基礎學習二
一點經驗:
main()函數作爲整個程序的入口,沒有參數,也沒有返回值,還有一個函數init()先於main()執行,以後待講。
導入包中fmt提供格式化文本和讀入格式文本的函數,os包提供了跨平臺的操作系統層面變量及函數,包括保存命令行參數的類型os.Args strings包提供處理字符串的函數。
go程序中不含有分號,分號由編譯器自動添加。
filepath.Base()函數會返回傳入路徑的基礎名,Go語言採用單引號表達字符,Go語言具有強類型轉換,將int16與int32不能直接相加,需要進行類型轉換。
棧-自定義類型及其方法,自定義棧可以通過空接口來實現將異構(類型不同的)的元素混合存儲,因爲go語言的所有類型都實現了空接口。
func (stack *Stack) Top() (interface{},error){
return stack[len(stack)-1],nil
} 函數的聲明,第一個圓括號 stack *Stack表示一個類型爲指向Stack的參數,名爲stack,最後一個圓括號表示返回的值,返回兩個值,一個是空接口,一個是錯誤信息。
切片(在我的理解,就是數組),切片的內容可以通過索引來表示[first:end]一定範圍的值。
buffo包提供了帶緩衝的I/O處理能力,io包提供了底層I/O能力,包含有io.Reader及io.Writer接口,io/ioutil包提供了一系列高級文件處理函數。regexp包則提供了強大的正則表達式支持。
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"strings"
)
var britishAmerican = "british-american.txt"
func init() {/*進入函數main()之前,首先要執行init()內部的內容,獲取當前目錄並構建單詞轉化所需的文本路徑*/
dir, _ := filepath.Split(os.Args[0])
britishAmerican = filepath.Join(dir, britishAmerican)
}
func main() {
inFilename, outFilename, err := filenamesFromCommandLine()
/*調用filenamesFromCommandLine()函數獲取命令行中的數據輸入文件和輸出文件,返回值爲輸入文件名和輸出文件名及錯誤信息*/
if err != nil {/*當錯誤信息不爲空時,輸出並退出程序*/
fmt.Println(err)
os.Exit(1)
}
inFile, outFile := os.Stdin, os.Stdout
if inFilename != "" {
/*打開只讀輸入文件,返回指向文件的指針和錯誤信息*/
if inFile, err = os.Open(inFilename); err != nil {
log.Fatal(err)
}
/*defer是指推遲執行,直到main()函數正常返回會調用該執行語句,關閉文件,回收內存,當程序崩潰時,不會執行,但是go語言系統會自動關閉所有打開的文件,垃圾回收器會回收內存*/
defer inFile.Close()
}
if outFilename != "" {
/*os.Create()讀取一個文件,若文件不存在創建該文件,若存在,將該文件長度截爲0*/
if outFile, err = os.Create(outFilename); err != nil {
log.Fatal(err)
}
defer outFile.Close()
}
/*americanise該函數用於替換英式單詞爲美式單詞*/
if err = americanise(inFile, outFile); err != nil {
log.Fatal(err)
}
}
/*該函數用於分析命令行*/
func filenamesFromCommandLine() (inFilename, outFilename string,
err error) {
/*os.Args保存有命令行參數,命令行參數大於1,且可以分析-h及--help等參數爲命令解釋請求*/
if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
err = fmt.Errorf("usage: %s [<]infile.txt [>]outfile.txt",
filepath.Base(os.Args[0]))
return "", "", err
}
if len(os.Args) > 1 {
inFilename = os.Args[1]
if len(os.Args) > 2 {
outFilename = os.Args[2]
}
}
if inFilename != "" && inFilename == outFilename {
log.Fatal("won't overwrite the infile")
}
return inFilename, outFilename, nil
}
/*該函數實現替換英式單詞爲美式單詞*/
func americanise(inFile io.Reader, outFile io.Writer) (err error) {
/*向bufio.NewReader傳入實現io.Rreader接口的值,提供緩衝機制*/
reader := bufio.NewReader(inFile)
writer := bufio.NewWriter(outFile)
/*延遲至main()函數返回,刷新寫緩衝區,確保全部寫入*/
defer func() {
if err == nil {
err = writer.Flush()
}
}()
/*定義一個替換器函數,參數爲string,返回string*/
var replacer func(string) string
/*makeReplacerFunction根據單詞替換文本britishAmerican生成替換器replacer*/
if replacer, err = makeReplacerFunction(britishAmerican); err != nil {
return err
}
/*正則表達式*/
wordRx := regexp.MustCompile("[A-Za-z]+")
eof := false
/*循環讀取文件,直到讀到文件末尾*/
for !eof {
var line string
line, err = reader.ReadString('\n')
if err == io.EOF {
err = nil // io.EOF isn't really an error
eof = true // this will end the loop at the next iteration
} else if err != nil {
return err // finish immediately for real errors
}
/*對讀取的每一行進行替換器替換並寫入輸出文件*/
line = wordRx.ReplaceAllStringFunc(line, replacer)
/*這裏的_表示空標記符,作爲一個佔位符放在一個需要變量的地方,並忽略掉該變量的值。*/
if _, err = writer.WriteString(line); err != nil {
return err
}
}
return nil
}
/*根據單詞替換文本生成替換器replacer*/
func makeReplacerFunction(file string) (func(string) string, error) {
/*首先讀取單詞替換文本*/
rawBytes, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
text := string(rawBytes)
/*創建一個映射對象*/
usForBritish := make(map[string]string)
/*將文本逐行分解*/
lines := strings.Split(text, "\n")
for _, line := range lines {
/*每一行將根據空格符生成對應切片,strings.Fields這個函數就是以空白分割符來分隔字符串*/
fields := strings.Fields(line)
if len(fields) == 2 {
/*將切片填充到映射對象中去*/
usForBritish[fields[0]] = fields[1]
}
}
return func(word string) string {
/*根據映射對象的映射關係生成替換器*/
if usWord, found := usForBritish[word]; found {
return usWord
}
return word
}, nil
}
go語言中字符串類型的內部表示統一爲utf-8編碼的。
go語言中的映射,切片和通道都是要通過make()函數來創建的。
for。。range語法用於遍歷切片和數組非常方便,每次迭代它會返回切片的索引和在該切片上的元素值。