From 9fb278b5191c895beddcb6945f8531376c864396 Mon Sep 17 00:00:00 2001 From: zl <1309515883@qq.com> Date: Fri, 10 May 2024 16:14:47 +0800 Subject: [PATCH] =?UTF-8?q?'Go-=E6=95=B0=E6=8D=AE=E7=B1=BB=E5=9E=8B'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 4 +- docs/backend/go/base/index.md | 555 +++++++++++++++++----------------- 2 files changed, 282 insertions(+), 277 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e5e58a2..803e2ac 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,5 +15,7 @@ "typeof", "unshift", "vuepress" - ] + ], + "workbench.colorTheme": "One Dark Pro", + "workbench.preferredDarkColorTheme": "Default Dark Modern" } diff --git a/docs/backend/go/base/index.md b/docs/backend/go/base/index.md index 73139cf..0e5ac3d 100644 --- a/docs/backend/go/base/index.md +++ b/docs/backend/go/base/index.md @@ -37,11 +37,13 @@ func main() { } ``` -> 注:同一个目录下最好只有一个 main 函数 -> -> go build 文件 => 生成 .exe 文件,可直接在终端执行 -> -> go run 文件 => 执行 +::: warning 注意 + +1. 同一个目录下最好只有一个 main 函数 +2. go build 文件 => 生成 .exe 文件,可直接在终端执行 +3. go run 文件 => 执行 + +::: ### 变量、常量及其作用域 @@ -49,7 +51,7 @@ func main() { ::: warning 注意 -1. 变量必须先定义才能使用,且不使用会编译报错;常量没有必须使用的要求 +1. 变量必须先定义才能使用,且不使用会编译报错,但匿名变量 `_` 除外;常量没有必须使用的要求 2. go 是静态语言,要求变量/常量的类型和赋值类型一致 3. 命名不能冲突,常量建议使用全大写 4. main 函数外声明的变量拥有 package 作用域,短声明不能用来声明 package 作用域的变量 @@ -148,86 +150,205 @@ default: ::: -### 实数 +### 数据类型 + +#### bool -#### 声明浮点型变量 +零值是 false -只要数字含有小数部分,那么它的类型就是 float64 +Print 家族函数,会把 bool 类型的值打印成 true/false 文本 ```go -/* 下面三个语句的效果是一样的 */ -days := 365.2425 -var days = 365.2425 -var days float64 = 365.2425 -/* 如果使用一个整数来初始化某个变量,则必须指定它的类型为 float64,否则它就是一个整数类型 */ -var answer float32 = 42 +launch := false +fmt.Printf("Type %T from %[1]v\n", launch) +launchText := fmt.Sprintf("%v", launch) +fmt.Println("Ready for launch:", launchText) // Ready for launch: false + +var yesNo string +if launch { + yesNo = "yes" +} else { + yesNo = "no" +} +fmt.Println("Ready for launch:", yesNo) // Ready for launch: no ``` -> Go 语言里有两种浮点数类型: -> -> 默认是 float64 -> -> - 64 位的浮点类型 -> - 占用 8 字节 -> -> float32 -> -> - 占用 4 字节 -> - 精度比 float64 低 -> - 有时叫做单精度浮点数类型 +布尔数据类型仅表示 true 或 false。布尔类型的值不会隐式或显式转换为任何其他类型 -想使用单精度类型,必须再声明变量的时候指定该类型: +如果想使用 string(false),int(false);bool(1), bool("yes") 等类似的方法进行转换,那么 Go 编译器会报错 + +#### 字符和 string + +字符串的零值 "" ```go -var pi64 = math.Pi -var pi32 float32 = math.Pi -fmt.Println(pi64) // 3.141592653589793 -fmt.Println(pi32) // 3.1415927 +var empty string +fmt.Println(empty == "") // true ``` -- 当处理大量数据时,例如 3D 游戏中的数千个顶点,使用 float32 牺牲精度来节省内存是很有意义的 -- math 包里面的函数操作都是 float64 类型,所以应该首选使用 float 64 类型,除非有足够的理由不去使用它 +声明 -#### 零值 +```go +peace := "peace" +var peace = "peace" +var peace string = "peace" +``` -Go 里面的每个类型都有一个默认值,它称作零值 +##### 字符串字面值/原始字符串字面值 -当声明变量却不对它进行初始化的时候,它的值就是零值 +字符串字面值(string literal)可以包含转义字符,例如 \n + +但如果想得到 \n 而不是换行的话,可以使用 ` 来代替 ",这叫做原始字符串字面值(raw string literal) ```go -var price float64 -fmt.Println(price) // 0 +fmt.Println("peace be upon you\nupon you be peace") +fmt.Println(`strings can span multiple lines with the \n escape sequence`) +fmt.Println(` +peace be upon you +upon you be peace +`) ``` -#### 显示浮点类型 +##### 字符,code points,runes,bytes -- Print 或 Println 打印浮点类型的时候,默认的行为是尽可能地多显示几位小数 -- Printf 函数,结合 %f 格式化动词来指定显示小数的位数 +Unicode 联盟为超过 100 万个字符分配了相应的数值,这个数叫做 code point + +- 例如:65 代表 A,128515 代表 😃 + +为了表示这样的 unicode code point,Go 提供了 rune 类型,它是 int32 的别名 + +byte 是 unit 8 类型的别名,目的是用于二进制数据 + +- byte 倒是可以表示由 ASCII 定义的英语字符,它是 Unicode 的一个子集(共 128 个字符) + +##### 类型别名 + +类型别名就是同一个类型的另一个名字 + +- 所以,rune 和 int32 可以互换使用 + +也可以自定义类型别名,语法如下 ```go -third := 1.0 / 3 -fmt.Println(third) // 0.3333333333333333 -fmt.Printf("%v\n", third) // 0.3333333333333333 -fmt.Printf("%f\n", third) // 0.333333 -fmt.Printf("%.3f\n", third) // 0.333 -fmt.Printf("%4.2f\n", third) // 0.33 -fmt.Printf("%05.2f\n", third) // 00.33,默认是空格填充 +type byte = uint8 +type rune = int32 ``` -> 浮点类型不适合用于金融类计算,为了尽量最小化舍入错误,建议先做乘法,再做除法 +##### 打印 -#### 如何比较浮点类型 +如果想打印字符而不是数值,使用 c% 格式化动词 ```go -piggyBank := 0.1 -piggyBank += 0.2 -fmt.Println(piggyBank == 0.3) // false +fmt.Printf("%c", 128515) // 😃 +``` -fmt.Println(math.Abs(piggyBank - 0.3) < 0.0001) +任何整数类型都可以使用 %c 打印,但是 rune 意味着该数值表示了一个字符 + +##### 字符 + +字符字面值使用 '' 括起来,例如 'A' + +如果没有指定字符类型的话,Go 会推断它的类型为 rune + +```go +grade := 'A' +var grade1 = 'A' +var grade2 rune = 'A' ``` -#### 整数类型 +> 这里的 grade 仍然包含一个数值,本例中就是 65,它是 A 的 code point + +字符字面值也可以用 byte 类型 +```go +var star byte = '*' +``` + +##### string + +可以给某个变量赋予不同的 string 值,但是 string 本身是不可变的 + +```go +message := "shalom" +c := message[5] +fmt.Printf("%c\n", c) // m +message[5] = 'd' // 报错 +``` + +##### Caesar cipher 凯撒加密法 + +凯撒加密法是一种简单的加密方法,它是通过将每个字符移动固定数目的位置来实现的 + +```go +c := 'a' +c = c + 3 +fmt.Printf("%c", c) // d +if c > 'z' { + c = c - 26 +} +``` + +##### ROT13 + +ROT13 (旋转 13) 是凯撒加密在 20 世纪的变体, 它会把字母替换成 +13 后对应的字母 + +```go +originalMessage := "uv vagreangvbany fcnpr fgngvba" +for i := 0; i < len(originalMessage); i++ { + c := originalMessage[i] + if c >= 'a' && c <= 'z' { + c = c + 13 + if c > 'z' { + c = c - 26 + } + } + fmt.Printf("%c", c) +} +``` + +##### Go 的内置函数 len + +```go +message := "uv vagreangvbany fcnpr fgngvba" +fmt.Println(len(message)) // 32 +``` + +> 本例中 len 返回 message 所占的 byte 数 + +##### UTF-8 + +Go 中的字符串是用 UTF-8 编码的,UTF-8 是 Unicode Code Point 的几种编码之一 + +UTF8 是一种有效率的可变长度的编码,每个 code point 可以是 8 位、16 位或 32 位的 + +通过使用可变长度编码,UTF-8 使得从 ASCII 的转换变得简单明了,因为 ASCII 字符与其 UTF-8 编码对应的字符是相同的 + +UTF-8 是万维网的主要字符编码,它是由 Ken Thompson(Go 语言的设计者之一) 于 1992 年发明的 + +```go +question := "¿Cómo estás?" +fmt.Println(len(question), "bytes") // 15 +fmt.Println(utf8.RuneCountInString(question), "runes") // 12 + +c,size := utf8.DecodeRuneInString(question) +fmt.Printf("First rune: %c %v bytes", c, size) // First rune: ¿ 2 bytes +``` + +##### range + +使用 range 关键字,可以遍历各种集合 + +```go +question := "¿Cómo estás?" +for i, c := range question { + fmt.Printf("%v %c\n", i, c) +} +``` + +#### 数值类型 +零值是 0 + +##### 整数 Go 提供了 10 种整数类型(不可以存小数部分,范围有限,通常根据数值范围来选取整数类型) - 5 种整数类型是有符号的,能表示正数、0、负数 @@ -255,22 +376,7 @@ int 和 uint 是针对目标设备优化的类型 > 如果在比较老的 32 位设备上,使用了超过 20 亿的整数,并且代码还能运行,那么最好使用 int64 和 uint64 来代替 int 和 uint -#### 打印数据类型 - -在 Printf 函数里面,可以使用 %T 格式化动词来打印变量的类型 - -```go -year := 2018 -fmt.Printf("Type %T for %v\n", year, year) // Type int for 2018 -a := "text" -fmt.Printf("Type %T for %[1]v\n", a) // Type string for text -b := 3.14 -fmt.Printf("Type %T for %[1]v\n", b) // Type float64 for 3.14 -c := true -fmt.Printf("Type %T from %[1]v\n", c) // Type bool from true -``` - -#### uint8 +###### uint8 取值范围 0-255 @@ -279,18 +385,18 @@ fmt.Printf("Type %T from %[1]v\n", c) // Type bool from true var red, green, blue unit8 = 0, 141, 213 ``` -#### 十六进制表示法 +###### 十六进制表示法 -Go 语言里,在数前面加上 0x 前缀,就可以用十六进制的形式来表示 +Go 语言里,在数前面加上 `0x` 前缀,就可以用十六进制的形式来表示 ```go var red, green, blue unit8 = 0, 141, 213 var red, green, blue unit8 = 0x00, 0x8d, 0xd5 ``` -#### 打印十六进制 +**打印十六进制** -打印十六进制的数,用 %x 格式化动词 +打印十六进制的数,用 `%x` 格式化动词 ```go fmt.Printf("%x %x %x", red, green, blue) @@ -298,7 +404,7 @@ fmt.Printf("%x %x %x", red, green, blue) fmt.Printf("color: #%02x%02x%02x;", red, green, blue) ``` -#### 整数环绕 +###### 整数环绕 所有的整数都有一个取值范围,超出这个范围,就会发生“环绕” @@ -325,7 +431,7 @@ future := time.Unix(12622780800, 0) fmt.Println(future) // 2370-01-01 08:00:00 +0800 CST ``` -#### 打印每个 bit +###### 打印每个 bit 使用 %b 格式化动词 @@ -336,7 +442,7 @@ green++ fmt.Printf("%08b\n", green) // 00000100 ``` -#### 整数类型的最大值、最小值 +###### 整数类型的最大值、最小值 - math 包里,为与架构无关的整数类型,定义了最大、最小值常量 ```go @@ -345,7 +451,96 @@ fmt.Printf("%08b\n", green) // 00000100 ``` - 而 int 和 uint,可能是 32 位 或 64 位的 -#### 比较大的数 +##### 浮点数 +只要数字含有小数部分,那么它的类型就是 `float64` + +```go +/* 下面三个语句的效果是一样的 */ +days := 365.2425 +var days = 365.2425 +var days float64 = 365.2425 +/* 如果使用一个整数来初始化某个变量,则必须指定它的类型为 float64,否则它就是一个整数类型 */ +var answer float32 = 42 +``` + +:::warning 注意 + + Go 语言里有两种浮点数类型: + + **默认是 float64** + + - 64 位的浮点类型 + - 占用 8 字节,大约 1.8e308 + +**float32** + + - 占用 4 字节,大约 3.4e38 + - 精度比 float64 低 + - 有时叫做单精度浮点数类型 + +::: + +想使用单精度类型,必须再声明变量的时候指定该类型: + +```go +var pi64 = math.Pi +var pi32 float32 = math.Pi +fmt.Println(pi64) // 3.141592653589793 +fmt.Println(pi32) // 3.1415927 +``` + +- 当处理大量数据时,例如 3D 游戏中的数千个顶点,使用 float32 牺牲精度来节省内存是很有意义的 +- math 包里面的函数操作都是 float64 类型,所以应该首选使用 float 64 类型,除非有足够的理由不去使用它 + +###### 打印浮点类型 +- Print 或 Println 打印浮点类型的时候,默认的行为是尽可能地多显示几位小数 +- Printf 函数,结合 %f 格式化动词来指定显示小数的位数 + +```go +third := 1.0 / 3 +fmt.Println(third) // 0.3333333333333333 +fmt.Printf("%v\n", third) // 0.3333333333333333 +fmt.Printf("%f\n", third) // 0.333333 +fmt.Printf("%.3f\n", third) // 0.333 +fmt.Printf("%4.2f\n", third) // 0.33 +fmt.Printf("%05.2f\n", third) // 00.33,默认是空格填充 +``` + +> 浮点类型不适合用于金融类计算,为了尽量最小化舍入错误,建议先做乘法,再做除法 + +###### 如何比较浮点类型 + +```go +piggyBank := 0.1 +piggyBank += 0.2 +fmt.Println(piggyBank == 0.3) // false + +fmt.Println(math.Abs(piggyBank - 0.3) < 0.0001) +``` + +##### 复数 +将复数分为两部分,如下表所示。float32和float64也是这些复数的一部分。内建函数从它的虚部和实部创建一个复数,内建虚部和实部函数提取这些部分 + +- complex64 包含float32作为实数和虚数分量的复数。 +- complex128 包含float64作为实数和虚数分量的复数。 + +``` go +package main +import "fmt" + +func main() { + + var a complex128 = complex(6, 2) + var b complex64 = complex(9, 2) + fmt.Println(a) // (6+2i) + fmt.Println(b) // (9+2i) + + //显示类型 + fmt.Printf("a的类型是 %T 以及"+ "b的类型是 %T", a, b) // a的类型是 complex128 以及b的类型是 complex64 +} +``` + +### 比较大的数 浮点类型可以存储非常大的数值,但是精度不高 @@ -409,176 +604,6 @@ fmt.Println(24000000000000000000000/299792/86400) // 926568346646 ### 多语言文本 -#### 声明字符串 - -声明 - -```go -peace := "peace" -var peace = "peace" -var peace string = "peace" -``` - -字符串的零值 - -```go -var empty string -fmt.Println(empty == "") // true -``` - -#### 字符串字面值/原始字符串字面值 - -字符串字面值(string literal)可以包含转义字符,例如 \n - -但如果想得到 \n 而不是换行的话,可以使用 ` 来代替 ",这叫做原始字符串字面值(raw string literal) - -```go -fmt.Println("peace be upon you\nupon you be peace") -fmt.Println(`strings can span multiple lines with the \n escape sequence`) -fmt.Println(` -peace be upon you -upon you be peace -`) -``` - -#### 字符,code points,runes,bytes - -Unicode 联盟为超过 100 万个字符分配了相应的数值,这个数叫做 code point - -- 例如:65 代表 A,128515 代表 😃 - -为了表示这样的 unicode code point,Go 提供了 rune 类型,它是 int32 的别名 - -byte 是 unit 8 类型的别名,目的是用于二进制数据 - -- byte 倒是可以表示由 ASCII 定义的英语字符,它是 Unicode 的一个子集(共 128 个字符) - -#### 类型别名 - -类型别名就是同一个类型的另一个名字 - -- 所以,rune 和 int32 可以互换使用 - -也可以自定义类型别名,语法如下 - -```go -type byte = uint8 -type rune = int32 -``` - -#### 打印 - -如果想打印字符而不是数值,使用 c% 格式化动词 - -```go -fmt.Printf("%c", 128515) // 😃 -``` - -任何整数类型都可以使用 %c 打印,但是 rune 意味着该数值表示了一个字符 - -#### 字符 - -字符字面值使用 '' 括起来,例如 'A' - -如果没有指定字符类型的话,Go 会推断它的类型为 rune - -```go -grade := 'A' -var grade1 = 'A' -var grade2 rune = 'A' -``` - -> 这里的 grade 仍然包含一个数值,本例中就是 65,它是 A 的 code point - -字符字面值也可以用 byte 类型 - -```go -var star byte = '*' -``` - -#### string - -可以给某个变量赋予不同的 string 值,但是 string 本身是不可变的 - -```go -message := "shalom" -c := message[5] -fmt.Printf("%c\n", c) // m -message[5] = 'd' // 报错 -``` - -#### Caesar cipher 凯撒加密法 - -凯撒加密法是一种简单的加密方法,它是通过将每个字符移动固定数目的位置来实现的 - -```go -c := 'a' -c = c + 3 -fmt.Printf("%c", c) // d -if c > 'z' { - c = c - 26 -} -``` - -#### ROT13 - -ROT13 (旋转 13) 是凯撒加密在 20 世纪的变体, 它会把字母替换成 +13 后对应的字母 - -```go -originalMessage := "uv vagreangvbany fcnpr fgngvba" -for i := 0; i < len(originalMessage); i++ { - c := originalMessage[i] - if c >= 'a' && c <= 'z' { - c = c + 13 - if c > 'z' { - c = c - 26 - } - } - fmt.Printf("%c", c) -} -``` - -#### Go 的内置函数 - -len 是 Go 语言的一个内置函数 - -```go -message := "uv vagreangvbany fcnpr fgngvba" -fmt.Println(len(message)) // 32 -``` - -> 本例中 len 返回 message 所占的 byte 数 - -#### UTF-8 - -Go 中的字符串是用 UTF-8 编码的,UTF-8 是 Unicode Code Point 的几种编码之一 - -UTF8 是一种有效率的可变长度的编码,每个 code point 可以是 8 位、16 位或 32 位的 - -通过使用可变长度编码,UTF-8 使得从 ASCII 的转换变得简单明了,因为 ASCII 字符与其 UTF-8 编码对应的字符是相同的 - -UTF-8 是万维网的主要字符编码,它是由 Ken Thompson(Go 语言的设计者之一) 于 1992 年发明的 - -```go -question := "¿Cómo estás?" -fmt.Println(len(question), "bytes") // 15 -fmt.Println(utf8.RuneCountInString(question), "runes") // 12 - -c,size := utf8.DecodeRuneInString(question) -fmt.Printf("First rune: %c %v bytes", c, size) // First rune: ¿ 2 bytes -``` - -#### range - -使用 range 关键字,可以遍历各种集合 - -```go -question := "¿Cómo estás?" -for i, c := range question { - fmt.Printf("%v %c\n", i, c) -} -``` - #### 类型不能混用 连接两个字符串,使用 + 运算符 @@ -602,9 +627,9 @@ earthDays := 365.2425 fmt.Println("I am", age * earthDays / marsDays, "years old on Mars.") // invalid operation: age * earthDays (mismatched types int and float64) ``` -#### 数值间类型转换 +#### 类型转换 -整数类型转换为浮点类型 +##### 整数类型转换为浮点类型 ```go age := 41 @@ -612,7 +637,7 @@ age := 41 marsAge := float64(age) ``` -浮点类型转换为整数类型,小数点后边的部分会被截断,而不是舍入 +##### 浮点类型转换为整数类型,小数点后边的部分会被截断,而不是舍入 ```go earthDays := 365.2425 @@ -643,9 +668,7 @@ if bh < math.MinInt16 || bh > math.MaxInt16 { } ``` -#### 字符串转换 - -把 rune、byte 转换为 string +##### 把 rune、byte 转换为 string ```go var pi rune = 960 @@ -686,26 +709,6 @@ if err != nil { fmt.Println(countdown) // 10 ``` -#### 布尔类型的转换 - -Print 家族函数,会把 bool 类型的值打印成 true/false 文本 - -```go -launch := false -launchText := fmt.Sprintf("%v", launch) -fmt.Println("Ready for launch:", launchText) // Ready for launch: false - -var yesNo string -if launch { - yesNo = "yes" -} else { - yesNo = "no" -} -fmt.Println("Ready for launch:", yesNo) // Ready for launch: no -``` - -如果想使用 string(false),int(false);bool(1), bool("yes") 等类似的方法进行转换,那么 Go 编译器会报错 - ### 函数 使用 func 关键字