Go 语言是一门由 Google 开发的开源编程语言,它以简洁高效的设计理念和强大的并发支持而著称。本篇笔记介绍了 Go 语言的基础语法和常用特性。
一、Hello World
了解一门语言都要先从最简单的程序开始。Go 语言的 Hello World 程序非常简单:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
以上的代码通过 package main 定义了一个包名为 main 的包,而且包名只能为main,这是 Go 语言执行程序的入口。import "fmt" 导入了 fmt 包,该包提供了格式化输入输出的函数。func main() 是程序的入口函数,在程序执行时会首先执行这个函数。
二、变量和常量
Go 语言是静态类型语言,声明变量时需要指定变量的类型。变量的声明格式如下:
var name type
即:var 变量名 变量类型
例如:
var age int
var name string
Go 语言还支持短变量声明,它可以自动推断变量的数据类型。注意该方式只能用在函数内部:
name := "Tom"
常量的声明方式如下:
const pi = 3.1415926
三、数据类型
Go 语言支持多种基本数据类型,包括整数类型、浮点数类型、布尔类型、字符串类型和数组类型等。还支持引用类,包括指针类型、切片、映射、管道等。注意,在其他语言中,数组往往是引用类型。
// 整数类型
var num1 int // 有符号整数
var num2 uint // 无符号整数
// 浮点数类型
var f1 float32
var f2 float64
// 布尔类型
var isTrue bool
// 字符串类型
var str string
// 声明一个长度为 5 的整数数组
var arr [5]int
// 给数组元素赋值
arr[0] = 1
1.字符串
两种声明方式:
str1 := "Hello, World!"
str2 := `Hello, World!`
获取字符串的长度:
str := "Hello, World!"
length := len(str)
fmt.Println(length) // 输出:13
访问字符串中的字符:
在 Golang 中,字符串底层是以字节数组的形式存储的,因此可以通过索引访问其中的字符,但要注意字符串可能包含多字节字符,索引访问可能导致乱码。
str := "Hello, 世界"
fmt.Println(str[0]) // 输出:72('H' 的 ASCII 码)
fmt.Println(str[7]) // 输出:228('世' 的 UTF-8 编码的第一个字节)
字符串连接
可以使用 + 运算符将多个字符串连接成一个新的字符串:
str1 := "Hello, "
str2 := "World!"
result := str1 + str2
fmt.Println(result) // 输出:Hello, World!
字符串切片
使用切片可以获取字符串的一部分子串:
str := "Hello, World!"
subStr := str[0:5]
fmt.Println(subStr) // 输出:Hello
字符串遍历
可以使用 for range 结构遍历字符串,其中每次迭代会返回字符的 Unicode 编码点:
str := "Hello, 世界"
for _, char := range str {
fmt.Println(char)
}
将字符串转换为字节数组:
str := "Hello, World!"
byteArr := []byte(str)
fmt.Println(byteArr) // 输出:[72 101 108 108 111 44 32 87 111 114 108 100 33]
将字节数组转换为字符串:
byteArr := []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
str := string(byteArr)
fmt.Println(str) // 输出:Hello, World!
判断字符串是否包含子串:
str := "Hello, World!"
contains := strings.Contains(str, "World")
fmt.Println(contains) // 输出:true
查找子串在字符串中的位置:
str := "Hello, World!"
index := strings.Index(str, "World")
fmt.Println(index) // 输出:7
替换字符串中的子串:
str := "Hello, World!"
newStr := strings.Replace(str, "World", "Golang", 1)
fmt.Println(newStr) // 输出:Hello, Golang!
使用分隔符将字符串分割成子串:
str := "apple,banana,orange"
fruits := strings.Split(str, ",")
fmt.Println(fruits) // 输出:[apple banana orange]
字符串大小写转换
str := "Hello, World!"
lower := strings.ToLower(str)
upper := strings.ToUpper(str)
fmt.Println(lower) // 输出:hello, world!
fmt.Println(upper) // 输出:HELLO, WORLD!
可以使用 strings.TrimSpace 去除字符串开头和结尾的空白字符,包括换行符:
str := " Hello, World! "
trimmed := strings.TrimSpace(str)
fmt.Println(trimmed) // 输出:Hello, World!
2.切片
创建切片
- 使用字面量创建切片:
slice := []int{1, 2, 3, 4, 5}
- 使用
make函数创建切片:
slice := make([]int, 5) // 创建一个长度为 5 的切片
- 创建指定长度和容量的切片:
slice := make([]int, 5, 10) // 创建一个长度为 5,容量为 10 的切片
切片操作
- 访问元素:
slice := []int{1, 2, 3, 4, 5}
fmt.Println(slice[0]) // 输出:1
- 切片截取:
slice := []int{1, 2, 3, 4, 5}
subSlice := slice[1:3] // 截取索引 1 到索引 2 的子切片
fmt.Println(subSlice) // 输出:[2 3]
- 动态增加元素:
slice := []int{1, 2, 3, 4, 5}
slice = append(slice, 6) // 在切片末尾追加元素
fmt.Println(slice) // 输出:[1 2 3 4 5 6]
- 动态删除元素:
slice := []int{1, 2, 3, 4, 5}
index := 2
slice = append(slice[:index], slice[index+1:]...) // 删除索引为 2 的元素
fmt.Println(slice) // 输出:[1 2 4 5]
- 切片长度和容量:
切片的长度是指切片当前包含的元素个数,而容量是指切片底层数组的长度。
slice := []int{1, 2, 3, 4, 5}
fmt.Println(len(slice)) // 输出:5
fmt.Println(cap(slice)) // 输出:5
切片的底层原理
在 Go 语言中,切片是对底层数组的一个引用,因此对切片的修改会影响底层数组的内容。当切片的长度超过了底层数组的容量时,Go 会重新分配一个更大的底层数组,并将原有的数据复制过去。
slice := []int{1, 2, 3, 4, 5}
subSlice := slice[1:3]
fmt.Println(subSlice) // 输出:[2 3]
subSlice[0] = 100
fmt.Println(slice) // 输出:[1 100 3 4 5]
切片的共享和复制
当多个切片指向同一个底层数组时,它们会共享数据。
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1 // slice2 和 slice1 共享同一个底层数组
slice2[0] = 100
fmt.Println(slice1) // 输出:[100 2 3 4 5]
fmt.Println(slice2) // 输出:[100 2 3 4 5]
如果需要复制切片的内容而不共享数据,可以使用 copy 函数。
slice1 := []int{1, 2, 3, 4, 5}
slice2 := make([]int, len(slice1))
copy(slice2, slice1) // 复制 slice1 的内容到 slice2
slice2[0] = 100
fmt.Println(slice1) // 输出:[1 2 3 4 5]
fmt.Println(slice2) // 输出:[100 2 3 4 5]
四、流程控制
Go 语言的流程控制与其他编程语言类似,包括条件语句、循环语句和跳转语句。
1. 条件语句 - if...else
if age >= 18 {
fmt.Println("成年人")
} else {
fmt.Println("未成年人")
}
2. 循环语句 - for,跳转语句 - break、continue
for i := 0; i < 5; i++ {
fmt.Println(i)
}
for i := 0; i < 5; i++ {
if i == 3 {
break // 跳出循环
}
fmt.Println(i)
}
五、函数
Go 语言中函数的定义格式如下:
func functionName(parameters) returnType {
// 函数体
}
六、go语言JSON处理的序列化和反序列化
在 Go 语言中,JSON 的序列化和反序列化是将数据结构与 JSON 字符串之间进行转换的过程。序列化将 Go 中的数据结构转换为 JSON 格式的字符串,而反序列化则将 JSON 字符串转换为 Go 中的数据结构。这在处理 API 请求和响应、配置文件读写等场景中非常常见。
Golang 提供了标准库 encoding/json 来处理 JSON 的序列化和反序列化。以下是详细的示例和说明:
一、JSON 序列化
将 Go 中的数据结构转换为 JSON 字符串称为 JSON 序列化。可以通过 json.Marshal() 函数来实现。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"` // 忽略空字段
}
func main() {
p := Person{
Name: "John",
Age: 30,
}
// 序列化为 JSON 字符串
data, err := json.Marshal(p)
if err != nil {
fmt.Println("序列化出错:", err)
return
}
fmt.Println(string(data))
}
输出结果:
{"name":"John","age":30}
在上面的示例中,我们定义了一个 Person 结构体,并使用 json 标签来定义字段在 JSON 中的键名。json.Marshal() 函数将 Person 实例 p 序列化为 JSON 格式的字符串,并输出结果。
二、JSON 反序列化
将 JSON 字符串转换为 Go 中的数据结构称为 JSON 反序列化。可以通过 json.Unmarshal() 函数来实现。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"`
}
func main() {
jsonData := `{"name":"Alice","age":25}`
// 反序列化为 Go 中的结构体
var p Person
err := json.Unmarshal([]byte(jsonData), &p)
if err != nil {
fmt.Println("反序列化出错:", err)
return
}
fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
}
输出结果:
Name: Alice
Age: 25
在上面的示例中,我们定义了一个 Person 结构体,并使用 json.Unmarshal() 函数将 JSON 字符串 jsonData 反序列化为 Person 结构体,并输出结果。
三、空字段处理
在序列化时,可以使用 omitempty 选项来忽略空字段,即将空字段排除在生成的 JSON 字符串中。这在避免输出不必要的空字段时很有用。
在上面的 Person 结构体中,我们为 Email 字段添加了 omitempty 选项:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"` // 忽略空字段
}
如果我们定义的 Person 结构体中 Email 字段为空(零值),则在序列化为 JSON 时会忽略该字段。
四、JSON 字符串中的未知字段
在反序列化 JSON 字符串时,如果 JSON 中有一些未知的字段,可以使用 json.RawMessage 来处理这些字段。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Unknown json.RawMessage `json:"-"`
}
func main() {
jsonData := `{"name":"Bob","age":35,"address":"123 Main St"}`
var p Person
err := json.Unmarshal([]byte(jsonData), &p)
if err != nil {
fmt.Println("反序列化出错:", err)
return
}
fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
fmt.Println("Unknown:", string(p.Unknown))
}
输出结果:
Name: Bob
Age: 35
Unknown: {"address":"123 Main St"}
在 Person 结构体中添加了一个 Unknown 字段,并使用 json.RawMessage 类型来存储未知的 JSON 字段。在反序列化时,未知的字段会被保存在 Unknown 字段中,我们可以通过 string(p.Unknown) 将其输出。
七、go语言的时间处理
在 Go 语言中,时间处理是一个常见且重要的任务,用于处理日期、时间、时间间隔等相关操作。Go 标准库 time 提供了丰富的函数和类型,使得时间处理变得非常简单和高效。以下是 Go 语言的时间处理功能的一些常见用法和示例:
一、获取当前时间
可以使用 time.Now() 函数获取当前的本地时间:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now)
}
输出结果:2023-07-30 12:34:56.789012345 +0800 CST m=+0.000000001
二、格式化时间
使用 time.Format() 方法可以将时间按照指定的格式进行格式化:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
formatted := now.Format("2006-01-02 15:04:05")
fmt.Println(formatted)
}
输出结果:2023-07-30 12:34:56
在这个例子中,我们使用了一个特殊的日期格式字符串 "2006-01-02 15:04:05",这是因为在 Go 语言中,日期和时间的格式化需要使用一个特定的参考日期 "2006-01-02 15:04:05"。
三、解析时间字符串
使用 time.Parse() 函数可以将时间字符串解析为时间对象:
package main
import (
"fmt"
"time"
)
func main() {
timeStr := "2023-07-30 12:34:56"
parsedTime, err := time.Parse("2006-01-02 15:04:05", timeStr)
if err != nil {
fmt.Println("解析时间出错:", err)
return
}
fmt.Println(parsedTime)
}
输出结果:2023-07-30 12:34:56 +0000 UTC
四、时间加减
使用 time.Add() 方法可以对时间进行加减操作,返回一个新的时间对象:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
oneHourLater := now.Add(time.Hour)
fmt.Println("One hour later:", oneHourLater)
}
输出结果类似:One hour later: 2023-07-30 13:34:56.789012345 +0800 CST m=+3600.000000001
五、时间间隔
在 Go 语言中,时间间隔可以使用 time.Duration 类型表示。可以使用 time.Sleep() 方法来暂停程序的执行一段时间:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Start...")
time.Sleep(2 * time.Second)
fmt.Println("End.")
}
这段代码会暂停程序执行 2 秒,然后输出 "End."。
六、定时器和计时器
在 Go 语言中,可以使用 time.NewTimer() 创建一个定时器,以在指定时间后触发某个操作:
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(3 * time.Second)
fmt.Println("Start...")
<-timer.C
fmt.Println("End.")
}
这段代码会在启动后等待 3 秒后输出 "End."。
另外,time.Tick() 函数可以创建一个计时器,以固定时间间隔触发某个操作:
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.Tick(1 * time.Second)
for tick := range ticker {
fmt.Println(tick)
}
}
这段代码会每隔 1 秒输出一次当前时间。