Go語言基礎05——異常、正則、字符串、json、文本文件處理

異常

Go語言引入了一個關于錯誤處理的標準模式,即error接口,它是Go語言內建的接口類型

package main

import "fmt"
import "errors"

func MyDiv(a, b int) (res int, err error) {
    if b == 0 {
        err = errors.New("除數不能為0")
        return
    }
    res = a / b
    return

}

func testa() {
    fmt.Println("aaaaaaaaaaa")
}
func testb() {
    panic("this is a pinic in testb.")
    fmt.Println("bbbbbbbbbbb")
}
func testc() {
    fmt.Println("ccccccccccc")
}
func testd() {
    arr := make([]int, 10)
    arr[10] = 10
}

func teste() {

    defer func() {
        if err := recover(); err != nil {
            fmt.Println("err in defer :", err) //err in defer : runtime error: index out of range
        }
    }()

    arr := make([]int, 10)
    arr[10] = 10
}
func main() {

    fmt.Println("異常演示案例")

    // 1. error接口的使用
    err1 := fmt.Errorf("%s", "this is normal error1")
    err2 := errors.New("this is normal error2")

    fmt.Println("error2:", err1) //this is normal error1
    fmt.Println("error2:", err2) //this is normal error2

    // 2. error接口的應用
    //res, err := MyDiv(10, 3) //res: 3
    res, err := MyDiv(10, 0) //error: 除數不能為0
    if err != nil {
        fmt.Println("error:", err)
    } else {
        fmt.Println("res:", res)
    }

    fmt.Println("---------------------")
    // 3.1 顯式調用panic函數
    testa()
    //testb()
    testc()

    // aaaaaaaaaaa
    // panic: this is a pinic in testb.
    // goroutine 1 [running]:

    // 3.2 數組越界導致panic
    fmt.Println("---------------------")
    //testd() //panic: runtime error: index out of range

    // 4. recover使用
    // 運行時panic異常一旦被引發就會導致程序崩潰。這當然不是我們愿意看到的,因為誰也不能保證程序不會發生任何運行時錯誤。
    //不過,Go語言為我們提供了專用于“攔截”運行時panic的內建函數——recover。它可以是當前的程序從運行時panic的狀態中恢復并重新獲得流程控制權。

    //注意:recover只有在defer調用的函數中有效

    //  如果調用了內置函數recover,并且定義該defer語句的函數發生了panic異常,recover會使程序從panic中恢復,并返回panic value。
    //導致panic異常的函數不會繼續運行,但能正常返回。在未發生panic時調用recover,recover會返回nil。

    testa()
    teste() //err in defer : runtime error: index out of range
    testc()

}

字符串處理

字符串在開發中經常用到,包括用戶的輸入,數據庫讀取的數據等,我們經常需要對字符串進行分割、連接、轉換等操作,我們可以通過Go標準庫中的strings和strconv兩個包中的函數進行相應的操作。
下面這些函數來自于strings包,這里介紹一些我平常經常用到的函數,更詳細的請參考官方的文檔。

Contains Join Index Repeat Replace Split Trim Fields

package main

import "fmt"
import "strings"

func main() {

    fmt.Println("字符串處理演示案例")
    // Contains Join Index Repeat Replace Split Trim Fields

    //Contains
    str1 := "hellogo"
    fmt.Println("str1 contains hello ?", (strings.Contains(str1, "hello"))) //true
    fmt.Println("str1 contains abc ?", (strings.Contains(str1, "abc")))     //false

    //Join
    str2 := []string{"a", "b", "c"}
    str3 := strings.Join(str2, "####")
    fmt.Println("str3:", str3) // a####b####c

    //Index
    fmt.Println("index:", strings.Index("abchello", "hello")) //3
    fmt.Println("index:", strings.Index("abchello", "xx"))    //-1

    //Repeat
    str4 := strings.Repeat("go", 3)
    fmt.Println("str4:", str4) //gogogo

    //Split
    str5 := "abc#123#hello"
    str6 := strings.Split(str5, "#")
    fmt.Println("str6:", str6) // [abc 123 hello]
    //Trim

    str7 := "     abc  123   hello     "
    str7 = strings.Trim(str7, " ")
    fmt.Println("str7:", str7) //abc  123   hello

    str7 = "----abc--123--hello----     "
    str7 = strings.Trim(str7, "-")
    fmt.Printf("str7:*%s*\n", str7) //*abc--123--hello----     *
    //Fields
    str8 := "     abc  123   hello     "
    str9 := strings.Fields(str8)
    fmt.Println("str9:", str9) // [abc 123 hello]

}

字符串轉換

package main

import "fmt"

import "strconv"

func main() {

    fmt.Println("字符串轉換演示案例")
    // Append Format Parse

    // Append:系列函數將整數等轉換為字符串后,添加到現有的字節數組中
    b := make([]byte, 0, 10)
    b = strconv.AppendBool(b, false)
    b = strconv.AppendInt(b, 123, 10) //以十進制追加
    b = strconv.AppendQuote(b, "abc")
    b = strconv.AppendQuoteRune(b, '中')
    fmt.Println("b = ", b)         //[102 97 108 115 101 49 50 51 34 97 98 99 34 39 228 184 173 39]
    fmt.Println("b = ", string(b)) // false123"abc"'中'

    //Format:系列函數把其他類型的轉換為字符串
    a := strconv.FormatBool(false)
    b1 := strconv.FormatInt(123, 10)
    c := strconv.FormatUint(12345, 10)
    d := strconv.Itoa(666)
    fmt.Println(a, b1, c, d) //false 123 12345 666

    fmt.Printf("%T,%T,%T,%T\n", a, b1, c, d) //string,string,string,string

    //Parse:系列函數把字符串轉換為其他類型

    r1, err := strconv.ParseBool("false")
    check(err)
    r2, err := strconv.ParseInt("123", 10, 64)
    check(err)
    r3, err := strconv.ParseUint("12345", 10, 64)
    check(err)
    r4, err := strconv.Atoi("666")
    check(err)

    fmt.Println(r1, r2, r3, r4)                 //false 123 12345 666
    fmt.Printf("%T,%T,%T,%T\n", r1, r2, r3, r4) //bool,int64,uint64,int

    r5, err := strconv.Atoi("66x6")
    check(err)      //err: strconv.Atoi: parsing "66x6": invalid syntax
    fmt.Println(r5) //0
}

func check(err error) {
    if err != nil {
        fmt.Println("err:", err)

    }
}

正則表達式

正則表達式是一種進行模式匹配和文本操縱的復雜而又強大的工具。雖然正則表達式比純粹的文本匹配效率低,但是它卻更靈活。按照它的語法規則,隨需構造出的匹配模式就能夠從原始文本中篩選出幾乎任何你想要得到的字符組合。

Go語言通過regexp標準包為正則表達式提供了官方支持,如果你已經使用過其他編程語言提供的正則相關功能,那么你應該對Go語言版本的不會太陌生,但是它們之間也有一些小的差異,因為Go實現的是RE2標準,除了\C,詳細的語法描述參考:http://code.google.com/p/re2/wiki/Syntax

其實字符串處理我們可以使用strings包來進行搜索(Contains、Index)、替換(Replace)和解析(Split、Join)等操作,但是這些都是簡單的字符串操作,他們的搜索都是大小寫敏感,而且固定的字符串,如果我們需要匹配可變的那種就沒辦法實現了,當然如果strings包能解決你的問題,那么就盡量使用它來解決。因為他們足夠簡單、而且性能和可讀性都會比正則好。

package main

import (
    "fmt"
    "regexp"
)

func main() {

    fmt.Println("正則表達式演示案例")
    buf := "abc azc a7c aac 888 a9c tac"
    //1、 提取 a.c 這樣的內容
    // 1)定義規則
    reg1 := regexp.MustCompile(`a.c`)
    if reg1 == nil { //解析失敗返回nil
        fmt.Println("regex err")
        return
    }
    //2)根據規則提取信息
    res := reg1.FindAllStringSubmatch(buf, -1) // 返回類型為 [][]string
    fmt.Println("res:", res)                   //[[abc] [azc] [a7c] [aac] [a9c]]
    res1 := reg1.FindAllStringSubmatch(buf, 1) // 返回類型為 [][]string
    fmt.Println("res1:", res1)                 //[[abc]]
    res2 := reg1.FindAllStringSubmatch(buf, 2) // 返回類型為 [][]string
    fmt.Println("res2:", res2)                 // [[abc] [azc]]

    //2、 提取有效小數:
    buf2 := "2.1 4.x 3.14 aac 7. .9 tac xx7.8p"
    reg2 := regexp.MustCompile(`\d+\.\d+`)
    if reg2 == nil {
        fmt.Println("regex err")
        return
    }
    res3 := reg2.FindAllStringSubmatch(buf2, -1)
    fmt.Println("res3:", res3) //[[2.1] [3.14] [7.8]]

    //2、獲取html中的div標簽內容
    //``   原生字符串
    buf3 := `
    
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>Go語言標準庫文檔中文版 | Go語言中文網 | Golang中文社區 | Golang中國</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
    <meta charset="utf-8">
    <link rel="shortcut icon" href="/static/img/go.ico">
    <link rel="apple-touch-icon" type="image/png" href="/static/img/logo2.png">
    <meta name="author" content="polaris <polaris@studygolang.com>">
    <meta name="keywords" content="中文, 文檔, 標準庫, Go語言,Golang,Go社區,Go中文社區,Golang中文社區,Go語言社區,Go語言學習,學習Go語言,Go語言學習園地,Golang 中國,Golang中國,Golang China, Go語言論壇, Go語言中文網">
    <meta name="description" content="Go語言文檔中文版,Go語言中文網,中國 Golang 社區,Go語言學習園地,致力于構建完善的 Golang 中文社區,Go語言愛好者的學習家園。分享 Go 語言知識,交流使用經驗">
</head>
    <div>和愛好</div>
    <div>哈哈
    你在嗎
    不在
    </div>
    <div>測試</div>
    <div>你過來啊</div>

<frameset cols="15,85">
    <frame src="/static/pkgdoc/i.html">
    <frame name="main" src="/static/pkgdoc/main.html" tppabs="main.html" >
    <noframes>
    </noframes>
</frameset>
</html>
    `

    reg3 := regexp.MustCompile(`<div>(.*)</div>`)
    if reg3 == nil {
        fmt.Println("regex err")
        return
    }
    res4 := reg3.FindAllStringSubmatch(buf3, -1)
    fmt.Println("res4:", res4) //[[<div>和愛好</div> 和愛好] [<div>測試</div> 測試] [<div>你過來啊</div> 你過來啊]]

    for k, v := range res4 {
        fmt.Println(k, v)
        // 0 [<div>和愛好</div> 和愛好]
        // 1 [<div>測試</div> 測試]
        // 2 [<div>你過來啊</div> 你過來啊]
    }

    for k, v := range res4 {
        fmt.Println(k, v[1])
        // 0 和愛好
        // 1 測試
        // 2 你過來啊

    }

    fmt.Println("-----------------")
    //優化

    reg4 := regexp.MustCompile(`<div>(?s:(.*?))</div>`)
    if reg4 == nil {
        fmt.Println("regex err")
        return
    }
    res5 := reg4.FindAllStringSubmatch(buf3, -1)
    fmt.Println("res5:", res5)
    /*
            res5: [[<div>和愛好</div> 和愛好] [<div>哈哈
                你在嗎
                不在
                </div> 哈哈
                你在嗎
                不在
                ] [<div>測試</div> 測試] [<div>你過來啊</div> 你過來啊]]

    */

    for k, v := range res5 {
        fmt.Println(k, v[1])
        // 0 和愛好
        // 1 哈哈
        //       你在嗎
        //       不在

        // 2 測試
        // 3 你過來啊

    }
}

json

JSON (JavaScript Object Notation)是一種比XML更輕量級的數據交換格式,在易于人們閱讀和編寫的同時,也易于程序解析和生成。盡管JSON是JavaScript的一個子集,但JSON采用完全獨立于編程語言的文本格式,且表現為鍵/值對集合的文本描述形式(類似一些編程語言中的字典結構),這使它成為較為理想的、跨平臺、跨語言的數據交換語言。

開發者可以用 JSON 傳輸簡單的字符串、數字、布爾值,也可以傳輸一個數組,或者一個更復雜的復合結構。在 Web 開發領域中, JSON被廣泛應用于 Web 服務端程序和客戶端之間的數據通信。

Go語言內建對JSON的支持。使用Go語言內置的encoding/json 標準庫,開發者可以輕松使用Go程序生成和解析JSON格式的數據。

JSON官方網站:http://www.json.org/

在線格式化:http://www.json.cn/

  1. 通過結構體生成json
    1.1 struct_tag的使用
  2. 通過map生成json
  3. json解析到結構體
  4. json解析到map
package main

import (
    "encoding/json"
    "fmt"
)

// 1. 通過結構體生成json
// 1.1 struct_tag的使用
// 2. 通過map生成json
// 3. json解析到結構體
// 4. json解析到map

//定義一個結構體類型,注意必須都要大寫
type Student struct {
    Id       int
    Name     string
    Age      int
    Sex      byte
    Addr     string
    Subjects []string
}

type IT struct {
    Company string   `json:"-"`               // 不會導出到json中
    Subject []string `json:"subject"`         // 二次編碼,key會變成小寫
    IsOk    bool     `json:",string"`         //轉換成字符串然后再輸出
    Price   float64  `json:"price,omitempty"` //如果Pricer為空,則不會輸出
}

type Book struct {
    Company string   `json:"company"`
    Subject []string `json:"subject"`
    IsOk    bool     `json:"isok"`
    Price   float64  `json:"price"`
}

type Book2 struct {
    Subject []string `json:"subject"`
}

func main() {

    fmt.Println("json演示案例")
    // 1. 通過結構體生成json
    s1 := Student{1, "zz", 18, 'm', "bj", []string{"c++", "java", "go"}}
    fmt.Println("s1:", s1) //{1 zz 18 109 bj}
    //func Marshal(v interface{}) ([]byte, error)
    //func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
    b1, err1 := json.Marshal(s1)
    if err1 != nil {
        fmt.Println("err1:", err1)
        return
    }
    fmt.Println("b1:", string(b1)) //{"Id":1,"Name":"zz","Age":18,"Sex":109,"Addr":"bj","Subjects":["c++","java","go"]}
    b2, err2 := json.MarshalIndent(s1, "", "    ")
    if err2 != nil {
        fmt.Println("err2:", err2)
        return
    }
    fmt.Println("b2:", string(b2))

    // {
    //       "Id": 1,
    //       "Name": "zz",
    //       "Age": 18,
    //       "Sex": 109,
    //       "Addr": "bj",
    //       "Subjects": [
    //               "c++",
    //               "java",
    //               "go"
    //       ]
    // }

    // 1.1 struct_tag的使用

    // 我們看到上面的輸出字段名的首字母都是大寫的,如果你想用小寫的首字母怎么辦呢?把結構體的字段名改成首字母小寫的?
    //JSON輸出的時候必須注意,只有導出的字段(首字母是大寫)才會被輸出,如果修改字段名,那么就會發現什么都不會輸出,所以必須通過struct tag定義來實現。

    // 針對JSON的輸出,我們在定義struct tag的時候需要注意的幾點是:
    // l  字段的tag是"-",那么這個字段不會輸出到JSON
    // l  tag中帶有自定義名稱,那么這個自定義名稱會出現在JSON的字段名中
    // l  tag中如果帶有"omitempty"選項,那么如果該字段值為空,就不會輸出到JSON串中
    // l  如果字段類型是bool, string, int, int64等,而tag中帶有",string"選項,那么這個字段在輸出到JSON的時候會把該字段對應的值轉換成JSON字符串

    it := IT{"google", []string{"c++", "java", "go"}, false, 88.88}
    fmt.Println("it:", it) //{google [c++ java go] false 88.88}
    b3, _ := json.Marshal(it)
    fmt.Println("b3:", string(b3)) //{"subject":["c++","java","go"],"IsOk":"false","price":88.88}
    b4, _ := json.MarshalIndent(it, "", "   ")
    fmt.Println("b4:", string(b4))

    // {
    //       "subject": [
    //               "c++",
    //               "java",
    //               "go"
    //       ],
    //       "IsOk": "false",
    //       "price": 88.88
    // }
    it2 := IT{"google", []string{"c++", "java", "go"}, false, 0}
    b5, _ := json.Marshal(it2)
    fmt.Println("b5:", string(b5)) //{"subject":["c++","java","go"],"IsOk":"false"} price=0的時候也不輸出

    // 2. 通過map生成json

    m1 := make(map[string]interface{}, 3)
    m1["company"] = "Google"
    m1["Subject"] = []string{"c++", "java", "go"}
    m1["isOk"] = false
    m1["price"] = 3.14

    b6, _ := json.Marshal(m1)
    fmt.Println("b6:", string(b6)) //{"Subject":["c++","java","go"],"company":"Google","isOk":false,"price":3.14}
    b7, _ := json.MarshalIndent(m1, "", "   ")
    fmt.Println("b7:", string(b7))

    // {
    //        "Subject": [
    //                "c++",
    //                "java",
    //                "go"
    //        ],
    //        "company": "Google",
    //        "isOk": false,
    //        "price": 3.14
    // }

    // 3. json解析到結構體

    //可以使用json.Unmarshal()函數將JSON格式的文本解碼為Go里面預期的數據結構。
    // func Unmarshal(data []byte, v interface{}) error
    //該函數的第一個參數是輸入,即JSON格式的文本(比特序列),第二個參數表示目標輸出容器,用于存放解碼后的值。

    jsondata := []byte(`{
          "company": "Google",
          "subject": [
                  "c++",
                  "java",
                  "go"
          ],
          "isOk": "false",
          "price": 88.88
    }`)
    fmt.Println("jsondata:", string(jsondata))
    var book1 Book
    json.Unmarshal(jsondata, &book1)
    fmt.Println("book1:", book1) // {Google [c++ java go] false 88.88}

    // 只想要subjec字段
    var book2 Book2
    json.Unmarshal(jsondata, &book2)
    fmt.Println("book2:", book2) // {[c++ java go]}

    // 4. json解析到map
    fmt.Println("------------")

    m2 := make(map[string]interface{}, 4)
    json.Unmarshal(jsondata, &m2)
    fmt.Println("m2:", m2) // map[company:Google isOk:false price:88.88 subject:[c++ java go]]
    // 使用斷言判m2的類型
    for k, v := range m2 {
        switch k1 := v.(type) {
        case int:
            fmt.Println(k, "is int", k1)
        case string:
            fmt.Println(k, "is string", k1)
        case bool:
            fmt.Println(k, "is bool", k1)
        case float64:
            fmt.Println(k, "is float64", k1)
        case []interface{}:
            fmt.Println(k, "is array", k1)
            for i, j := range k1 {
                fmt.Println(i, "--->", j)
            }
        }

    }

    // subject is array [c++ java go]
    // 0 ---> c++
    // 1 ---> java
    // 2 ---> go
    // isOk is string false
    // price is float64 88.88
    // company is string Google

}

文本處理

  1. WriteString的使用
  2. Read的使用
  3. 借助bufio實現按行讀取內容
  4. 文件案例:拷貝文件
  1. WriteString的使用
package main

import (
    "fmt"
    "os"
    "strconv"
)

// 1. WriteString的使用
// 2. Read的使用
// 3. 借助bufio實現按行讀取內容
// 4. 文件案例:拷貝文件

func main() {

    fmt.Println("文本處理演示案例")

    // 1. WriteString的使用

    f1, err := os.Create("writeString.txt")
    if err != nil {
        fmt.Println("err:", err)
        return
    }

    defer f1.Close() //main函數結束前,關閉文件

    for i := 0; i < 10; i++ {
        f1.WriteString("hello go! " + strconv.Itoa(i) + "\n")
        f1.Write([]byte("ABC\n"))
    }

    // 2. Read的使用
    // 3. 借助bufio實現按行讀取內容
    // 4. 文件案例:拷貝文件
}

寫入的文件內容為:

hello go! 0
ABC
hello go! 1
ABC
hello go! 2
ABC
hello go! 3
ABC
hello go! 4
ABC
hello go! 5
ABC
hello go! 6
ABC
hello go! 7
ABC
hello go! 8
ABC
hello go! 9
ABC

  1. Read的使用
package main

import (
    "fmt"
    "os"
)

// 1. WriteString的使用
// 2. Read的使用
// 3. 借助bufio實現按行讀取內容
// 4. 文件案例:拷貝文件

func main() {

    fmt.Println("文本處理演示案例")

    // 2. Read的使用

    f1, err := os.Open("writeString.txt")
    if err != nil {
        fmt.Println("err:", err)
        return
    }

    defer f1.Close() //main函數結束前,關閉文件

    bytes := make([]byte, 1024) //開辟1024字節的slice作為緩存
    for {
        n, _ := f1.Read(bytes)
        if n != 0 {
            fmt.Println(string(bytes))
        } else {
            break
        }

    }
    fmt.Println("程序結束")

}

  1. 借助bufio實現按行讀取內容
package main

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

// 1. WriteString的使用
// 2. Read的使用
// 3. 借助bufio實現按行讀取內容
// 4. 文件案例:拷貝文件

func main() {

    fmt.Println("文本處理演示案例")

    // 3. 借助bufio實現按行讀取內容

    f1, err := os.Open("writeString.txt")
    if err != nil {
        fmt.Println("err:", err)
        return
    }

    r := bufio.NewReader(f1)
    for {
        buffer, err := r.ReadBytes('\n')
        if err != nil {
            if err == io.EOF {
                break
            }
            fmt.Println("err = ", err)
        }
        fmt.Println("內容為:", string(buffer))
    }

    fmt.Println("程序結束")

}

  1. 文件案例:拷貝文件
package main

import (
    // "bufio"
    "fmt"
    "io"
    "os"
)

// 1. WriteString的使用
// 2. Read的使用
// 3. 借助bufio實現按行讀取內容
// 4. 文件案例:拷貝文件

func main() {

    fmt.Println("拷貝文件演示案例")

    // 4. 文件案例:拷貝文件 src xxx.mp4 yyy.mp4

    list := os.Args
    fmt.Println("args:", list)
    if len(list) != 3 {
        fmt.Println("參數錯誤,案例:src xxx.mp4 yyy.mp4")
        return
    }
    srcFileName := list[1]
    destFileName := list[2]
    if srcFileName == destFileName {
        fmt.Println("源文件和目標文件名稱不能相等")
        return
    }

    sf, err1 := os.Open(srcFileName)
    if err1 != nil {
        fmt.Println("err1", err1)
        return
    }

    df, err2 := os.Create(destFileName)
    if err2 != nil {
        fmt.Println("err2", err2)
        return
    }

    defer sf.Close()
    defer df.Close()

    buf := make([]byte, 1024)
    for {
        n, err3 := sf.Read(buf)
        if err3 != nil {
            if err3 == io.EOF {
                break
            }
            fmt.Println("err3", err3)

        }
        if n != 0 {
            df.Write(buf[:n])
        }
    }

    fmt.Println("程序結束")

}

END.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容