Golang常用操作

总结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常用操作)