异常
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/
- 通过结构体生成json
1.1 struct_tag的使用 - 通过map生成json
- json解析到结构体
- 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
}
文本处理
- WriteString的使用
- Read的使用
- 借助bufio实现按行读取内容
- 文件案例:拷贝文件
- 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
- 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("程序结束")
}
- 借助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("程序结束")
}
- 文件案例:拷贝文件
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.