1. 程式人生 > >Go內部培訓——節點解析11-20

Go內部培訓——節點解析11-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)
}
}