写在开始:笔者也是首次学习Go语言,感觉到这门语言的奇妙之处。乘秉着费曼学习法的原则,我想把自己学Go语言中遇到的一些问题和总结性的东西写在这里,有不足的地方还望大家多多指点,谢谢大家!
开发环境:本文中的开发均在云端开发平台中进行:工作台 - 1024Code
1.1 基础语法——从Hello World开始
我们来看这么一串代码:
package main //属于main包(程序的入口包)的一部分
import ( //导入fmt包,该包的功能:往屏幕输入输出字符串、格式化字符串
"fmt"
)
func main() {/main函数,程序的入口函数
fmt.Println("Hello, World!")//打印输出 Hello, World!
}
注意,在Go语言中,main函数是程序的入口函数。当我们运行一个Go程序时,操作系统会首先调用main函数来启动程序的执行。main函数是必须的,没有main函数的程序无法运行。 通过定义main函数,我们可以在其中编写程序的逻辑代码,包括调用其他函数、执行各种操作等。main函数可以有参数和返回值,但是在Go语言中,main函数的参数和返回值通常是固定的,即没有参数和返回值。 总之,main函数是Go程序的入口,它负责启动程序的执行,并且是必须的。
当我们需要谁用Println函数来打印字符串时,需要先声明该函数属于fmt包,因此需要在Println前加上fmt.
1.2——变量的声明与赋值
在Go语言中,声明变量有2种方式:
第一种:
var 变量名 = 值,例如:
var a = "initial"//定义a为字符串"initial"
var d = true//定义d为布尔型变量 true
var e float64//定义e为浮点型变量,且未将其赋予初始值
值得注意的是,我们在定义变量a、d的过程中,并未像C/C++那样预先声明变量类型,而是让Go语言根据其被赋予的初始值自动推断变量类型;
而在定义变量e的过程中,因为为赋予其初始值,所以需要在变量名后面加上数据类型float64,即双精度浮点数。我们也可以自己先声明变量类型,同时也赋予其初始值。例如:
var b, c int = 1, 2//声明了两个整数类型的变量b和c,并分别赋值为1和2
第二种:变量名 := 值,例如:
f := float32(e)//将变量e转换为float32类型,并赋值给f
g := a + "foo"//将变量a与字符串"foo"进行拼接,并将结果赋值给变量g
声明常量的方法:
const 变量名 数据类型 = 值,例如:
const s string = "constant"
const h = 500000000
const i = 3e20 / h//定义了一个常量i,它的值是3乘以10的20次方除以变量h的值
类似的,可以在变量名后面加上变量名,也可以不加——让Go根据赋予的初始值自行判断数据类型。
注意:一旦设置了常量,就不可以在后续的代码中再对其进行修改。 例如,我们令上段代码中设定的常量h自增2:
h = h + 2
运行代码会报错:cannot assign to h (declared const)。
我们可以再导入math标准库来使用一些科学计算的函数:
import (
"fmt"
"math"
)
来求sin(h), sin(i), 并将之前的几个变量输出:
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))//注意这里的Sin()中的‘S’为大写
得到运行结果:
constant 500000000 6e+11 -0.28470407323754404 0.7591864109375384
1.3——条件&判断 if else
我们来看这么一段代码:
package main
import "fmt"
func main() {
if 7%2 == 0 { // 如果7除以2的余数等于0
fmt.Println("7 is even") // 打印输出"7 is even"
} else {
fmt.Println("7 is odd") // 打印输出"7 is odd"
} if 8%4 == 0 { // 如果8除以4的余数等于0
fmt.Println("8 is divisible by 4") // 打印输出"8 is divisible by 4"
} if num := 9; num < 0 { //它定义了一个变量`num`并赋值为9。然后,检查num是否小于0
fmt.Println(num, "is negative") // 打印输出num和"is negative"
} else if num < 10 { // 如果num小于10
fmt.Println(num, "has 1 digit") // 打印输出num和"has 1 digit"
} else {
fmt.Println(num, "has multiple digits") // 打印输出num和"has multiple digits"
}
}
观察代码,我们可以发现以下几点规律:
- Go中的
if条件表达式必须是布尔类型(true或false)。不支持非布尔类型的隐式转换,例如不能用非零的整数或非空字符串作为条件:上面这块代码就是错误的,执行会报错显示n := 1 if n { fmt.Println("hello") }non-boolean condition in if statement。
2.不需要用括号条件表达式给括起来;如果用了括号程序也会自行忽略;
3.条件表达式后立即接{,不能像C/C++那样可以把大括号{移到下一行。其实不难发现,不光是在if这里,在函数这里和接下来要谈到的for循环中,需要用到大括号时,{都是紧跟着的;
4.Go中可以使用短声明语法:=在if语句中定义新的变量,这些变量仅在if块中有效;
5.在Go中,else if要写成连在一起的else if,不能写成两个独立的关键字。
1.4——for循环
在Go语言中,循环只有一种——那就是for循环。没有while循环、没有do while循环。接下来我们来看示例:
i := 1
for {
fmt.Println("loop")
break
}
在这里,先定义了一个整型变量i等于1,接下来是一个无限循环(for后面没有跟任何条件),来打印loop并使用break来跳出循环。最终。程序只会输出一个loop。
来看下一个例子:
for j := 7; j < 9; j++ {
fmt.Println(j)
}
这里就很类似于C/C++中的for循环了。定义了一个循环变量j,初始值为7;循环条件为j < 9,每次循环完毕之后执行j++,循环体内的语句为fmt.Println(j),即输出j。很容易想到,最后只会输出7 8。
我们再来看一个例子:
for n := 0; n < 5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
每当n为偶数的时候,就跳出该循环,也就是说会跳过执行下面的语句 fmt.Println(n),直接执行自增命令,再转到循环体语句开始的地方。最终,该程序会输出 1 3。
我们也可以把自增语句移到循环体内。例如:
for i <= 3 {
fmt.Println(i)
i = i + 1
}
1.5——switch case:多选择分支结构
在Go语言中,switch语句是一种用于根据不同条件执行不同代码块的控制流语句。它与其他编程语言中的switch语句类似,但在Go中有一些特有的特点。我们来看示例:
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
可以将其抽象化:
switch expression {
case value1:
// code to be executed if expression equals value1
case value2:
// code to be executed if expression equals value2
...
case valueN:
// code to be executed if expression equals valueN
default:
// code to be executed if expression doesn't match any case
}
在switch语句中,expression是一个表达式或变量的值,而value1、value2等是可能与expression相等的常量或常量表达式。每个case后面的值必须是唯一且可比较的,不能重复。
Go的switch语句有一些特点:
- 自动退出:当匹配到某个
case后,Go会自动退出switch语句,无需显式添加break语句。 - 多值case:一个
case可以包含多个值,用逗号分隔,表示在这些值中任何一个匹配都会执行相应代码块。 - 默认case:如果没有任何
case匹配到expression的值,可以使用default来执行相应的代码块。 - 表达式匹配:
switch的expression可以是任何类型的表达式,不限于整数或字符。
在上述a := 2开头的代码中,因为 a = 2 所以匹配到case 2,执行输出“two”,然后就退出switch语句。
switch后面也可以不接表达式,例如:
t := time.Now() //这里需要导入time包
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
switch { ... }: 这是一个空的switch语句,没有在switch后面跟任何表达式。在这种情况下,它等同于switch true { ... },意味着它将匹配第一个为真(true)的case。
1.6——切片:长度可变的数组
切片是一个动态数组,它可以根据需要自动调整大小。
使用make来创建切片:
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) // c
fmt.Println("len:", len(s)) // 3
s := make([]string, 3): 这行代码创建了一个字符串切片(slice)并将其赋值给变量s。在这里,使用make函数来创建切片,它有两个参数:
- 第一个参数是切片的类型,这里是
[]string,表示字符串类型的切片;创建整数类型的则只需要把string换成int - 第二个参数是切片的长度,这里是
3,表示切片最初包含3个元素的空间。
所以,这行代码创建了一个包含3个元素的字符串切片,并且每个元素的初始值为空字符串 ""。
需要注意的是,由于切片是引用类型,它在创建时实际上只是分配了指向底层数组的引用,并没有直接创建底层数组。底层数组会随着切片的操作而动态扩展或缩减。
如何往切片里增添元素——使用append函数:
s = append(s, "d")
s = append(s, "e", "f")
c := male([]string, len(s))
注意,必须把append的结果赋值给原切片。因为在Go语言中,切片的原理是存储了一个长度加一个容量加一个指向数组的指针,如果长度不够,会发生扩容,并返回一个新的slice。
slice还拥有类似于Python的切片操作,例如:
fmt.Println(s[2:5]) // [c d e]
fmt.Println(s[:5]) // [a b c d e]
fmt.Println(s[2:]) // [c d e f]
其切片的原理和Python完全相同,会从冒号前面的数字下标对应的元素开始,到冒号后面的数字对应的下标的前一个元素结束。注意:这里不支持负数索引。
1.7——map:使用最频繁的数据结构
在Go语言中,map是一种非常有用的数据结构,用于存储键值对(key-value pairs)。它类似于其他编程语言中的字典或关联数组。map 提供了快速的查找功能,使得根据键来获取对应值的操作非常高效。
以下是Go语言中map的一些特点和用法:
创建map: 使用make函数创建一个新的map,格式为make(map[KeyType]ValueType),其中KeyType是键的类型,ValueType是值的类型。
// 创建一个键为string类型,值为int类型的map
m := make(map[string]int)
插入和更新元素: 可以通过指定键来插入或更新map中的元素。
m["apple"] = 50 // 插入键为"apple",值为50的元素
m["banana"] = 30 // 插入键为"banana",值为30的元素
m["apple"] = 60 // 更新键为"apple"的元素的值为60
获取元素: 通过键来获取map中的元素。如果键不存在,将返回值类型的零值。
fmt.Println(m["apple"]) // 输出:60
fmt.Println(m["orange"]) // 输出:0,因为"orange"键不存在,返回int类型的零值
检查元素是否存在: 可以通过使用_, ok := m[key]的形式,检查map中是否存在特定的键。
value, ok := m["apple"]
if ok {
fmt.Println("apple存在,值为:", value)
} else {
fmt.Println("apple不存在")
}
删除元素: 使用delete函数来删除map中的键值对。
delete(m, "banana") // 删除键为"banana"的键值对
遍历map: 使用for range循环来遍历map的所有键值对(range的相关知识点会在下文中介绍)。
for key, value := range m {
fmt.Println(key, ":", value)
}
需要注意的是,map是一个引用类型,当传递它作为函数参数或赋值给另一个变量时,实际上传递的是指向底层数据的引用,而不是副本。这意味着对map的修改会影响原始数据。
另外,由于map是无序的,遍历结果可能会按照不同的顺序返回。如果你需要按照特定顺序遍历map,需要自行处理顺序。如果对元素的顺序要求较高,可以使用slice来替代map。
1.8——range:遍历
在Go语言中,range用于遍历数组、切片、字符串、映射(map)等数据结构。
其主要用法为配合for循环:
for 索引 值 := range 数组/字符串/切片/map
它提供了一种简洁且方便的方式来迭代数据中的元素。range关键字返回两个值:索引(index)和元素的值(value)。
1.遍历数组和切片:
numbers := []int{1, 2, 3, 4, 5}
// 使用range遍历切片中的元素
for index, value := range numbers {
fmt.Println("Index:", index, "Value:", value)
}
运行结果为:
2.遍历字符串:
message := "Hello, Gophers!"
// 使用range遍历字符串中的字符
for index, char := range message {
fmt.Printf("Index: %d, Character: %c\n", index, char)
}
运行结果为:
3.遍历映射(map):
fruits := map[string]int{"apple": 50, "banana": 30, "orange": 40}
// 使用range遍历映射中的键值对
for key, value := range fruits {
fmt.Println("Fruit:", key, "Quantity:", value)
}
运行结果为:
注意:
- 对于数组、切片、字符串,
range返回索引和元素的值。 - 对于映射(map),
range返回键和对应的值。 - 对于字符串遍历,
range返回的char是rune类型,它代表UTF-8编码的Unicode字符。
另外在使用range遍历数据时,如果你只需要元素的值而不关心索引或键,可以使用下划线 _ 来忽略它们:
numbers := []int{1, 2, 3, 4, 5}
// 只遍历切片中的元素值,忽略索引
for _, value := range numbers {
fmt.Println("Value:", value)
}
运行结果为:
1.9——function:函数
在Go语言中,定义函数使用func关键字,具体的语法如下:
func functionName(parameter1 type1, parameter2 type2, ...) returnType {
// 函数体
// 可以包含多条语句
return result // 返回值(如果有的话)
}
上述代码为抽象化的,我们解释一下:
func: 是定义函数的关键字。functionName: 是函数的名字,遵循标识符的命名规则。(parameter1 type1, parameter2 type2, ...): 是函数的参数列表,每个参数由参数名和类型组成,多个参数之间用逗号,分隔。returnType: 是函数的返回值类型,如果函数不返回任何值,则可以省略返回值部分。return result: 是函数返回结果的语句,如果函数有返回值,则使用return关键字返回结果。
接下来,我们来看具体的代码:
// 定义一个函数,计算两个整数的和
func add(x int, y int) int {
return x + y
}
func main() {
result := add(3, 5)
fmt.Println("Result:", result) // 输出:Result: 8
}
在上述示例中,我们定义了一个名为add的函数,它有两个参数 x 和 y,都是整数类型,并返回它们的和作为整数类型的返回值。在main函数中,我们调用了add函数并打印了返回的结果。
需要注意的是,在Go语言中函数的参数和返回值都必须显式声明类型。此外,Go语言中支持多返回值,可以在函数定义时指定多个返回值类型,例如:func functionName() (int, string),然后在函数体中使用return语句返回多个值。
1.10——切片:长度可变的数组
在Go语言中,指针是一种特殊的数据类型,用于存储变量的内存地址。与其他一些编程语言不同,Go语言对指针的使用有一些限制,但它仍然是非常有用的。以下是关于Go语言中指针的一些重要概念和用法:
-
声明指针:在Go语言中,通过在变量类型前添加
*符号来声明一个指针变量。例如,var ptr *int声明了一个指向整数类型的指针变量。 -
初始化指针:指针在声明后需要初始化,通常使用
new关键字来创建一个指针,并分配对应类型的零值。例如,ptr = new(int)会创建一个指向整数类型的指针,并分配一个值为0的整数空间。 -
取地址操作符:使用
&符号可以获取变量的地址,并将其赋值给指针变量。例如,var x int = 42; var ptr *int = &x将ptr指向变量x的内存地址。 -
解引用操作符:使用
*符号可以从指针中获取存储在其指向地址上的值。例如,var value int = *ptr将获取指针ptr所指向的整数值,并将其赋值给value变量。 -
空指针:在Go语言中,未初始化的指针值为
nil,表示指针不指向任何有效的内存地址。当指针为nil时,对其解引用会导致运行时错误。 -
指针作为函数参数:指针常用于在函数间传递引用。通过将指针作为函数参数传递,函数可以直接修改指针所指向的值。这样可以避免在函数调用时进行值的复制,从而提高性能。
-
返回指针:函数可以返回指针,使得函数能够返回在函数内部分配的变量的地址。但应当注意确保在返回指针时,不要返回局部变量的地址,否则可能导致未定义的行为。
指针在Go语言中通常用于以下情况:在函数间传递引用、动态分配内存、操作大型数据结构时避免复制等。然而,在使用指针时,需要小心处理空指针和确保不会返回局部变量的地址,以避免出现潜在的错误。
比如在传递函数参数的时候:
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
函数add2ptr的参数类型为指针变量,指向的是函数的参数本身(参数变量的地址),故该函数会实实在在改变传递进去的参数;而非采用指针类型的函数add2,则对参数的值的改变仅在函数内部生效。
1.11——切片:结构体
在Go语言中,结构体是一种用户自定义的复合数据类型,用于表示一组相关字段的集合。结构体可以包含不同类型的字段,这使得它们非常适合用于建模现实世界中的实体和数据结构。结构体类似于C语言中的结构体,但Go语言对结构体进行了增强,使其更加灵活和强大。
我们来看如何定义一个结构体:使用type关键字和struct关键字来定义一个新的结构体类型。在结构体中,可以列出需要的字段及其类型。例如:
type user struct {
name string
password string
}
定义结构体变量并修改其值的几种方法:
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
不难发现,可以使用.运算符来访问结构体中的字段。
另外,还可以将结构体作为函数的参数传递,这样可以在函数内部修改结构体的字段。
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
1.12——结构体方法
在Go语言中,结构体方法是与特定类型相关联的函数。它们允许我们在结构体上执行特定的操作,类似于面向对象编程中的方法。结构体方法是Go语言中的一种重要特性,可以对结构体进行操作和修改,使代码更加模块化和易于维护。
func (u *user) resetPassword(password string) {
u.password = password
a.resetPassword("2048")
1.13——字符串操操作
接下来我们来看一下Go语言中的字符串操作: 首先导入string库:
import (
"fmt"
"strings"
)
然后,来看一些常用的字符串工具函数:
func main() {
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // true
fmt.Println(strings.Count(a, "l")) // 2
fmt.Println(strings.HasPrefix(a, "he")) // true
fmt.Println(strings.HasSuffix(a, "llo")) // true
fmt.Println(strings.Index(a, "ll")) // 2
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
fmt.Println(strings.Repeat(a, 2)) // hellohello
fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo
fmt.Println(strings.Split("a-b-c", "-")) // [a b c]
fmt.Println(strings.ToLower(a)) // hello
fmt.Println(strings.ToUpper(a)) // HELLO
fmt.Println(len(a)) // 5
b := "你好"
fmt.Println(len(b)) // 6
}
我们来分别介绍一下这些字符串工具函数:
strings.Contains(a, "ll"): 检查字符串a中是否包含子串"ll",返回一个布尔值,如果包含则为true,否则为false。strings.Count(a, "l"): 统计字符串a中子串"l"的出现次数。strings.HasPrefix(a, "he"): 检查字符串a是否以子串"he"开头,返回一个布尔值。strings.HasSuffix(a, "llo"): 检查字符串a是否以子串"llo"结尾,返回一个布尔值。strings.Index(a, "ll"): 在字符串a中查找子串"ll"第一次出现的位置,返回其索引值。如果没有找到则返回-1。strings.Join([]string{"he", "llo"}, "-"): 将字符串切片["he", "llo"]中的元素用-连接成一个新的字符串。strings.Repeat(a, 2): 将字符串a重复2次,生成一个新的字符串。strings.Replace(a, "e", "E", -1): 将字符串a中所有的子串"e"替换为"E",-1表示替换所有匹配项。strings.Split("a-b-c", "-"): 将字符串"a-b-c"按照"-"分割,返回一个字符串切片["a", "b", "c"]。strings.ToLower(a): 将字符串a中的所有字符转换为小写形式。strings.ToUpper(a): 将字符串a中的所有字符转换为大写形式。len(a): 返回字符串a的字节长度,一个ASCII字符占一个字节,一个中文字符占三个字节。len(b): 返回字符串b的字节长度,由于字符串b包含中文字符,所以长度为6,一个中文字符占三个字节。
这些字符串函数是Go语言标准库strings包提供的常用函数,可以用于处理和操作字符串。在使用这些函数时,务必注意字符编码和长度计算,尤其是涉及多字节字符(例如中文)的情况。
1.14——字符串格式化
在标准库的fmt包里,有很多字符串格式化相关的方法。字符串格式化是将数据按照一定的格式转换为字符串的过程。在编程中,我们经常需要将各种数据类型(如整数、浮点数、布尔值等)转换为可读性更好的字符串表示,或者根据特定的格式要求输出字符串。一个很实际的应用就是,在用prinf函数输出时,我们往往需要区分不同类型的变量,例如:整数用%d,浮点数用%f,但是当我们使用格式化输出的时候,就可以不用考虑这个问题:
type point struct {
x, y int
}
func main() {
s := "hello"
n := 123
p := point{1, 2}
fmt.Println(s, n) // hello 123
fmt.Println(p) // {1 2}
fmt.Printf("s=%v\n", s) // s=hello
fmt.Printf("n=%v\n", n) // n=123
fmt.Printf("p=%v\n", p) // p={1 2}
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
f := 3.141592653
fmt.Println(f) // 3.141592653
fmt.Printf("%.2f\n", f) // 3.14
}
其中,fmt.Printf("p=%#v\n", p) 是Go语言中用于格式化输出的一种方式,其中的占位符 %#v 用于将变量 p 的值以Go语言语法的形式格式化输出。
%:表示格式化操作的开始。#:表示输出时输出语法格式,常用于调试和查看变量的值。v:表示值的默认格式,它会根据变量的类型自动选择格式化方式。
1.15——JSON处理
当我们在计算机中处理数据时,数据通常以不同的格式存在。JSON是一种特殊的格式,它非常适合用来在不同的程序之间传递和存储数据。
想象一下,你需要从一台计算机向另一台计算机发送一条消息,告诉它一些数据。但是,计算机之间不能直接说话,所以我们需要找到一种共同的语言。JSON就是这种共同的语言。
JSON数据看起来像这样:
{
"name": "Alice",
"age": 30,
"isStudent": true
}
其中,"name"是名字的标签,"Alice"是名字的值,"age"是年龄的标签,30是年龄的值,"isStudent"是一个标签,true表示是学生。
使用JSON,我们可以将这样的数据轻松地从一个程序发送到另一个程序,而无需担心两者之间使用不同的编程语言。JSON简单易懂,也很容易阅读和写入。它在互联网上被广泛用于传输数据,比如在网页中获取数据、发送请求到服务器,或者在不同的应用程序之间进行数据交换。
总的来说,JSON是一种方便、通用且易于使用的数据格式,它让不同的程序之间可以友好地交流数据。
在Go语言中,JSON的处理是通过标准库中的encoding/json包来完成的。该包提供了用于JSON编码和解码的功能,让我们能够轻松地将Go语言的数据结构转换为JSON格式,并且可以将JSON格式的数据反序列化为Go语言中的数据类型。
Go语言中encoding/json包的主要功能如下:
-
编码(序列化):使用
json.Marshal()函数将Go语言的数据结构转换为JSON格式的字节切片。 -
解码(反序列化):使用
json.Unmarshal()函数将JSON格式的数据解析为Go语言中的数据类型。 -
标签:可以在Go语言的结构体字段上使用
json标签来指定在JSON中对应的字段名称,从而实现JSON字段和Go结构体字段之间的映射。
下面是一些常见的JSON操作示例:
- 编码(Go结构体转JSON字符串):
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
p := Person{
Name: "Alice",
Age: 30,
}
jsonData, err := json.Marshal(p)
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
jsonString := string(jsonData)
fmt.Println("JSON string:", jsonString)
}
- 解码(JSON字符串转Go结构体):
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
jsonString := `{"name":"Bob","age":25}`
var p Person
err := json.Unmarshal([]byte(jsonString), &p)
if err != nil {
fmt.Println("JSON decoding error:", err)
return
}
fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
}
以上代码展示了Go语言中如何将Go结构体转换为JSON字符串,以及如何将JSON字符串转换为Go结构体。通过json标签,我们可以对字段进行重命名,使得在Go语言结构体和JSON之间可以方便地进行映射。
在Go语言中,
err通常用于表示函数执行过程中可能出现的错误。它是error类型的变量,用于在函数中返回错误信息,以便调用方能够根据错误情况采取适当的处理措施。 Go语言中的函数通常会返回两个值,第一个值是函数执行的结果,第二个值是error类型,表示函数执行时是否发生了错误。如果函数执行成功,err值为nil,表示没有发生错误。如果函数执行失败,err值将包含错误信息,调用方可以根据这个错误信息来进行异常处理。
总的来说,Go语言内置的encoding/json包提供了简单而强大的JSON处理功能,让我们能够在Go程序中方便地进行JSON数据的编码和解码操作,从而实现数据交换和存储。
在编码中,先是定义了一个名为Person的结构体,其中有两个字段:Name和Age,注意:首字母都是大写;另外,还在结构体字段之后加上了json标签来指定转换成JSON后对应的字段名称。
通过json标签,我们可以对字段进行重命名,使得在Go语言结构体和JSON之间可以方便地进行映射。
总的来说,Go语言内置的encoding/json包提供了简单而强大的JSON处理功能,让我们能够在Go程序中方便地进行JSON数据的编码和解码操作,从而实现数据交换和存储。
1.15——时间处理
在Go语言中,时间处理是一个非常重要且常见的任务。Go语言内置了标准库time,提供了强大的时间处理功能,包括获取当前时间、格式化时间、解析时间字符串、进行时间计算等。
以下是一些常用的时间处理操作:
- 获取当前时间:可以使用
time.Now()函数来获取当前本地时间。它返回一个time.Time类型的对象,表示当前时间。
package main
import (
"fmt"
"time"
)
func main() {
currentTime := time.Now()
fmt.Println("Current Time:", currentTime)
}
- 格式化时间:使用
time.Format()函数可以将时间按照指定的格式转换为字符串。
package main
import (
"fmt"
"time"
)
func main() {
currentTime := time.Now()
formattedTime := currentTime.Format("2006-01-02 15:04:05")
fmt.Println("Formatted Time:", formattedTime)
}
- 解析时间字符串:使用
time.Parse()函数可以将字符串解析为time.Time类型的时间对象。
package main
import (
"fmt"
"time"
)
func main() {
dateString := "2023-07-26 12:34:56"
parsedTime, err := time.Parse("2006-01-02 15:04:05", dateString)
if err != nil {
fmt.Println("Error parsing time:", err)
return
}
fmt.Println("Parsed Time:", parsedTime)
}
- 时间计算:可以对时间进行加减等计算,得到新的时间对象。
package main
import (
"fmt"
"time"
)
func main() {
currentTime := time.Now()
// 加上一小时
oneHourLater := currentTime.Add(time.Hour)
fmt.Println("One Hour Later:", oneHourLater)
// 减去一天
oneDayAgo := currentTime.Add(-24 * time.Hour)
fmt.Println("One Day Ago:", oneDayAgo)
}
Go语言的时间处理功能非常强大,可以满足大部分的时间需求。在进行时间处理时,建议使用time.Time类型来存储和操作时间,避免使用字符串来表示时间,以确保时间的正确性和可靠性。同时,注意在跨时区和跨夏令时的场景下,要特别小心时间的处理,确保程序的行为符合预期。