Go內部培訓——節點解析11-20
阿新 • • 發佈:2018-12-20
11. Go Line Filters
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// 使用緩衝scanner來包裹無緩衝的`os.Stdin`可以讓我們
// 方便地使用`Scan`方法,這個方法會將scanner定位到下
// 一行的位置
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
// `Text`方法從輸入中返回當前行
ucl := strings.ToUpper(scanner.Text())
// 輸出轉換為大寫的行
fmt. Println(ucl)
}
// 在`Scan`過程中,檢查錯誤。檔案結束不會被當作一個錯誤
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
12. Go Panic
- Panic表示的意思就是有些意想不到的錯誤發生了。通常我們用來表示程式正常執行過程中不應該出現的,或者我們沒有處理好的錯誤。
package main
import "os"
func main() {
// 我們使用panic來檢查預期不到的錯誤
panic("a problem" )
// Panic的通常使用方法就是如果一個函式
// 返回一個我們不知道怎麼處理的錯誤的
// 時候,直接終止執行。
_, err := os.Create("/tmp/file")
if err != nil {
panic(err)
}
}
13. Go range函式
- Go range函式
- range函式是個神奇而有趣的內建函式,你可以使用它來遍歷陣列,切片和字典。
- 當用於遍歷陣列和切片的時候,range函式返回索引和元素;
- 當用於遍歷字典的時候,range函式返回字典的鍵和值。
package main
import "fmt"
func main( ) {
// 這裡我們使用range來計算一個切片的所有元素和
// 這種方法對陣列也適用
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
// range 用來遍歷陣列和切片的時候返回索引和元素值
// 如果我們不要關心索引可以使用一個下劃線(_)來忽略這個返回值
// 當然我們有的時候也需要這個索引
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
// 使用range來遍歷字典的時候,返回鍵值對。
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
// range函式用來遍歷字串時,返回Unicode程式碼點。
// 第一個返回值是每個字元的起始位元組的索引,第二個是字元程式碼點,
// 因為Go的字串是由位元組組成的,多個位元組組成一個rune型別字元。
for i, c := range "go" {
fmt.Println(i, c)
}
}
14. Go SHA1 雜湊
- SHA1雜湊經常用來計算二進位制或者大文字資料的短標識值。git版本控制系統用SHA1來標識受版本控制的檔案和目錄。這裡介紹Go中如何計算SHA1雜湊值。
package main
import "crypto/sha1"
import "fmt"
func main() {
s := "sha1 this string"
// 生成一個hash的模式是`sha1.New()`,`sha1.Write(bytes)`
// 然後是`sha1.Sum([]byte{})`,下面我們開始一個新的hash
// 示例
h := sha1.New()
// 寫入要hash的位元組,如果你的引數是字串,使用`[]byte(s)`
// 把它強制轉換為位元組陣列
h.Write([]byte(s))
// 這裡計算最終的hash值,Sum的引數是用來追加而外的位元組到要
// 計算的hash位元組裡面,一般來講,如果上面已經把需要hash的
// 位元組都寫入了,這裡就設為nil就可以了
bs := h.Sum(nil)
// SHA1雜湊值經常以16進位制的方式輸出,例如git commit就是
// 這樣,所以可以使用`%x`來將雜湊結果格式化為16進位制的字串
fmt.Println(s)
fmt.Printf("%x\n", bs)
}
15. Go String與Byte切片之間的轉換
- tring轉換到Byte陣列時,每個byte(byte型別其實就是uint8)儲存字串對應位元組的數值。注意Go的字串是UTF-8編碼的,每個字元長度是不確定的,一些字元可能是1、2、3或者4個位元組結尾。
package main
import "fmt"
func main() {
s1 := "abcd"
b1 := []byte(s1)
fmt.Println(b1) // [97 98 99 100]
s2 := "中文"
b2 := []byte(s2)
fmt.Println(b2) // [228 184 173 230 150 135], unicode,每個中文字元會由三個byte組成
r1 := []rune(s1)
fmt.Println(r1) // [97 98 99 100], 每個字一個數值
r2 := []rune(s2)
fmt.Println(r2) // [20013 25991], 每個字一個數值
}
16. Go Switch語句
- 當條件判斷分支太多的時候,我們會使用switch語句來優化邏輯。
package main
import "fmt"
import "time"
func main() {
// 基礎的switch用法
i := 2
fmt.Print("write ", i, " as ")
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
}
// 你可以使用逗號來在case中分開多個條件。還可以使用default語句,
// 當上面的case都沒有滿足的時候執行default所指定的邏輯塊。
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("it's the weekend")
default:
fmt.Println("it's a weekday")
}
// 當switch沒有跟表示式的時候,功能和if/else相同,這裡我們
// 還可以看到case後面的表示式不一定是常量。
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("it's before noon")
default:
fmt.Println("it's after noon")
}
}
17. Go URL解析
- URL提供了一種統一訪問資源的方式。我們來看一下Go裡面如何解析URL。
package main
import "fmt"
import "net/url"
import "strings"
func main() {
// 我們將解析這個URL,它包含了模式,驗證資訊,
// 主機,埠,路徑,查詢引數和查詢片段
s := "postgres://user:[email protected]:5432/path?k=v#f"
// 解析URL,並保證沒有錯誤
u, err := url.Parse(s)
if err != nil {
panic(err)
}
// 可以直接訪問解析後的模式
fmt.Println(u.Scheme)
// User包含了所有的驗證資訊,使用
// Username和Password來獲取單獨的資訊
fmt.Println(u.User)
fmt.Println(u.User.Username())
p, _ := u.User.Password()
fmt.Println(p)
// Host包含了主機名和埠,如果需要可以
// 手動分解主機名和埠
fmt.Println(u.Host)
h := strings.Split(u.Host, ":")
fmt.Println(h[0])
fmt.Println(h[1])
// 這裡我們解析出路徑和`#`後面的片段
fmt.Println(u.Path)
fmt.Println(u.Fragment)
// 為了得到`k=v`格式的查詢引數,使用RawQuery。你可以將
// 查詢引數解析到一個map裡面。這個map為字串作為key,
// 字串切片作為value。
fmt.Println(u.RawQuery)
m, _ := url.ParseQuery(u.RawQuery)
fmt.Println(m)
fmt.Println(m["k"][0])
}
18. Go 閉包函式
- Go支援匿名函式,匿名函式可以形成閉包。閉包函式可以訪問定義閉包的函式定義的內部變數。
package main
import "fmt"
// 這個"intSeq"函式返回另外一個在intSeq內部定義的匿名函式,
// 這個返回的匿名函式包住了變數i,從而形成了一個閉包
func intSeq() func() int {
i := 0
return func() int {
i += 1
return i
}
}
func main() {
// 我們呼叫intSeq函式,並且把結果賦值給一個函式nextInt,
// 這個nextInt函式擁有自己的i變數,這個變數每次呼叫都被更新。
// 這裡i的初始值是由intSeq呼叫的時候決定的。
nextInt := intSeq()
// 呼叫幾次nextInt,看看閉包的效果
fmt.Println(nextInt())
fmt.Println(nextInt())
fmt.Println(nextInt())
// 為了確認閉包的狀態是獨立於intSeq函式的,再建立一個。
newInts := intSeq()
fmt.Println(newInts())
}
package main
import "fmt"
func main() {
add10 := closure(10)//其實是構造了一個加10函式
fmt.Println(add10(5))
fmt.Println(add10(6))
add20 := closure(20)
fmt.Println(add20(5))
}
func closure(x int) func(y int) int {
return func(y int) int {
return x + y
}
}
package main
import "fmt"
func main() {
var fs []func() int
for i := 0; i < 3; i++ {
fs = append(fs, func() int {
return i
})
}
for _, f := range fs {
fmt.Printf("%p = %v\n", f, f())
}
}
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
result := adder()
for i := 0; i < 10; i++ {
fmt.Println(result(i))
}
}
19. Go變數
- Go是靜態型別語言,變數是有明確型別的。編譯器會檢查函式呼叫中,變數型別的正確性。
package main
import "fmt"
func main() {
// `var` 關鍵字用來定義一個或者多個變數
var a string = "initial"
fmt.Println(a)
// 你一次可以定義多個變數
var b, c int = 1, 2
fmt.Println(b, c)
// Go會推斷出具有初始值的變數的型別
var d = true
fmt.Println(d)
//定義變數時,沒有給出初始值的變數被預設初始化為零值
//整型的零值就是0
var e int
fmt.Println(e)
//":=" 語法是同時定義和初始化變數的快捷方式
f := "short"
fmt.Println(f)
}
20. Go 遍歷通道
- 我們知道range函式可以遍歷陣列,切片,字典等。這裡我們還可以使用range函式來遍歷通道以接收通道資料。
package main
import "fmt"
func main() {
// 我們遍歷queue通道里面的兩個資料
queue := make(chan string, 2)
queue <- "one"
queue <- "two"
close(queue)
// range函式遍歷每個從通道接收到的資料,因為queue再發送完兩個
// 資料之後就關閉了通道,所以這裡我們range函式在接收到兩個資料
// 之後就結束了。如果上面的queue通道不關閉,那麼range函式就不
// 會結束,從而在接收第三個資料的時候就阻塞了。
for elem := range queue {
fmt.Println(elem)
}
}