当前位置: 首页 > news >正文

山西网站搜索排名优化公司网站o2o

山西网站搜索排名优化公司,网站o2o,网站交互性,wordpress 自定义导航**本人是第六届字节跳动青训营#xff08;后端组#xff09;的成员。本文由博主本人整理自该营的日常学习实践#xff0c;首发于稀土掘金#xff1a;#x1f517;Go语言入门指南#xff1a;基础语法和常用特性解析 | 青训营 本文主要梳理自第六届字节跳动青训营#xff…**本人是第六届字节跳动青训营后端组的成员。本文由博主本人整理自该营的日常学习实践首发于稀土掘金Go语言入门指南基础语法和常用特性解析 | 青训营 本文主要梳理自第六届字节跳动青训营后端组-Go语言原理与实践第一节王克纯老师主讲。同时博主也结合了自己的理解和其他参考资料对Go语言基础语法和常用特性进行解析。后续若有需要补充的地方本文会相应地进行更新。 如何安装和配置Go的开发环境这里就不细说了有需要的朋友可以自己去搜索教程。 我的go开发环境 *本地IDEGoLand 2023.1.2 *go1.20.6 其它参考资料 《Go语言圣经》 面向加薪学习-欢喜哥 Go语言官方文档 一、Hello World程序 1、在GoLand中运行一个Hello World 首先新建一个go的项目 写下如下代码 没错Go语言中的Hello World程序长这样 package main // 声明 main 包import fmt // 导入 fmt 包打印字符串时需要用到func main(){ // 声明 main 主函数fmt.Println(Hello, World!) // 打印 }Go是一门编译型语言Go语言的工具链将源代码及其依赖转换成计算机的机器指令静态编译。Go语言提供的工具都通过一个单独的命令go调用go命令有一系列子命令。最简单的一个子命令就是run。这个命令编译一个或多个以.go结尾的源文件链接库文件并运行最终生成的可执行文件。 在终端输入 go run main.go 即可编译并且运行这个go文件go build main.go 即可编译而不运行这个文件。 在GoLand中直接右键点击运行或点击func main左边的绿色小箭头就能运行出第一个Go语言程序了 Go语言原生支持Unicode它可以处理全世界任何语言的文本。 接下来我们来讨论一下这个Hello World程序本身。 2、package包 声明包 Go语言以包package为管理单位包类似于其它语言里的库libraries或者模块modules。 每个 Go 源文件必须以一条package声明语句先声明它所属的包。比如上面Hello world程序中的package main它表示该文件属于哪个包。一般来说程序的包名由目录名来指定 不过一个go文件声明的包名也可以与其目录名不一致但同一个目录下的go文件声明的包名必须一致 main包与main函数 main包比较特殊。它定义了一个独立可执行的程序而不是一个库。 在main包里的main函数也很特殊它是整个程序执行时的入口C系语言差不多都这样比如JavaC#。main函数所做的事情就是程序所做的。当然main函数一般调用其它包里的函数完成很多工作如fmt.Println。 也就是说main包是一个go语言程序的入口包func main()是go语言的入口函数。一个go语言项目程序中 必须有且仅有一个main包main 函数只能声明在 main 包中不能声明在其他包中一个 main 包中也必须有且仅有一个 main 函数。 如果一个程序没有main包或main包中没有main函数那么它在编译的时候将会出错。无法生成执行文件。 如果执意运行GoLand会弹出下面的界面提示没有入口包main若用命令行执行则会报错 从main函数的声明可以得知在 Go 语言中所有函数都以关键字 func 开头的定义格式如下 func 函数名 (参数列表) (返回值列表){函数体 }总结go语言中的包 Go 语言的包与文件夹是一一对应的它具有以下几点特性 一个目录下的同级文件属于同一个包。包名可以与其目录名不同。main 包是 Go 语言程序的入口包一个 Go 语言程序必须有且仅有一个 main 包。如果一个程序没有 main 包那么编译时将会出错无法生成可执行文件。 3、import导入包 在包声明之后是 import 语句用于导入程序中所依赖的包导入的包名使用双引号包围格式如下 import name // 其中 import 是导入包的关键字name 为所导入包的名字。有一点需要注意导入的包中不能含有代码中没有使用到的包否则 Go 编译器会报编译错误例如 imported and not used: xxx”xxx” 表示包名。如果在GoLand中出现了冗余的包它会自动给你删除冗余包的导入。也可以使用一个 import 关键字导入多个包此时需要用括号( )将包的名字包围起来并且每个包名占用一行也就是写成下面的样子 import(name1name2 )二、程序结构 1、命名 Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名都遵循一个简单的命名规则一个名字必须以一个字母 Unicode 字母或下划线开头后面可以跟任意数量的字母、数字或下划线。 且大写字母和小写字母是不同的。 Go语言中关键字有25个关键字不能用于自定义名字 对于Go语言字段的命名有以下注意点 如果一个名字是在函数内部定义那么它就只在函数内部有效。如果是在函数外部定义那么将在当前包的所有文件中都可以访问相当于是全局变量。名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的注必须是在函数外部定义的包级名字包级函数名本身也是包级名字那么它是可以被外部的包访问的相当于Java中的public例如fmt包的Printf函数就是导出的可以在fmt包外部访问。包本身的名字一般总是用小写字母。 此外命名的长度没有逻辑上的限制但是Go语言的编码风格是尽量使用短小的名字尤其是局部变量。通常来说如果一个名字的作用域比较大生命周期也比较长那么用长的名字将会更有意义。 习惯上Go语言的变量以 驼峰式 命名而不是用下划线分隔。而像ASCII和HTML这样的缩略词则避免使用大小写混合的写法它们可能被称为htmlEscape、HTMLEscape或escapeHTML但不会是escapeHtml。 2、变量 王克纯讲师的给出的变量声明代码如下 package mainimport (fmtmath )func main() {var a initial //声明string类型变量省略变量类型var b, c int 1, 2 //完整的声明var d true //声明bool类型变量var e float64 //声明浮点类型变量f : float32(e) //短变量声明g : a foo //用加号连接两个字符串fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0fmt.Println(g) // initialapple }非常清晰易懂。Go语言是一门强类型语言每一个变量都有它自己的变量类型常见的变量类型包括字符串整数浮点型布尔型等。Go 语言的字符串是内置类型可以直接通过加号拼接也能够直接用等于号去比较两个字符串。 在Go语言里面大部分运算符的使用和优先级都和 C 或者 C 类似这里就不再概述。 下面梳理了一下Go中变量的声明几种规则。 声明变量的一般形式是使用 var 关键字。 var 变量名字 类型 表达式var num int 10其中“变量的类型”或“表达式 ” 这两个部分可以省略其中的一个。也就是说也可以这样写 var num1 10 //省略类型 var num2 int //省略初始化表达式如果省略的是类型信息那么将根据等号右边的初始化表达式来推导该变量是什么类型。 如果初始化表达式被省略那么将用零值初始化该变量。 数值类型变量对应的零值是0布尔类型变量对应的零值是false字符串类型对应的零值是空字符串接口或引用类型包括slice、指针、map、chan和函数变量对应的零值是nil意义相当于其它语言的null数组或结构体等聚合类型对应的零值是每个元素或字段都对应的该类型的零值 零值初始化机制可以确保每个声明的变量总是有一个值因此在Go语言中不存在未初始化的变量。 接下来来讲一讲如何声明变量。 方法一声明一个变量但不赋值采用默认值 package mainimport fmtfunc main(){// 方法一声明一个变量, 默认的值是0var a intfmt.Println(a , a)fmt.Printf(a的类型是: %T\n, a)// %T 是一种格式化占位符用于在 fmt.Printf 或 fmt.Sprintf 等函数中//将变量的类型信息格式化并插入到字符串中。 }/*输出a 0 a的类型是: int*/方法二声明一个变量并初始化一个值 package mainimport fmtfunc main(){// 方法二声明一个变量, 初始化一个值var num1 int 100fmt.Printf(num1 %d, type of num1 %T\n, num1, num1)var num2 string hellofmt.Printf(num2 %s, num2的类型是: %T\n, num2, num2) }方法三在初始化时省去数据类型自动推导当前数据类型 package mainimport fmtfunc main(){// 方法三在初始化时省去数据类型自动推导当前数据类型var num1 100fmt.Printf(num1 %d, type of num1 %T\n, num1, num1)var num2 hellofmt.Printf(num2 %s, num2的类型是: %T\n, num2, num2) }方法四多变量声明 也可以在一个声明语句中同时声明一组变量或用一组初始化表达式声明并初始化一组变量。如果省略每个变量的类型可以声明多个类型不同的变量每个变量的类型都由初始化表达式去推导 var i, j, k int // int, int, int var b, f, s true, 2.3, four // bool, float64, string这样就可以用以下的方式来交换两个变量的值这比C语言Java更加简洁。 i, j j, i // 交换 i 和 j 的值方法五短变量声明 在函数内部只能在函数内有一种简短变量声明语句的形式可用于声明和初始化局部变量。 它以名字 : 表达式形式声明变量变量的类型根据表达式来自动推导。 anim : gif.GIF{LoopCount: nframes} freq : rand.Float64() * 3.0 t : 0.0因为短变量的声明方式更加简洁和灵活它被广泛用于大部分的局部变量的声明和初始化。 var形式的声明语句往往是用于需要显式指定变量类型的地方或者因为变量稍后会被重新赋值而初始值无关紧要的地方。 和var形式声明语句一样简短变量声明语句也可以用来声明和初始化一组变量 i, j : 0, 1但是《Go语言圣经》中指出这种同时声明多个变量的方式应该限制只在可以提高代码可读性的地方使用比如for语句的循环的初始化语句部分。 总而言之需要记住:是一个变量声明语句而是一个变量赋值操作。 和普通var形式的变量声明语句一样短变量声明语句也可以用函数的返回值来声明和初始化变量 f, err : os.Open(name) //声明变量f,err并接收函数的返回值来初始化它们 if err ! nil {return err } // ...use f... f.Close()短变量声明的注意事项 短变量声明中左边的变量可能并不是全部都是刚刚声明的。如果有一些变量在与当前语句相同的作用域中已经声明过了那么短变量声明语句对这些已经声明过的变量就只有赋值行为了。如果是在外部的作用域中声明的那依然会在短变量声明时重新创建一个新的变量。 例如在下面的代码中两个语句的功能是不同的 in, err : os.Open(infile) //声明了in和err两个变量 // ... out, err : os.Create(outfile) //只声明了out一个变量然后对已经声明的err进行了赋值操作并且简短变量声明语句中必须至少要声明一个新的变量 f, err : os.Open(infile) // ... f, err : os.Create(outfile) // compile error: no new variables//解决的方法是第二个简短变量声明语句改用普通的多重赋值语句 var f,err os.Create(outfile)3、常量 常量就是只读属性定义后不允许被修改。定义一个常量使用 const 关键字且定义的时候就要赋值。 package mainimport (fmtmath )func main() {const s string constant //声明常量const h 500000000const i 3e20 / hfmt.Println(s, h, i, math.Sin(h), math.Sin(i)) }可以使用const来定义枚举类型 package mainimport fmt// const来定义枚举类型 const (BEIJING 0SHANGHAI 1SHENZHEN 2 )func main() {fmt.Println(BEIJING , BEIJING) // 0fmt.Println(SHANGHAI , SHANGHAI) // 1fmt.Println(SHENZHEN , SHENZHEN) // 2 }4、if-else package mainimport fmtfunc main() {if 7%2 0 {fmt.Println(7 is even)} else {fmt.Println(7 is odd)}if 8%4 0 {fmt.Println(8 is divisible by 4)}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)} }Go语言里面的 if-else 写法和 C 或者 C 类似。不同点一个是 if 后面不加括号。如果你写了括号那么在保存的时候编译器会自动去掉。第二个不同点是 Golang 里面的 if 它必须后面接大括号不能像 C 或者 C 一样直接把 if 后面的大括号另起一行写。 比较有趣的一点是当写错了刷个新或者切屏一下再切回来编译器就会很贴心地帮你把错误的书写方式更改过来了。 5、for package mainimport fmtfunc main() {i : 1//死循环for {fmt.Println(loop)break //使用break跳出循环}for j : 7; j 9; j {fmt.Println(j)}for n : 0; n 5; n {if n%2 0 {continue //使用continue跳过当前循环}fmt.Println(n)}for i 3 {fmt.Println(i)i i 1} }在Go里没有whiledo while循环只有一种for循环毕竟一个for完全够用。 最简单的for循环就是for后面啥也不写这代表一个死循环。循环途中想要退出使用break。也可以使用经典的C中的循环即 for(i : 0; i n; i)。这中间三段任何一段都可以省略。在循环里面可以用 break 或者 continue 来跳出或者继续循环这与C语言循环中的break和continue的用法是相似的。 6、switch package mainimport (fmttime )func main() {a : 2switch 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)}t : time.Now()switch {case t.Hour() 12:fmt.Println(Its before noon)default:fmt.Println(Its after noon)} }/* 输出 two Its after noon */Go语言里面的 switch 分支结构与 C 或者 C 的不同点在于 在Go语言中不需要显式地加break。执行完匹配的case代码块后它会直接退出 switch-case 如果没有任何一个匹配会执行 default 的代码块。Go不像C一样如果不显式地加 break 就会继续往下跑完所有的 case。正如上面的代码第一个输出的是two而不是把case 2下面的打印语句全部执行一遍。 相比C或者CGo语言里面的switch-case功能更加强大。switch后可以使用任意的变量类型甚至函数可以用来取代任意的 if-else 语句。 1一个case多个条件 在 Go 中case 后可以接多个条件多个条件之间是 或 的关系用逗号 , 相隔。 month : 5 switch month { case 1, 3, 5, 7, 8, 10, 12:fmt.Println(该月份有 31 天) case 4, 6, 9, 11:fmt.Println(该月份有 30 天) case 2:fmt.Println(该月份闰年为 29 天非闰年为 28 天) default:fmt.Println(输入有误) }2选择语句高级写法 switch 还有另外一种写法它包含一个 statement 可选语句部分该可选语句在表达式之前运行 switch month : 5; month { case 1, 3, 5, 7, 8, 10, 12:fmt.Println(该月份有 31 天) case 4, 6, 9, 11:fmt.Println(该月份有 30 天) case 2:fmt.Println(该月份闰年为 29 天非闰年为 28 天) default:fmt.Println(输入有误) }这样这里 month 变量的作用域就仅限于这个 switch 内了。 3fallthrough 语句 正常情况下 switch-case 语句在执行时只要有一个 case 满足条件就会直接退出 switch-case 如果一个都没有满足才会执行 default 的代码块。不同于其他语言需要在每个 case 中添加 break 语句才能退出。 使用 fallthrough 语句可以在已经执行完成的 case 之后向下“穿透”一层。 package mainimport (fmttime )func main() {a : 2switch a {case 1:fmt.Println(one)case 2:fmt.Println(two)fallthroughcase 3:fmt.Println(three)case 4, 5:fmt.Println(four or five)default:fmt.Println(other)} }/* 输出 two three */fallthrough 语句是 case 子句的最后一个语句。如果它出现在了 case 语句的中间编译会不通过。 4无表达式的 switch switch 后面的表达式可以省略然后在case中写条件分支。此时switch-case的作用相当于一个 if-elseif-else。 score : 88 switch { case score 90 score 100:fmt.Println(grade A) case score 80 score 90:fmt.Println(grade B) case score 70 score 80:fmt.Println(grade C) case score 60 score 70:fmt.Println(grade D) case score 60:fmt.Println(grade E) }这比使用多个 if-else 逻辑更加清晰。 5switch 后可接函数 switch 后面可以接一个函数只要保证 case 后的值类型与函数的返回值一致即可。 package mainimport fmtfunc getResult(args ...int) bool {for _, v : range args {if v 60 {return false}}return true }func main() {chinese : 88math : 90english : 95switch getResult(chinese, math, english) {case true:fmt.Println(考试通过)case false:fmt.Println(考试未通过)} }三、复合数据类型 基本数据类型是Go语言世界的原子它包括整型int浮点数float32复数布尔型bool字符串string和常量const。 而复合数据类型是以不同的方式组合基本类型而构造出来的。主要有四种数组、slice、map和结构体。 数组和结构体是聚合类型它们的值由许多元素或成员字段的值组成。 数组是由同构的元素组成——每个数组元素都是完全相同的类型结构体则是由异构的元素组成的。 数组和结构体都是有固定内存大小的数据结构相比之下slice和map则是动态的数据结构它们可以根据需要动态增长。 1、数组 package mainimport fmtfunc main() {var a [5]int //一个可以存放 5 个int元素的数组 aa[4] 100fmt.Println(get:, a[2])fmt.Println(len:, len(a))b : [5]int{1, 2, 3, 4, 5}fmt.Println(b)var twoD [2][3]intfor i : 0; i 2; i {for j : 0; j 3; j {twoD[i][j] i j}}fmt.Println(2d: , twoD) }数组就是一个具有编号且长度固定的元素序列。对于一个数组可以很方便地取特定索引的值或者往特定索引取存储值然后也能够直接去打印一个数组。不过在真实业务代码里面很少直接使用数组因为它长度是固定的用的更多的是切片。 数组遍历 使用 for range 循环可以获取数组每个索引以及索引上对应的元素 func showArr() {arr : [...]string{Go123, Go456, Go789}for index, value : range arr {fmt.Printf(arr[%d]%s\n, index, value)}for _, value : range arr {fmt.Printf(value%s\n, value)} }输出结果 注意Go 中的数组是值类型而不是引用类型。当数组赋值给一个新的变量时该变量会得到一个原始数组的一个副本。如果对新变量进行更改不会影响原始数组。 func arrByValue() {arr : [...]string{Go123, Go456, Go789}copy : arrcopy[0] Golangfmt.Println(arr)fmt.Println(copy) }输出结果 2、slice 切片不同于数组可以任意更改长度也有更丰富的操作。 package mainimport fmtfunc main() {s : make([]string, 3)s[0] as[1] bs[2] cfmt.Println(get:, s[2]) // cfmt.Println(len:, len(s)) // 3s append(s, d)s append(s, e, f)fmt.Println(s) // [a b c d e f]c : make([]string, len(s))copy(c, s)fmt.Println(c) // [a b c d e f]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]good : []string{g, o, o, d}fmt.Println(good) // [g o o d] }切片是对数组的一个连续片段的引用切片是一个引用类型。 切片本身不拥有任何数据它们只是对现有数组的引用每个切片值都会将数组作为其底层的数据结构。 slice 的语法和数组很像只是没有固定长度而已。 创建切片 a.使用 []Type 可以创建一个带有 Type 类型元素的切片 // 声明整型切片 var numList []int //未赋值numList默认值是nil// 声明一个空切片 var numListEmpty []int{}b.使用 make 函数构造一个切片格式为 make([]Type, size, cap) package mainimport (fmt )func main() {// 创建一个初始长度为 3容量为 5 的整数切片slice : make([]int, 3, 5)fmt.Println(切片长度:, len(slice))fmt.Println(切片容量:, cap(slice)) }Type表示切片的元素类型。size表示切片的长度包含的元素数量。cap表示切片的容量capability底层数组的长度即可以容纳的元素数量上限。 c.通过对数组进行片段截取创建一个切片 arr : [5]string{Go123, Go456, Go789, Go1101112, Go131415} var s1 arr[1:4] //左闭右开 fmt.Println(arr) fmt.Println(s1)slice的切片操作s[i:j]其中0 ≤ i≤ j≤ cap(s)用于创建一个新的slice引用s的从第i个元素开始到第j-1个元素的子序列。新的slice将只有j-i个元素。如果i位置的索引被省略的话将使用0代替如果j位置的索引被省略的话将使用len(s)代替。 有点类似于Python但不同于PythonGo不支持负数索引。 切片的长度和容量 一个 slice 由三个部分构成指针、长度和容量。 指针指向第一个 slice 元素对应的底层数组元素的地址要注意的是 slice 的第一个元素并不一定就是数组的第一个元素。 长度对应 slice 中元素的数目长度不能超过容量。 容量一般是从 slice 的开始位置到底层数据的结尾位置。 简单的讲容量就是从创建切片索引开始的底层数组中的元素个数而长度是切片中的元素个数。 如果切片操作超出上限将导致一个 panic 异常。 s : make([]int, 3, 5) fmt.Println(s[10]) //panic: runtime error: index out of range [10] with length 3切片元素的修改 切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中。 使用 append 可以将新元素追加到切片上。append 函数的定义是 func append(slice []Type, elems ...Type) []Type 。其中 elems ...Type 在函数定义中表示该函数接受参数 elems 的个数是可变的。这些类型的函数被称为可变函数。 当新的元素被添加到切片时如果容量不足会创建一个新的数组。现有数组的元素被复制到这个新数组中并返回新的引用。 3、数组与切片的区别 数组Array 固定长度 数组是一种固定长度的数据结构定义数组时需要指定其长度并且长度在创建后不能改变。值类型 数组是值类型当将数组赋值给另一个数组时会复制数组的内容。内存 分配 数组的内存是一次性分配的所以它们在内存中占据一块连续的存储空间。声明和初始化 数组的声明和初始化可以使用大括号 {}也可以在声明时指定元素的值。 // 声明一个包含 5 个整数的数组 var arr [5]int //必须显式指定长度不能省略 arr : [5]int{1, 2, 3, 4, 5}切片Slice 可变长度 切片是动态长度的数据结构可以根据需要进行扩容或缩减。引用类型 切片是引用类型复制切片时只会复制一个引用而不是整个数据内容。内存 分配 切片的底层是由数组支持的底层数组的长度可能大于切片的长度。声明和初始化 切片的声明和初始化使用 make 函数或者通过从现有数组或切片中切取子集来创建。 // 使用 make 函数创建一个包含 3 个整数的切片 slice : make([]int, 3) // 从现有数组或切片中切取子集创建切片 subSlice : arr[1:3] // 包含索引 1 和 2 的元素 // 直接定义一个切片 不用指定长度切片会根据元素个数自动确定长度 nums : []int{1, 2, 3, 4, 5}总结来说数组和切片都用于存储一组相同类型的数据但数组具有固定长度和值类型特点而切片具有可变长度和引用类型特点。通常情况下切片更加灵活因为它们支持动态大小调整。 4、map map 是实际使用过程中最频繁用到的数据结构。在其它语言里叫做字典或者哈希。 package mainimport fmtfunc main() {m : make(map[string]int) //key的类型是stringvalue的类型是intm[one] 1m[two] 2fmt.Println(m) // map[one:1 two:2]fmt.Println(len(m)) // 2fmt.Println(m[one]) // 1fmt.Println(m[unknow]) // 0r, ok : m[unknow]fmt.Println(r, ok) // 0 falsedelete(m, one)m2 : map[string]int{one: 1, two: 2}var m3 map[string]int{one: 1, two: 2}fmt.Println(m2, m3) }我们可以用 make 来创建一个空 map。这里需要两个类型第一个是 key 的类型这里是 string另一个是 value 的类型这里是int。我们可以从里面去存储或者取出键值对。可以用 delete 从里面删除键值对。golang的map是完全无序的遍历的时候不会按照字母顺序也不会按照插入顺序输出而是随机顺序。 map是引用类型的当 map 被赋值为一个新变量的时候它们指向同一个内部数据结构。因此改变其中一个变量就会影响到另一变量。 可以在声明的时候直接对map进行初始化 m : map[int]string{1: Go123,2: Go456,3: Go789, } fmt.Println(m)也可以只声明但不初始化后续通过添加操作将元素添加进map。 map 操作 a.添加元素 // 使用 map[key] value 向 map 添加元素。 m[4] Go101112b.更新元素 // 若 key 已存在使用 map[key] value 可以直接更新对应 key 的 value 值。 m[4] GoGoGoc.获取元素 // 直接使用 map[key] 即可获取对应 key 的 value 值,如果 key不存在,会返回其 value 类型的零值。 fmt.Println(m[4])d.删除元素 //使用 delete(map, key)可以删除 map 中的对应 key 键值对,如果 key 不存在,delete也不会报错。 delete(m, 4)e.判断 key 是否存在 // 如果我们想知道 map 中的某个 key 是否存在可以使用下面的语法value, ok : map[key] v3, ok : m[3] fmt.Println(ok) fmt.Println(v3)v5, ok : m[5] fmt.Println(ok) fmt.Println(v5)map 的下标读取可以返回两个值第一个值为当前 key 的 value 值第二个值表示对应的 key 是否存在若存在 ok 为 true 若不存在则 ok 为 false 。 f.遍历 map // 遍历 map 中所有的元素需要用 for range 循环。 for key, value : range m {fmt.Printf(key: %s, value: %s\n, key, value) }g.获取 map 长度 // 使用 len 函数可以获取 map 长度 fmt.Println(len(m)) // 4四、range关键字 range 是一个关键字用于迭代数组、切片、映射、通道或字符串中的元素。 range 的使用方式取决于所遍历的数据类型。对于一个 slice 或者一个 map可以用 range 来快速遍历这样代码能够更加简洁。 比如 range 遍历数组或slice的时候会返回两个值第一个是索引第二个是对应位置的值遍历map也会返回key和value两个值。如果不需要索引可以用下划线来忽略。 package mainimport fmtfunc main() {nums : []int{2, 3, 4}sum : 0for i, num : range nums {sum numif num 2 {fmt.Println(index:, i, num:, num) // index: 0 num: 2}}fmt.Println(sum) // 9m : map[string]string{a: A, b: B}for k, v : range m {fmt.Println(k, v) // b 8; a A}for k : range m {fmt.Println(key, k) // key a; key b} }以下是 range 在不同数据类型中的使用示例 1. 数组和切片 nums : []int{2, 3, 4}// 使用 range 遍历切片 for index, value : range nums {fmt.Printf(Index: %d, Value: %d\n, index, value) }2. Map person : map[string]int{Alice: 25, Bob: 30}// 使用 range 遍历映射 for key, value : range person {fmt.Printf(Name: %s, Age: %d\n, key, value) }3. Channel ch : make(chan int)// 使用 range 遍历通道等待通道关闭 go func() {for num : range ch {fmt.Println(Received:, num)} }()ch - 1 ch - 2 close(ch) // 关闭通道4. 字符串 在遍历字符串时value 会表示当前字符的 Unicode 码点值即“char”。 text : Hello, Go!// 使用 range 遍历字符串 for index, char : range text {fmt.Printf(Index: %d, Char: %c\n, index, char) }输出 在这些示例中range 的语法是相同的 for index, value : range collection其中index 是当前迭代的索引或键value 是当前元素的值。 需要注意的是在使用 range 迭代切片、数组、映射和通道时会为每个迭代创建一个新的变量副本而不是直接访问原始数据。这对于遍历数据结构并进行操作是很有用的。 五、函数 这个是 Golang 里面一个简单的实现两个变量相加的函数。 Golang 和其他很多语言不一样的是变量类型是后置的。 package mainimport fmtfunc add(a int, b int) int {return a b }func add2(a, b int) int {return a b }func exists(m map[string]string, k string) (v string, ok bool) {v, ok m[k]return v, ok }func main() {res : add(1, 2)fmt.Println(res) // 3v, ok : exists(map[string]string{a: A}, a)fmt.Println(v, ok) // A True }Golang 里面的函数原生支持返回多个值。在实际的业务逻辑代码里面几乎所有的函数都返回两个值第一个是真正的返回结果第二个值是一个错误信息。 六、指针 Go里面也支持指针。但是相比 C 和 C 里面的指针支持的操作很有限。指针的一个主要用途就是对于传入参数进行修改。 package mainimport fmtfunc add2(n int) {n 2 }func add2ptr(n *int) {*n 2 }func main() {n : 5add2(n)fmt.Println(n) // 5add2ptr(n)fmt.Println(n) // 7 }package mainimport fmttype user struct {name stringpassword string }func main() {a : user{name: wang, password: 1024}b : user{wang, 1024}c : user{name: wang}c.password 1024var d userd.name wangd.password 1024fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}fmt.Println(checkPassword(a, haha)) // falsefmt.Println(checkPassword2(a, haha)) // false }func checkPassword(u user, password string) bool {return u.password password }func checkPassword2(u *user, password string) bool {return u.password password }这个函数试图把一个变量2。但是单纯像上面add2()这种写法其实是无效的因为传入函数的参数实际上是一个拷贝。add2()中的这个n2是对原变量n的拷贝进行了2回到main()中并不起作用。如果想要在函数中对外部变量的修改起作用的话那么我们需要把那个类型写成指针类型。 为了类型匹配调用的时候会加一个 符号。 七、结构体 结构体是带类型的字段的集合。 package mainimport fmttype user struct {name stringpassword string }func main() {//可以用结构体的名称去初始化一个结构体变量构造的时候需要传入每个字段的初始值a : user{name: wang, password: 1024}b : user{wang, 1024}c : user{name: wang}c.password 1024//也可以用这种键值对的方式去指定初始值这样可以只对一部分字段进行初始化var d userd.name wangd.password 1024fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}fmt.Println(checkPassword(a, haha)) // falsefmt.Println(checkPassword2(a, haha)) // false }func checkPassword(u user, password string) bool {return u.password password }func checkPassword2(u *user, password string) bool {return u.password password }比如这里 user 结构体包含了两个字段name 和 password。 同样的结构体也能支持指针这样能够实现对于结构体的修改也可以在某些情况下避免一些大结构体的拷贝开销。 结构体方法 在 Golang 里可以为结构体定义一些方法。 结构体方法是与特定类型的结构体相关联的函数。结构体方法允许为结构体类型定义“附加的功能”并且可以通过结构体实例调用这些方法。这在面向对象编程中类似于类的方法。 要定义一个结构体方法需要 先定义一个结构体类型。然后为该结构体类型定义一个方法。 比如将上面例子中的 checkPassword()从一个普通函数改成结构体方法。这样用户可以通过 a.checkPassword(“xx”) 这样去调用。 具体的代码修改就是把第一个参数加上括号写到函数名称前面。在实现结构体的方法的时候也有两种写法一种是带指针一种是不带指针。它们的区别是如果带指针的话那么就可以对这个结构体去做修改。如果不带指针的话那实际上操作的是一个拷贝就无法对结构体进行修改。 package mainimport fmttype user struct {name stringpassword string }func (u user) checkPassword(password string) bool {return u.password password }func (u *user) resetPassword(password string) {u.password password }func main() {a : user{name: wang, password: 1024}a.resetPassword(2048)fmt.Println(a.checkPassword(2048)) // true }以下例子演示了如何在 Go 中定义和使用结构体方法 package mainimport (fmt )// 定义一个结构体类型 type Rectangle struct {Width float64Height float64 }// 为 Rectangle 结构体定义一个方法 func (r Rectangle) Area() float64 {return r.Width * r.Height }func main() {// 创建一个 Rectangle 结构体实例rect : Rectangle{Width: 10, Height: 5}// 调用结构体方法area : rect.Area()fmt.Println(矩形的面积:, area) }在上述示例中我们定义了一个名为 Rectangle 的结构体类型具有 Width 和 Height 两个字段。然后我们为 Rectangle 结构体定义了一个名为 Area 的方法用于计算矩形的面积。 结构体方法的语法如下 func (receiver Type) MethodName() ReturnType {// 方法实现 }receiver是方法的接收器它定义了哪个结构体类型可以调用该方法。在上面的例子中receiver 是 Rectangle 结构体类型。MethodName是为该结构体定义的方法的名称。ReturnType是该方法返回的数据类型。 结构体方法在 Go 中被广泛使用用于将操作与数据结构关联起来提高代码的可读性和封装性。通过使用方法可以将特定类型的功能封装到结构体中并通过结构体实例调用这些方法来执行相关操作。 八、错误处理 在 Go 语言里符合语言习惯的做法是使用一个单独的返回值来传递错误信息。 不同于 Java 使用的异常Go语言的处理方式能够很清晰地知道是哪个函数返回了错误并且能用简单的 if else 来处理错误。 在函数定义时可以在函数的返回值类型列表后加一个 error 代表这个函数可能会返回错误。那么在函数实现的时候 return 就需要同时 return 两个值如果出现错误那么 return nil 和一个 error如果没有错误那么返回原本的结果和 nil。 package mainimport (errorsfmt )type user struct {name stringpassword string }func findUser(users []user, name string) (v *user, err error) {for _, u : range users {if u.name name {return u, nil}}return nil, errors.New(not found) }func main() {u, err : findUser([]user{{wang, 1024}}, wang)if err ! nil {fmt.Println(err)return}fmt.Println(u.name) // wangif u, err : findUser([]user{{wang, 1024}}, li); err ! nil {fmt.Println(err) // not foundreturn} else {fmt.Println(u.name)} }九、字符串操作 在标准库 strings 包里有很多常用的字符串工具函数比如contains判断一个字符串里是否包含有另一个字符串 count 字符串计数 index 查找某个字符串的位置。 join 连接多个字符串 repeat 重复多个字符串 replace 替换字符串。 package mainimport (fmtstrings )func main() {a : hellofmt.Println(strings.Contains(a, ll)) // truefmt.Println(strings.Count(a, l)) // 2fmt.Println(strings.HasPrefix(a, he)) // truefmt.Println(strings.HasSuffix(a, llo)) // truefmt.Println(strings.Index(a, ll)) // 2fmt.Println(strings.Join([]string{he, llo}, -)) // he-llofmt.Println(strings.Repeat(a, 2)) // hellohellofmt.Println(strings.Replace(a, e, E, -1)) // hEllofmt.Println(strings.Split(a-b-c, -)) // [a b c]fmt.Println(strings.ToLower(a)) // hellofmt.Println(strings.ToUpper(a)) // HELLOfmt.Println(len(a)) // 5b : 你好fmt.Println(len(b)) // 6 }字符串的格式化 Go 语言中的格式化占位符和 C 语言中的格式化占位符在某些方面是相似的它们用于将变量的值以特定格式插入到字符串中。以下是一些关于 Go 和 C 中格式化占位符的比较 相似 %d: 用于格式化整数十进制。%f: 用于格式化浮点数。%s: 用于格式化字符串。%c: 用于格式化字符。%p: 用于格式化指针地址。 不同 在 Go 语言中没有像 C 语言中的 %i 一样的格式化占位符。在 Go 中可以使用 %d 来格式化整数十进制。Go 语言中的 %v 是一个通用的占位符可以用于格式化任何类型的变量。它会根据变量的类型自动选择合适的格式。Go 语言中的 %T 用于格式化变量的类型。在 C 语言中一些特定于整数长度的占位符如 %ld、%lld 等用于格式化长整型。而在 Go 中可以使用 %d 格式化不同长度的整数Go 会自动处理。 在标准库的 fmt 包里面有很多的字符串格式相关的方法可以很轻松地用 %v 来打印任意类型的变量而不需要区分数字字符串。也可以用 %v 打印详细结果%#v 则更详细。 package mainimport fmttype point struct {x, y int }func main() {s : hellon : 123p : point{1, 2}fmt.Println(s, n) // hello 123fmt.Println(p) // {1 2}fmt.Printf(s%v\n, s) // shellofmt.Printf(n%v\n, n) // n123fmt.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) // pmain.point{x:1, y:2}f : 3.141592653fmt.Println(f) // 3.141592653fmt.Printf(%.2f\n, f) // 3.14 }十、JSON处理 在 Go 编程语言中JSONJavaScript Object Notation处理是一项常见的任务用于在应用程序之间传递和存储结构化的数据。Go 提供了内置的标准库来处理 JSON 数据使得编码将数据转换为 JSON 格式和解码将 JSON 数据转换为数据结构变得非常容易。 对于一个已有的结构体我们可以什么都不做只要保证每个字段的第一个字母是大写也就是是公开字段那么这个结构体就能用 json.Marshal 序列化变成一个 JSON 的字符串。序列化之后的字符串也能够用 json.Unmarshal 反序列化到一个空的变量里。 以下是在 Go 中处理 JSON 数据的基本流程 1. 编码 序列化 将数据转换为 JSON 格式。 使用 encoding/json 包中的 Marshal 函数可以将 Go 数据结构编码为 JSON 格式的字节流。这通常用于在应用程序中将数据发送到其他系统或存储到文件中。 json.Marshal()返回的是一个字节切片 []byte和一个err通过把字节切片转换成字符串看到其中的内容。 package mainimport (encoding/jsonfmt )type Person struct {Name string json:nameAge int json:ageEmail string json:email }func main() {person : Person{Name: Alice, Age: 30, Email: aliceexample.com}jsonBytes, err : json.Marshal(person)if err ! nil {fmt.Println(JSON encoding error:, err)return}fmt.Println(string(jsonBytes)) //将字节切片转换为字符串查看内容 }输出 可以看到这样转换过来的json格式可读性较差包含很长的字符串并且没有空白缩进。为了生成便于阅读的格式另一个json.MarshalIndent函数将产生整齐缩进的输出。该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进 data, err : json.MarshalIndent(person, , ) if err ! nil {log.Fatalf(JSON marshaling failed: %s, err) } fmt.Printf(%s\n, data)当需要把多个结构体即一个结构体切片中的各个元素都转换成json格式时这个函数就非常有用了。 2. 解码反 序列化 将 JSON 数据转换为 Go 数据结构。 使用 encoding/json 包中的 Unmarshal 函数可以将 JSON 格式的字节流解码为相应的 Go 数据结构。这通常用于从外部系统获取 JSON 数据并在应用程序中进行处理。 package mainimport (encoding/jsonfmt )type Person struct {Name string json:nameAge int json:ageEmail string json:email }func main() {jsonStr : {name:Bob,age:25,email:bobexample.com}var person Personerr : json.Unmarshal([]byte(jsonStr), person)if err ! nil {fmt.Println(JSON decoding error:, err)return}fmt.Println(Name:, person.Name)fmt.Println(Age:, person.Age)fmt.Println(Email:, person.Email) }结构体字段的标签json:field可以用于指定 JSON 键与 Go 结构体字段之间的映射关系。这在编码和解码过程中非常有用确保正确的字段匹配。 补充代码示例 package mainimport (encoding/jsonfmt )type userInfo struct {Name stringAge int json:ageHobby []string }func main() {a : userInfo{Name: wang, Age: 18, Hobby: []string{Golang, TypeScript}}buf, err : json.Marshal(a)if err ! nil {panic(err)}fmt.Println(buf) // [123 34 78 97...] 字节切片fmt.Println(string(buf)) // {Name:wang,age:18,Hobby:[Golang,TypeScript]}buf, err json.MarshalIndent(a, , \t)if err ! nil {panic(err)}fmt.Println(string(buf))var b userInfoerr json.Unmarshal(buf, b)if err ! nil {panic(err)}fmt.Printf(%#v\n, b) // main.userInfo{Name:wang, Age:18, Hobby:[]string{Golang, TypeScript}} }十一、时间处理 在Go语言里面最常用的就是 time.Now() 来获取当前时间。也可以用 time.Date 构造一个带时区的时间。t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()有很多方法来获取这个时间点的年 月 日 小时 分钟 秒。用点 .Sub 对两个时间进行减法得到一个时间段diff。时间段diff又可以得到它有多少小时多少分钟、多少秒diff.Minutes(), diff.Seconds()在和某些系统交互的时候我们经常会用到时间戳可以用 .Unix 来获取时间戳。t.Format() 可以将时间进行格式化。time.Parse()用于将字符串解析为时间对象。它在处理时间格式的字符串时非常有用可以将字符串转换为对应的 time.Time 类型。 package mainimport (fmttime )func main() {now : time.Now()fmt.Println(now) // 2023-08-10 20:49:44.8566865 0800 CST m0.007017101t : time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)t2 : time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)fmt.Println(t) // 2022-03-27 01:25:36 0000 UTCfmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25fmt.Println(t.Format(2006-01-02 15:04:05)) // 2022-03-27 01:25:36diff : t2.Sub(t) //t2和t之间的获得时间差fmt.Println(diff) // 1h5m0sfmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900t3, err : time.Parse(2006-01-02 15:04:05, 2022-03-27 01:25:36)if err ! nil {panic(err)}fmt.Println(t3 t) // truefmt.Println(now.Unix()) // 1648738080 }十二、数字解析 Go语言中关于字符串和数字类型之间的转换都在 strconv 这个包下。这个包是 string convert 这两个单词的缩写可以用其中的 ParseInt() 或者 ParseFloat() 来解析一个字符串把字符串按照要求转换为相应的整数。 我们还可以用该包中的 Atoi() 把一个十进制字符串转成数字Atoi 是ASCII to Integer的缩写。反过来可以用 Itoa() 把数字转成字符串。如果输入的参数不合法那么这些函数都会返回error。 Atoi()和ParseInt()这两者的基本功能是相同的都是将字符串解析为整数但是 ParseInt 提供了更多的选项来处理不同的解析需求。例如ParseInt 允许指定解析的进制比如二进制、十六进制等也可以指定结果的位数比如 32 位整数或 64 位整数。 package mainimport (fmtstrconv )func main() {f, _ : strconv.ParseFloat(1.234, 64)fmt.Println(f) // 1.234n, _ : strconv.ParseInt(111, 10, 64)fmt.Println(n) // 111n, _ strconv.ParseInt(0x1000, 0, 64)fmt.Println(n) // 4096n2, _ : strconv.Atoi(123)fmt.Println(n2) // 123n2, err : strconv.Atoi(AAA)fmt.Println(n2, err) // 0 strconv.Atoi: parsing AAA: invalid syntax }十三、进程信息 在Go里我们能够用 os.Args 来得到程序执行时的指定的命令行参数。os.Args 是一个字符串切片它包含了传递给程序的所有命令行参数包括程序名称本身。比如我们编译的一个二进制文件在command命令后接a b c d 来启动那么 os.Args 会是一个长度为 5 的 slice。第一个成员代表程序名称本身 /var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main而命令行参数是a、b、c、d。os.Setenv用于读取环境变量的值。exec.Command(grep, 127.0.0.1, /etc/hosts).CombinedOutput()则用于在命令行执行 grep 命令查找文件 /etc/hosts 中包含 127.0.0.1 的行。exec.Command 创建了一个命令对象.CombinedOutput() 方法运行该命令并捕获其标准输出和标准错误输出。如果命令执行出错会抛出错误。 package mainimport (fmtosos/exec )func main() {// go run example/20-env/main.go a b c dfmt.Println(os.Args) // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]fmt.Println(os.Getenv(PATH)) // /usr/local/go/bin...fmt.Println(os.Setenv(AA, BB))buf, err : exec.Command(grep, 127.0.0.1, /etc/hosts).CombinedOutput()if err ! nil {panic(err)}fmt.Println(string(buf)) // 127.0.0.1 localhost }十二、Go语言特性 Go语言的特点有 高性能、高并发。语法简单、学习曲线平缓。标准库丰富工具链完善。很多情况下不需要借助第三方库就能完成大部分基础功能的开发。静态链接。快速编译拥有静态语言中几乎最快的编译速度。可跨平台。垃圾回收。 Go语言的“语法简单”在很多地方都有体现如以下HTTP服务器的Go语言代码案例 Go语言代码实现HTTP服务器 package mainimport (net/http )func main() {http.Handle(/, http.FileServer(http.Dir(.))) //路由处理http.ListenAndServe(:8080, nil) //服务器监听与启动 }该代码只有10行而核心的代码只有main函数中的两行。其中http.Handle(“/”, http.FileServer(http.Dir(“.”))) 用于路由处理使用 http.FileServer 函数创建一个路由处理器并将其映射到根路径 (“/”)。http.FileServer 函数接受一个参数即一个表示要提供的文件目录的 http.Dir 类型。 而 http.ListenAndServe(“:8080”, nil) 用于服务器的监听与启动这行代码启动了一个 HTTP 服务器监听本地主机的 8080 端口。http.ListenAndServe 函数接受两个参数第一个参数是监听地址在这里是 “:8080”第二个参数是一个处理器这里是 nil表示使用默认的路由处理器。 这样一来这段代码就创建了一个简单的本地服务器它会将访问根路径的请求映射到当前程序所在的目录并在本地的 8080 端口上启动一个服务器允许用户通过浏览器或其他 HTTP 客户端访问这些文件。 我们可以在Go语言的开发环境GoLand中运行该代码片段并随便导入一个html文件来进行测试 运行go程序启动服务器访问localhost:8080/login.html会发现确实可以访问到该静态页面 Go语言的基本语法入门就到这里。更为细致的知识点和用法可以从Go语言的官方文档寻求帮助。
http://www.ho-use.cn/article/10818286.html

相关文章:

  • 建站小软件南宁网站开发软件
  • 网站访问流程装修效果图免费软件
  • 济宁做网站的贵州网站设计公司
  • 网站如何防止攻击园林设计
  • 模块化网站建设系统网络宣传网站建设定制
  • 网站怎么做百度口碑网站建设结论与改进
  • 新兴街做网站公司项目管理的主要内容包括哪些
  • 百度文库网站立足岗位做奉献企业网站如何做优化
  • 长沙建站网站模板wordpress站长
  • 峨眉山移动网站建设静态网站什么意思
  • 男女做暧暧网站免费广州网站建设亅新科送推广
  • wordpress建站页面什么是网络营销评价
  • 网站建设规章制度重庆哪有作网站的
  • 珠海市横琴新区建设环保局网站网站建设的业务规划
  • 杭州做网站的公司哪些比较好我是建造网站
  • 经典重庆网站黑色企业网站
  • 网站访问统计 曲线图网络设计开发专业
  • 网站建设费用细项wordpress获取当前文章id
  • 程序网站开发学生个人作品集制作
  • 北京网站建设工作陕西高速公路建设集团公司网站
  • 修车店怎么做网站学做网站多长时间
  • 企业网站用哪个cms好wordpress文章视频
  • 上海网站备案中心logo在线设计标小智
  • 在深圳学网站设计欧派全屋定制多少钱一平米
  • 创新的微商城网站建设用wordpress做淘宝客
  • 设计型网站建设网站运营是什么意思
  • 网站建设洽谈方案阿里巴巴国际站外贸流程
  • 南京网站制作开发wordpress 产品管理
  • 青岛公司网站建设公司排名招标网官网登录
  • 加强企业网站建设小榄网站建设