1. Go 状态协程

  • 基于通道的方法和Go的通过消息共享内存,保证每份数据为单独的协程所有的理念是一致的。
package main
import (
\"fmt\"
\"math/rand\"
\"sync/atomic\"
\"time\"
)
// 在这个例子中,将有一个单独的协程拥有这个状态。这样可以
// 保证这个数据不会被并行访问所破坏。为了读写这个状态,其
// 他的协程将向这个协程发送信息并且相应地接受返回信息。
// 这些`readOp`和`writeOp`结构体封装了这些请求和回复
type readOp struct {
key int
resp chan int
}
type writeOp struct {
key int
val int
resp chan bool
}
func main() {
// 我们将计算我们执行了多少次操作
var ops int64 = 0
// reads和writes通道将被其他协程用来从中读取或写入数据
reads := make(chan *readOp)
writes := make(chan *writeOp)
// 这个是拥有`state`的协程,`state`是一个协程的私有map
// 变量。这个协程不断地`select`通道`reads`和`writes`,
// 当有请求来临的时候进行回复。一旦有请求,首先执行所
// 请求的操作,然后给`resp`通道发送一个表示请求成功的值。
go func() {
 
go func() {
var state = make(map[int]int)
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:
state[write.key] = write.val
write.resp <- true
}
}
}()
// 这里启动了100个协程来向拥有状态的协程请求读数据。
// 每次读操作都需要创建一个`readOp`,然后发送到`reads`
// 通道,然后等待接收请求回复
for r := 0; r < 100; r++ {
go func() {
for {
read := &readOp{
key: rand.Intn(5),
resp: make(chan int)}
reads <- read
<-read.resp
atomic.AddInt64(&ops, 1)
}
}()
}
// 我们开启10个写协程
for w := 0; w < 10; w++ {
go func() {
for {
write := &writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool)}
writes <- write
<-write.resp
atomic.AddInt64(&ops, 1)
}
}()
}
// 让协程运行1秒钟
time.Sleep(time.Second)
// 最后输出操作数量ops的值
opsFinal := atomic.LoadInt64(&ops)
fmt.Println(\"ops:\", opsFinal)
}

2. Go字典

  • 字典是Go语言内置的关联数据类型。因为数组是索引对应数组元素,而字典是键对应值。
package main
import \"fmt\"
func main() {
// 创建一个字典可以使用内置函数make
// \"make(map[键类型]值类型)\"
m := make(map[string]int)
// 使用经典的\"name[key]=value\"来为键设置值
m[\"k1\"] = 7
m[\"k2\"] = 13
// 用Println输出字典,会输出所有的键值对
fmt.Println(\"map:\", m)
// 获取一个键的值 \"name[key]\".
v1 := m[\"k1\"]
fmt.Println(\"v1: \", v1)
// 内置函数返回字典的元素个数
fmt.Println(\"len:\", len(m))
// 内置函数delete从字典删除一个键对应的值
delete(m, \"k2\")
fmt.Println(\"map:\", m)
// 根据键来获取值有一个可选的返回值,这个返回值表示字典中是否
// 存在该键,如果存在为true,返回对应值,否则为false,返回零值
// 有的时候需要根据这个返回值来区分返回结果到底是存在的值还是零值
// 比如字典不存在键x对应的整型值,返回零值就是0,但是恰好字典中有
// 键y对应的值为0,这个时候需要那个可选返回值来判断是否零值。
_, ok := m[\"k2\"]
fmt.Println(\"ok:\", ok)
// 你可以用 \":=\" 同时定义和初始化一个字典
n := map[string]int{\"foo\": 1, \"bar\": 2}
fmt.Println(\"map:\", n)
}

3. Go 字符串操作函数

  • strings 标准库提供了很多字符串操作相关的函数。这里提供的几个例子是让你先对这个包有个基本了解。
package main
import s \"strings\"
import \"fmt\"
// 这里给fmt.Println起个别名,因为下面我们会多处使用。
var p = fmt.Println
func main() {
// 下面是strings包里面提供的一些函数实例。注意这里的函数并不是
// string对象所拥有的方法,这就是说使用这些字符串操作函数的时候
// 你必须将字符串对象作为第一个参数传递进去。
p(\"Contains: \", s.Contains(\"test\", \"es\"))
p(\"Count: \", s.Count(\"test\", \"t\"))
p(\"HasPrefix: \", s.HasPrefix(\"test\", \"te\"))
p(\"HasSuffix: \", s.HasSuffix(\"test\", \"st\"))
p(\"Index: \", s.Index(\"test\", \"e\"))
p(\"Join: \", s.Join([]string{\"a\", \"b\"}, \"-\"))
p(\"Repeat: \", s.Repeat(\"a\", 5))
p(\"Replace: \", s.Replace(\"foo\", \"o\", \"0\", -1))
p(\"Replace: \", s.Replace(\"foo\", \"o\", \"0\", 1))
p(\"Split: \", s.Split(\"a-b-c-d-e\", \"-\"))
p(\"ToLower: \", s.ToLower(\"TEST\"))
p(\"ToUpper: \", s.ToUpper(\"test\"))
p()
// 你可以在strings包里面找到更多的函数
// 这里还有两个字符串操作方法,它们虽然不是strings包里面的函数,
// 但是还是值得提一下。一个是获取字符串长度,另外一个是从字符串中
// 获取指定索引的字符
p(\"Len: \", len(\"hello\"))
p(\"Char:\", \"hello\"[1])
}

4. Go 字符串格式化

  • Go对字符串格式化提供了良好的支持。下面我们看些常用的字符串格式化的例子。
package main
import \"fmt\"
import \"os\"
type point struct {
x, y int
}
func main() {
// Go提供了几种打印格式,用来格式化一般的Go值,例如
// 下面的%v打印了一个point结构体的对象的值
p := point{1, 2}
fmt.Printf(\"%v\\n\", p)
// 如果所格式化的值是一个结构体对象,那么`%+v`的格式化输出
// 将包括结构体的成员名称和值
fmt.Printf(\"%+v\\n\", p)
// `%#v`格式化输出将输出一个值的Go语法表示方式。
fmt.Printf(\"%#v\\n\", p)
// 使用`%T`来输出一个值的数据类型
fmt.Printf(\"%T\\n\", p)
// 格式化布尔型变量
fmt.Printf(\"%t\\n\", true)
// 有很多的方式可以格式化整型,使用`%d`是一种
// 标准的以10进制来输出整型的方式
fmt.Printf(\"%d\\n\", 123)
// 这种方式输出整型的二进制表示方式
fmt.Printf(\"%b\\n\", 14)
// 这里打印出该整型数值所对应的字符
fmt.Printf(\"%c\\n\", 33)
// 使用`%x`输出一个值的16进制表示方式
fmt.Printf(\"%x\\n\", 456)
// 浮点型数值也有几种格式化方法。最基本的一种是`%f`
fmt.Printf(\"%f\\n\", 78.9)
// `%e`和`%E`使用科学计数法来输出整型
fmt.Printf(\"%e\\n\", 123400000.0)
fmt.Printf(\"%E\\n\", 123400000.0)
// 使用`%s`输出基本的字符串
fmt.Printf(\"%s\\n\", \"\\\"string\\\"\")
// 输出像Go源码中那样带双引号的字符串,需使用`%q`
fmt.Printf(\"%q\\n\", \"\\\"string\\\"\")
// `%x`以16进制输出字符串,每个字符串的字节用两个字符输出
fmt.Printf(\"%x\\n\", \"hex this\")
// 使用`%p`输出一个指针的值
fmt.Printf(\"%p\\n\", &p)
// 当输出数字的时候,经常需要去控制输出的宽度和精度。
// 可以使用一个位于%后面的数字来控制输出的宽度,默认
// 情况下输出是右对齐的,左边加上空格
fmt.Printf(\"|%6d|%6d|\\n\", 12, 345)
// 你也可以指定浮点数的输出宽度,同时你还可以指定浮点数
// 的输出精度
fmt.Printf(\"|%6.2f|%6.2f|\\n\", 1.2, 3.45)
// To left-justify, use the `-` flag.
fmt.Printf(\"|%-6.2f|%-6.2f|\\n\", 1.2, 3.45)
// 你也可以指定输出字符串的宽度来保证它们输出对齐。默认
// 情况下,输出是右对齐的
fmt.Printf(\"|%6s|%6s|\\n\", \"foo\", \"b\")
// 为了使用左对齐你可以在宽度之前加上`-`号
fmt.Printf(\"|%-6s|%-6s|\\n\", \"foo\", \"b\")
// `Printf`函数的输出是输出到命令行`os.Stdout`的,你
// 可以用`Sprintf`来将格式化后的字符串赋值给一个变量
s := fmt.Sprintf(\"a %s\", \"string\")
fmt.Println(s)
// 你也可以使用`Fprintf`来将格式化后的值输出到`io.Writers`
fmt.Fprintf(os.Stderr, \"an %s\\n\", \"error\")
}

5. Go 自定义排序

  • 有的时候我们希望排序不是仅仅按照自然顺序排序。例如,我们希望按照字符串的长度来对一个字符串数组排序而不是按照字母顺序来排序。这里我们介绍一下Go的自定义排序。
package main
import \"sort\"
import \"fmt\"
// 为了能够使用自定义函数来排序,我们需要一个
// 对应的排序类型,比如这里我们为内置的字符串
// 数组定义了一个别名ByLength
type ByLength []string
// 我们实现了sort接口的Len,Less和Swap方法
// 这样我们就可以使用sort包的通用方法Sort
// Len和Swap方法的实现在不同的类型之间大致
// 都是相同的,只有Less方法包含了自定义的排序
// 逻辑,这里我们希望以字符串长度升序排序
func (s ByLength) Len() int {
return len(s)
}
func (s ByLength) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s ByLength) Less(i, j int) bool {
return len(s[i]) < len(s[j])
}
// 一切就绪之后,我们就可以把需要进行自定义排序
// 的字符串类型fruits转换为ByLength类型,然后使用
// sort包的Sort方法来排序
func main() {
fruits := []string{\"peach\", \"banana\", \"kiwi\"}
sort.Sort(ByLength(fruits))
fmt.Println(fruits)
}

6. Go 64编码

  • Go提供了对 64编码和解码的内置支持。
package main
// 这种导入包的语法将默认的 64起了一个别名b64,这样
// 我们在下面就可以直接使用b64表示这个包,省点输入量
import b64 \"encoding/ 64\"
import \"fmt\"
func main() {
// 这里是我们用来演示编码和解码的字符串
data := \"abc123!?$*&()\'-=@~\"
// Go支持标准的和兼容URL的 64编码。
// 我们这里使用标准的 64编码,这个
// 函数需要一个`[]byte`参数,所以将这
// 个字符串转换为字节数组
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
fmt.Println(sEnc)
// 解码一个 64编码可能返回一个错误,
// 如果你不知道输入是否是正确的 64
// 编码,你需要检测一些解码错误
sDec, _ := b64.StdEncoding.DecodeString(sEnc)
fmt.Println(string(sDec))
fmt.Println()
// 使用兼容URL的 64编码和解码
uEnc := b64.URLEncoding.EncodeToString([]byte(data))
fmt.Println(uEnc)
uDec, _ := b64.URLEncoding.DecodeString(uEnc)
fmt.Println(string(uDec))
}

7. Go Defer

  • Defer 用来保证一个函数调用会在程序执行的最后被调用。通常用于资源清理工作。
package main
import \"fmt\"
import \"os\"
// 假设我们想创建一个文件,然后写入数据,最后关闭文件
func main() {
// 在使用createFile得到一个文件对象之后,我们使用defer
// 来调用关闭文件的方法closeFile,这个方法将在main函数
// 最后被执行,也就是writeFile完成之后
f := createFile(\"/tmp/defer.txt\")
// Windows下面使用这个语句
// f := createFile(\"D:\\\\Temp\\\\defer.txt\")
defer closeFile(f)
writeFile(f)
}
func createFile(p string) *os.File {
fmt.Println(\"creating\")
f, err := os.Create(p)
if err != nil {
panic(err)
}
return f
}
func writeFile(f *os.File) {
fmt.Println(\"writing\")
fmt.Fprintln(f, \"data\")
}
func closeFile(f *os.File) {
fmt.Println(\"closing\")
f.Close()
}

Go Exit
使用 os.Exit 可以给定一个状态,然后立刻退出程序运行。

package main
import \"fmt\"
import \"os\"
func main() {
// 当使用`os.Exit`的时候defer操作不会被运行,
// 所以这里的``fmt.Println`将不会被调用
defer fmt.Println(\"!\")
// 退出程序并设置退出状态值
os.Exit(3)
}

8. Go for循环

  • for循环是Go语言唯一的循环结构。这里有三个基本的for循环类型。
package main
import \"fmt\"
func main() {
// 最基本的一种,单一条件循环
// 这个可以代替其他语言的while循环
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
// 经典的循环条件初始化/条件判断/循环后条件变化
for j := 7; j <= 9; j++ {
fmt.Println(j)
}
// 无条件的for循环是死循环,除非你使用break跳出循环或者
// 使用return从函数返回
for {
fmt.Println(\"loop\")
break
}
}

9. Go if…else if…else 条件判断

package main
import \"fmt\"
func main() {
// 基本的例子
if 7%2 == 0 {
fmt.Println(\"7 is even\")
} else {
fmt.Println(\"7 is odd\")
}
// 只有if条件的情况
if 8%4 == 0 {
fmt.Println(\"8 is divisible by 4\")
}
// if条件可以包含一个初始化表达式,这个表达式中的变量
// 是这个条件判断结构的局部变量
if num := 9; num < 0 {
fmt.Println(num, \"is negative\")
} else if num < 10 {
fmt.Println(num, \"has 1 digit\")
} else {
fmt.Println(num, \"has multiple digits\")
}
}

10. Go JSON支持

  • Go内置了对JSON数据的编码和解码,这些数据的类型包括内置数据类型和自定义数据类型。
package main
import \"encoding/json\"
import \"fmt\"
import \"os\"
// 我们使用两个结构体来演示自定义数据类型的JSON数据编码和解码。
type Response1 struct {
Page int
Fruits []string
}
type Response2 struct {
Page int `json:\"page\"`
Fruits []string `json:\"fruits\"`
}
func main() {
// 首先我们看一下将基础数据类型编码为JSON数据
bolB, _ := json.Marshal(true)
fmt.Println(string(bolB))
intB, _ := json.Marshal(1)
fmt.Println(string(intB))
fltB, _ := json.Marshal(2.34)
fmt.Println(string(fltB))
strB, _ := json.Marshal(\"gopher\")
fmt.Println(string(strB))
// 这里是将切片和字典编码为JSON数组或对象
slcD := []string{\"apple\", 					
收藏 打印