总结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
网友评论