美文网首页
Golang常用操作

Golang常用操作

作者: MrCloudPeak | 来源:发表于2019-07-24 22:50 被阅读0次

    总结Golang在实际开发中的常用操作,以便快速查找

    处理错误

    • 包装一个错误处理函数,避免大量重复if err!=nil的判断
    func checkError(err error) {
        if err != nil {
            panic(err)
        }
    }
    
    • 使用闭包处理panic错误,对拥有同样签名的函数,比如func handler1(w http.ResponseWriter, r *http.Request) { ... },可以复用panic处理机制,
    func errorHandler(fn func()) func() {
        return func() {
            defer func() {
                if err, ok := recover().(error); ok {
                    log.Printf("run time panic: %v", err)
                }
            }()
            fn()
        }
    }
    
    • 完整示例如下
    package main
    
    import (
        "errors"
        "log"
    )
    
    func main() {
        calc := errorHandler(calc)
        calc()
    }
    
    func div(x, y int) (int, error) {
        if y == 0 {
            return 0, errors.New("divisor cannot be zero")
        }
        return x / y, nil
    }
    
    func calc() {
        _, err := div(1, 1)
        checkError(err)
        _, err = div(1, 0)
        checkError(err)
    }
    
    func checkError(err error) {
        if err != nil {
            panic(err)
        }
    }
    
    func errorHandler(fn func()) func() {
        return func() {
            defer func() {
                if err, ok := recover().(error); ok {
                    log.Printf("run time panic: %v", err)
                }
            }()
            fn()
        }
    }
    

    读写文件

    • 逐行读文件
    package main
    
    import (
        "bufio"
        "fmt"
        "io"
        "os"
    )
    
    func main() {
        inputFile, inputError := os.Open("client.go")
        if inputError != nil {
            fmt.Println(inputError.Error())
            return
        }
        defer inputFile.Close()
    
        inputReader := bufio.NewReader(inputFile)
        for {
            inputString, readerError := inputReader.ReadString('\n') 
            fmt.Print(inputString)
            if readerError == io.EOF {
                return
            }
        }
    }
    
    • 一次读取文件
    package main
    
    import (
        "fmt"
        "io/ioutil"
    )
    
    func main() {
        content, err := ioutil.ReadFile("client.go")
        if err != nil {
            fmt.Println(err.Error())
            return
        }
        //byte[]转string
        fmt.Println(string(content))
    }
    
    
    • 追加写文件
    package main
    
    import (
        "bufio"
        "fmt"
        "os"
    )
    
    func main () {
        //下面的os.O_WRONLY|os.O_APPEND可以有各种组合,以实现只读、只写、追加等等不同模式
        outputFile, outputError := os.OpenFile("output.txt",os.O_WRONLY|os.O_APPEND,0666)
        if outputError != nil {
            fmt.Printf("An error occurred with file opening or creation\n")
            return
        }
        defer outputFile.Close()
    
        outputWriter := bufio.NewWriter(outputFile)
        outputString := "hello world!\n"
    
        for i:=0; i<10; i++ {
            //写入缓冲区
            outputWriter.WriteString(outputString)
        }
        //真正写入文件
        outputWriter.Flush()
    }
    
    • 一次性写文件
    package main
    
    import (
        "fmt"
        "io/ioutil"
    )
    
    func main() {
        buf := []byte("hello world")
        err := ioutil.WriteFile("output.txt", buf, 0644) //注意这里是8进制
        if err != nil {
            fmt.Print(err.Error())
        }
    }
    

    处理文件路径

    package main
    
    import (
        "fmt"
        "os"
        "path/filepath"
    )
    
    func main() {
        path := "/home/hello/test.txt"
        fmt.Println("父目录", filepath.Dir(path))
        fmt.Println("文件名", filepath.Base(path))
        fmt.Println("拼接路径", filepath.Join(filepath.Dir(path), "hello.txt"))
        currentDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
        fmt.Println("当前文件的绝对路径", currentDir)
        pwd, _ := os.Getwd()
        fmt.Println("当前工作路径", pwd)
    }
    
    

    处理字符串

    • strings包的常见用法
    package main
    
    import (
        "fmt"
        "strings"
    )
    
    func main() {
        s := "hello world"
        sSplit := strings.Split(s, " ")
        fmt.Println("字符串分割", sSplit)
        sJoin := strings.Join(sSplit, "&")
        fmt.Println("字符串拼接", sJoin)
        sReplace := strings.Replace(s, "l", "n", 2)
        fmt.Println("字符串替换", sReplace)
        sContains := strings.Contains(s, "wo")
        fmt.Println("字符串是否包含", sContains)
        sIndex := strings.Index(s, "o")
        fmt.Println("第一个子串的位置", sIndex)
    }
    
    • 利用切片获取子串
    package main
    
    import (
        "fmt"
        "strings"
    )
    
    func main() {
        s := "hello world"
        fmt.Println(s[strings.Index(s, " ")+1:])
    }
    
    • 字符串拼接(适用于较长的字符串拼接,不会反复申请内存,类似于java的StringBuild)
    var buffer bytes.Buffer
    for {
        if s, ok := getNextString(); ok { //method getNextString() not shown here
            buffer.WriteString(s)
        } else {
            break
        }
    }
    fmt.Print(buffer.String(), "\n")
    
    • 遍历中文字符串
    package main
    
    import "fmt"
    
    func main() {
        s := "你好,世界"
        for _, c := range s {
            fmt.Printf("%c\n", c)
        }
    }
    
    
    • 正则表达式
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        s := "hello world"
        //判断字符串是否匹配
        match, err := regexp.MatchString(`[0-9]+`, s)
        if err != nil {
            panic(err.Error())
        }
        fmt.Println("contains number?", match)
    
        s = "2019-02-10"
        re := regexp.MustCompile(`[0-9]+`)
        //查找所有匹配的字符串
        allNum := re.FindAllString(s, -1)
        fmt.Println("all number", allNum)
    
        //查找第一个匹配的字符串
        firstNum := re.FindString(s)
        fmt.Println("first number", firstNum)
    }
    
    

    处理http请求

    package main
    
    import (
        "encoding/json"
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
    )
    
    type Student struct {
        Name string
        Age  int
    }
    
    func studentHandler(w http.ResponseWriter, req *http.Request) {
        switch req.Method {
        case http.MethodGet:
            doGet(w, req)
        case http.MethodPost:
            doPost(w, req)
        default:
            http.NotFound(w, req)
        }
    }
    
    func doGet(w http.ResponseWriter, req *http.Request) {
        //GET方法访问
        //http://127.0.0.1:8080/api/v1/student?name=pi,pi,zhu&age=10,20,30(url中的参数列表用逗号隔开)
        //得到map[age:[10,20,30] name:[pi,pi,zhu]],注意得到的map的value是一个slice
        queryParams := req.URL.Query()
        _, _ = fmt.Fprintf(w, "get all student %v", queryParams)
    }
    
    func doPost(w http.ResponseWriter, req *http.Request) {
        body, _ := ioutil.ReadAll(req.Body)
        var s Student
        _ = json.Unmarshal(body, &s)
        _, _ = fmt.Fprintf(w, "save student [%-v]", s)
    }
    
    func main() {
        http.HandleFunc("/api/v1/student", studentHandler)
        err := http.ListenAndServe("localhost:8080", nil)
        if err != nil {
            log.Fatal("ListenAndServe: ", err.Error())
        }
    }
    

    发送http请求

    高层api

    • GET
    package main
    
    import (
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
    )
    
    func main() {
        res, err := http.Get("http://www.google.com")
        checkError(err)
        defer res.Body.Close()
        data, err := ioutil.ReadAll(res.Body)
        checkError(err)
        fmt.Printf("Got: %q", string(data))
    }
    
    func checkError(err error) {
        if err != nil {
            log.Fatalf("Get : %v", err)
        }
    }
    

    低层api(可设置timeout、header等详细请求信息)

    • GET
    package main
    
    import (
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
        "time"
    )
    
    func main() {
        client := http.Client{Timeout: time.Second * 5}
        req, _ := http.NewRequest("GET", "https://www.bing.com", nil)
        agent := `Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1;.NET CLR 1.1.4322;.NET CLR 2.0.50727;.NET CLR 3.0.04506.30)`
    
        req.Header.Set("User-Agent", agent)
        res, err := client.Do(req)
        checkError(err)
        defer res.Body.Close()
        data, err := ioutil.ReadAll(res.Body)
        checkError(err)
        fmt.Printf("Got: %q,status is %s", string(data), res.Status)
    }
    
    func checkError(err error) {
        if err != nil {
            log.Fatalf("Get : %v", err)
        }
    }
    
    
    • POST
    package main
    
    import (
        "bytes"
        "encoding/json"
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
        "time"
    )
    
    type Person struct {
        Name   string
        Age    int
        weight int
    }
    
    func main() {
        p := &Person{Name: "piggie", Age: 5, weight: 100}
        pJson, _ := json.Marshal(p)
        client := http.Client{Timeout: time.Second * 5}
        req, _ := http.NewRequest("GET", "https://www.example.com/person", bytes.NewBuffer(pJson))
        req.Header.Set("Content-Type", "application/json")
        res, err := client.Do(req)
        checkError(err)
        defer res.Body.Close()
        data, err := ioutil.ReadAll(res.Body)
        checkError(err)
        fmt.Printf("Got: %q,status is %s", string(data), res.Status)
    }
    
    func checkError(err error) {
        if err != nil {
            log.Fatalf("Get : %v", err)
        }
    }
    

    日志打印

    package main
    
    import "log"
    
    func Test() {
        //定义日志格式,默认是Ldate | Ltime
        log.SetFlags(log.Lshortfile | log.Ltime | log.Lmicroseconds | log.Ldate)
        //设置前缀
        log.SetPrefix("[MrCloudPeak]")
        //用法跟fmt包中的同名函数类似
        log.Printf("hello %s", "world")
        //Fatal系列是打印错误信息并直接退出进程
        //log.Fatalf("hello %s", "world")
        //Panic系列是打印错误信息并panic
        //log.Panicf("hello %s", "world")
    }
    

    操作数据库

    处理json

    • 结构体转json
    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    type Person struct {
        Name   string
        Age    int
        weight int
    }
    
    func main() {
        p := &Person{Name: "piggie", Age: 5, weight: 100}
        pJson, err := json.Marshal(p)
        if err != nil {
            fmt.Println(err)
            return
        }
        //只有结构的导出字段才能被转换,weight这里就读不到
        fmt.Printf("结构体转Json%s", pJson)
    }
    
    • json转结构体
    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    type Person struct {
        Name   string
        Age    int
        weight int
    }
    
    func main() {
        var p Person
        pJson := []byte(`{"age":18,"name":"piggies","weight":200,"length":100}`)
        //虽然反射能够让 JSON 字段去尝试匹配目标结构字段;但是只有真正匹配上的字段才会填充数据。字段没有匹配不会报错,而是直接忽略掉。
        err := json.Unmarshal(pJson, &p)
        if err != nil {
            fmt.Println(err)
        }
        //{piggies 18 0}
        fmt.Println(p)
    }
    
    • map/slice转json
    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    func main() {
        //json 包使用 map[string]interface{} 和 []interface{} 储存任意的 JSON 对象和数组
        p := map[string]interface{}{"name": "piggies", "age": 18, "weight": 200}
        s := []interface{}{p}
        pJson, err := json.Marshal(s)
        if err != nil {
            fmt.Println(err)
        }
        fmt.Printf("map/slice转json %s", pJson)
    }
    
    • json转interface{}
    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    func main() {
        var p interface{}
        pJson := []byte(`[{"age":18,"name":"piggies","weight":200}]`)
        err := json.Unmarshal(pJson, &p)
        if err != nil {
            fmt.Println(err)
        }
        //p是interface,所以这里需要类型判断
        pList, ok := p.([]interface{})
        if ok {
            for _, p := range pList {
                //这里的p也是interface,所以使用Type Switch语句处理
                switch pp := p.(type) {
                case map[string]interface{}:
                    fmt.Println(pp["age"])
                default:
                    fmt.Println("unknown type")
                }
            }
        }
    
    }
    

    操作系统

    • 读写环境变量
    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
        err := os.Setenv("redis_ip", "127.0.0.1")
        if err != nil {
            fmt.Println(err)
        }
        redisIp := os.Getenv("redis_ip")
        println(redisIp)
    }
    
    • 获取cpu核数
    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    func main() {
        fmt.Println(runtime.NumCPU())
    }
    
    • 设置操作系统线程数
    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    func main() {
        //默认是CPU的核数
        fmt.Println(runtime.GOMAXPROCS(4))
    }
    

    • 别名机制
    import (
        math_rand "math/rand"
        crypt_rand "crypto/rand"
    )
    
    • _操作
      由于go在引入包时调用包的init方法。所以使用_操作,主要是为了使用包的init函数,但不要调用包中的任何函数或变量。例如注册数据库驱动
    import (
        "database/sql"
        _ "github.com/mattn/go-sqlite3"
      )
    

    语法技巧

    多层for循环的break

    循环嵌套循环时,可以在 break 后指定标签。用标签决定哪个循环被终止。

    package main
    
    import "fmt"
    
    func main() {
    
    OuterLoop:
        for i := 0; i < 2; i++ {
            for j := 0; j < 5; j++ {
                switch j {
                case 2:
                    fmt.Println(i, j)
                    break OuterLoop
                case 3:
                    fmt.Println(i, j)
                    break OuterLoop
                }
            }
        }
    }
    //输出0 2
    

    单元用例

    创建一个 _test.go 结尾的文件,包含名为 TestXXX ,结构为func (t *testing.T)的方法。测试框架会自动执行这些方法。如果方法中包含了t.Error 或者t.Fail之类的错误调用,则任务这条用例执行失败
    $GOPATH/src/github.com/user/stringutil/reverse_test.go

    package stringutil
    
    import "testing"
    
    func TestReverse(t *testing.T) {
        cases := []struct {
            in, want string
        }{
            {"Hello, world", "dlrow ,olleH"},
            {"Hello, 世界", "界世 ,olleH"},
            {"", ""},
        }
        for _, c := range cases {
            got := Reverse(c.in)
            if got != c.want {
                t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
            }
        }
    }
    

    执行go test github.com/user/stringutil,得到如下结果
    ok github.com/user/stringutil 0.165s

    相关文章

      网友评论

          本文标题:Golang常用操作

          本文链接:https://www.haomeiwen.com/subject/hlnglctx.html