Go语言基础语法

131 阅读6分钟

写在开始:笔者也是首次学习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" 
    } 
}

观察代码,我们可以发现以下几点规律:

  1. Go中的if条件表达式必须是布尔类型(truefalse)。不支持非布尔类型的隐式转换,例如不能用非零的整数或非空字符串作为条件:
    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是一个表达式或变量的值,而value1value2等是可能与expression相等的常量或常量表达式。每个case后面的值必须是唯一且可比较的,不能重复。

Go的switch语句有一些特点:

  1. 自动退出:当匹配到某个case后,Go会自动退出switch语句,无需显式添加break语句。
  2. 多值case:一个case可以包含多个值,用逗号分隔,表示在这些值中任何一个匹配都会执行相应代码块。
  3. 默认case:如果没有任何case匹配到expression的值,可以使用default来执行相应的代码块。
  4. 表达式匹配:switchexpression可以是任何类型的表达式,不限于整数或字符。

在上述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)
}

运行结果为:

23bced483138bd0eae53b564617a61ee.png

2.遍历字符串:

message := "Hello, Gophers!"
// 使用range遍历字符串中的字符
for index, char := range message {
    fmt.Printf("Index: %d, Character: %c\n", index, char)
}

运行结果为:

image.png

3.遍历映射(map):

fruits := map[string]int{"apple": 50, "banana": 30, "orange": 40}
// 使用range遍历映射中的键值对
for key, value := range fruits {
    fmt.Println("Fruit:", key, "Quantity:", value)
}

运行结果为:

image.png

注意:

  • 对于数组、切片、字符串,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)
}

运行结果为:

image.png

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的函数,它有两个参数 xy,都是整数类型,并返回它们的和作为整数类型的返回值。在main函数中,我们调用了add函数并打印了返回的结果。

需要注意的是,在Go语言中函数的参数和返回值都必须显式声明类型。此外,Go语言中支持多返回值,可以在函数定义时指定多个返回值类型,例如:func functionName() (int, string),然后在函数体中使用return语句返回多个值。

1.10——切片:长度可变的数组

在Go语言中,指针是一种特殊的数据类型,用于存储变量的内存地址。与其他一些编程语言不同,Go语言对指针的使用有一些限制,但它仍然是非常有用的。以下是关于Go语言中指针的一些重要概念和用法:

  1. 声明指针:在Go语言中,通过在变量类型前添加*符号来声明一个指针变量。例如,var ptr *int 声明了一个指向整数类型的指针变量。

  2. 初始化指针:指针在声明后需要初始化,通常使用new关键字来创建一个指针,并分配对应类型的零值。例如,ptr = new(int) 会创建一个指向整数类型的指针,并分配一个值为0的整数空间。

  3. 取地址操作符:使用&符号可以获取变量的地址,并将其赋值给指针变量。例如,var x int = 42; var ptr *int = &xptr指向变量x的内存地址。

  4. 解引用操作符:使用*符号可以从指针中获取存储在其指向地址上的值。例如,var value int = *ptr 将获取指针ptr所指向的整数值,并将其赋值给value变量。

  5. 空指针:在Go语言中,未初始化的指针值为nil,表示指针不指向任何有效的内存地址。当指针为nil时,对其解引用会导致运行时错误。

  6. 指针作为函数参数:指针常用于在函数间传递引用。通过将指针作为函数参数传递,函数可以直接修改指针所指向的值。这样可以避免在函数调用时进行值的复制,从而提高性能。

  7. 返回指针:函数可以返回指针,使得函数能够返回在函数内部分配的变量的地址。但应当注意确保在返回指针时,不要返回局部变量的地址,否则可能导致未定义的行为。

指针在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  
}

我们来分别介绍一下这些字符串工具函数:

  1. strings.Contains(a, "ll"): 检查字符串a中是否包含子串"ll",返回一个布尔值,如果包含则为true,否则为false
  2. strings.Count(a, "l"): 统计字符串a中子串"l"的出现次数。
  3. strings.HasPrefix(a, "he"): 检查字符串a是否以子串"he"开头,返回一个布尔值。
  4. strings.HasSuffix(a, "llo"): 检查字符串a是否以子串"llo"结尾,返回一个布尔值。
  5. strings.Index(a, "ll"): 在字符串a中查找子串"ll"第一次出现的位置,返回其索引值。如果没有找到则返回-1。
  6. strings.Join([]string{"he", "llo"}, "-"): 将字符串切片["he", "llo"]中的元素用-连接成一个新的字符串。
  7. strings.Repeat(a, 2): 将字符串a重复2次,生成一个新的字符串。
  8. strings.Replace(a, "e", "E", -1): 将字符串a中所有的子串"e"替换为"E"-1表示替换所有匹配项。
  9. strings.Split("a-b-c", "-"): 将字符串"a-b-c"按照"-"分割,返回一个字符串切片["a", "b", "c"]
  10. strings.ToLower(a): 将字符串a中的所有字符转换为小写形式。
  11. strings.ToUpper(a): 将字符串a中的所有字符转换为大写形式。
  12. len(a): 返回字符串a的字节长度,一个ASCII字符占一个字节,一个中文字符占三个字节。
  13. 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包的主要功能如下:

  1. 编码(序列化):使用json.Marshal()函数将Go语言的数据结构转换为JSON格式的字节切片。

  2. 解码(反序列化):使用json.Unmarshal()函数将JSON格式的数据解析为Go语言中的数据类型。

  3. 标签:可以在Go语言的结构体字段上使用json标签来指定在JSON中对应的字段名称,从而实现JSON字段和Go结构体字段之间的映射。

下面是一些常见的JSON操作示例:

  1. 编码(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)
}
  1. 解码(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的结构体,其中有两个字段:NameAge,注意:首字母都是大写;另外,还在结构体字段之后加上了json标签来指定转换成JSON后对应的字段名称。

通过json标签,我们可以对字段进行重命名,使得在Go语言结构体和JSON之间可以方便地进行映射。

总的来说,Go语言内置的encoding/json包提供了简单而强大的JSON处理功能,让我们能够在Go程序中方便地进行JSON数据的编码和解码操作,从而实现数据交换和存储。

1.15——时间处理

在Go语言中,时间处理是一个非常重要且常见的任务。Go语言内置了标准库time,提供了强大的时间处理功能,包括获取当前时间、格式化时间、解析时间字符串、进行时间计算等。

以下是一些常用的时间处理操作:

  1. 获取当前时间:可以使用time.Now()函数来获取当前本地时间。它返回一个time.Time类型的对象,表示当前时间。
package main

import (
    "fmt"
    "time"
)

func main() {
    currentTime := time.Now()
    fmt.Println("Current Time:", currentTime)
}
  1. 格式化时间:使用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)
}
  1. 解析时间字符串:使用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)
}
  1. 时间计算:可以对时间进行加减等计算,得到新的时间对象。
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类型来存储和操作时间,避免使用字符串来表示时间,以确保时间的正确性和可靠性。同时,注意在跨时区和跨夏令时的场景下,要特别小心时间的处理,确保程序的行为符合预期。