前言
记录一些Go的语法糖,当然也可能会跳过一些简单的语法~
一、结构分析
package main
import (
"fmt"
)
func Hello(str string) {
fmt.Println("Hello " + str)
}
func main() {
Hello("World!")
}
这是一个简单的Go
代码,我们可以看到
- 第一行
package main
代表这个文件属于main包的一部分,main
包也就是程序的入口包。第一行包主代表这个文件属于主包的一部分,主包也就是程序的入口包。 - 第三行
import
的操作表示的是引入一些标准库的包,可以简单的和C
语言的#include
预处理对应起来 - 第七行这里就是
Go
的一个函数的结构了,我们这里接受一个string
类型的str
变量,然后将其加上Hello
前缀打印输出 - 第十一行这里就是主函数了,类比
C
语言的main
,即程序的启动入口
二、变量&常量
变量的基本类型:
①:bool类型
记录真或者假
②:数值类型
int8
、int16
、int32
、int64
、int
uint8
、uint16
、uint32
、uint64
、uint
float32
,float64
complex64
,complex128
byte
rune
记录数值
我们发现有一些变量后面跟了个数字,这其实就是表示这个变量的占用的位的大小,例如
int8
就表示占用8
个bit,能表示的数字个数就是 2 8 2^8 28 考虑到 0 0 0 和负数的情况,那么这个int8
表示的范围就是: − 2 7 -2^7 −27 ~ 2 7 − 1 2^7-1 27−1 其他的一次类推了,其中前缀加了u
的表示无符号型,即没有负数,直接从0
到 2 x − 1 2^x-1 2x−1 即可
③:string类型
用于记录字符串类型的值
变量的定义
①:第一种通过var
关键字来定义
var a = "hello"
var b = 3
var c float64 = 3.14
注意这里如果不给变量初始化的话,则需要带上这个变量的类型:
var a string
var b int
var c float64
②:直接定义
a := "hello"
b := 3
c := 3.14
当然变量定义的时候可以使用强转
常量的定义
常量的定义其实就是将变量的var
变成const
关键字即可
const str = "Hello World!"
三、分支&循环
if-else
package main
import (
"fmt"
)
func main() {
var a int
fmt.Scanf("%d", &a)
if a < 10 {
fmt.Println("step1")
} else if a < 20 {
fmt.Println("step2")
} else {
fmt.Println("step3")
}
}
这里我们发现和C/C++
的区别在于判断条件没有括号包裹了,如果非要加括号,Go
也是能运行的
switch case
package main
import (
"fmt"
)
func main() {
var a int
fmt.Scanf("%d", &a)
switch a {
case 1:
fmt.Println("This is 1")
case 2:
fmt.Println("This is 2")
case 3:
fmt.Println("This is 3")
default:
fmt.Println("This is error")
}
}
- 在
c++
里面,switch case
如果不在目标case
下加break
的话会然后会继续往下跑完所有的case
,在go
语言 里面的话是不需要加break
的。- 相比
C
或者C++
,go
语言里面的switch
功能更强大。可以使用任意的变量类型,甚至可以用来取代任意的if else
语句。你可以在switch
后面不加任何的变量,然后在case
里面写条件分支。这样代码相比你用多个if else
代码逻辑会更为清晰
for循环
Go
只有一个for
循环,不过也已经够了
package main
import (
"fmt"
)
func main() {
var a int
fmt.Scanf("%d", &a)
for i := 1; i < a; i++ {
fmt.Println("index = ", i)
}
}
while类型的写法:
package main
import (
"fmt"
)
func main() {
var a int
fmt.Scanf("%d", &a)
var i int = 1
for i <= a {
fmt.Println("index = ", i)
i++
}
}
for-range
for key, value := range oldMap {
newMap[key] = value
}
注意这里的key
有的时候不会用上,所以可以用_
下划线代替,这样就不会报没使用这个变量的错了
四、数组
数组的定义只需要将申请的空间放在变量名和类型中间即可
eg:
//声明数组
var a[10]int
//声明并初始化操作
var d = [3]int{
1,2,3}
二维数组类似:
var a[5][5]int
var d = [5][5]int{
{
1,2,3,4,5}}
数组长度我们可以通过len
函数获取,例如
//声明数组
var a[10]int
//声明并初始化操作
var d = [3]int{
1,2,3}
//输出长度
fmt.Println(len(d))
元素访问就是正常下标访问即可,例如:a[i][j]
五、函数
标准格式:
func function_name( [parameter list] ) [return_types] {
函数体
}
结构记录:
// 传入两个 int 类型的值,然后返回其和,且也是int类型
func add(a int, b int) int {
return a + b
}
// 传入一个stirng参数,然后没有返回值
func printt(str string) {
fmt.Println("Hello", str)
}
Go
函数可以返回多个值,一般来说,第一个是函数的返回值,第二个是错误信息。
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
返回错误的情况:
func (c *Client) Do(req *Request) (*Response, error) {
return c.do(req)
}
我们调用这个函数的时候就需要给出两个变量用于接受这个返回值:
resp, err := client.Do(req)
六、切片
切片(slice
)与数组的区别是切片的长度不是固定的而是可变的,比数组的用途更加广泛。
切片声明需要通过 make
来进行,需要指定切片的类型以及长度,例如下面为创建一个长度为 3 3 3 的string
类型的切片。
// 声明一个空切片
s1 := make([]string, 3)
// 声明并初始化
s2 :=[] int {
1,2,3,4,5,6}
切片除了长度还有一个容量(capacity
)的概念,可以在声明切片的时候同时指定长度和容量。
s := make([]string, 3, 10)
通过s1 := s[startIndex:endIndex]
可以创造新切片s1
,且s
可以为数组,其余操作貌似和python
的切片类似,不过go
的切片更像一个变长数组或者说链表,go
支持append
函数用于向切片中追加值,支持copy
函数用于复制切片
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
七、 指针
貌似和C/C++
中的指针的操作是一样的,通过*
访问,然后&
是取地址符
不过Go
中的指针不支持指针运算,例如下面的代码就会报错:
main.go:6: invalid operation: p++ (non-numeric type *[3]int)
package main
func main() {
b := [...]int{
109, 110, 111}
p := &b
p++
}
new创建指针
这里mark一下格式:
ptr := new(int)
,其中的int
可以替换成其他数据类型
其他
-
关于指针的解引用是和
C/C++
类似的 -
想函数中传入指针参数的时候,我们在函数的定义时,需要使用
*
来表示接受指针的
感觉Go
有个弱智的语法,先看代码:
package main
import (
"fmt"
)
func find_max(a []int) int {
l := len(a)
ans := a[0]
for i := 0; i < l; i++ {
if a[i] > ans {
ans = a[i]
}
}
return ans
}
func main() {
var d = [3]int{
1, 2, 3}
fmt.Println(find_max(d))
}
这也是C/C++
正常的数组传参,不过这里会报错:
cannot use d (variable of type [3]int) as []int value in argument to find_max
意思大概就是a []int
是一个切片,而传入的d
是一个数组,所以会报错,我们需要将第 20 20 20 行的数据改为切片,即:
fmt.Println(find_max(d[:]))
八、结构体
结构体声明语法:
type StructName struct{
FieldName type
}
不同成员之间直接换行写就行,不用,
隔开,如下:
type StructName struct{
a int
b float32
c string
d bool
}
定义结构体变量:
var t1 StructName
t2 := StructName{
a: 1,
b: 1.1,
c: "hello",
d: true,
}
t3 := StructName{
1, 1.1, "hello", true}
成员函数
成员函数写在结构体外面,格式如下:
func (t StructName) func_name(str string) int {
return 1
}
这里我们将当前的这个结构体传入,当然也可以使用结构体指针,这样就能对当前的这个结构体变量的进行一些修改操作
json转结构体
JSON转Golang Struct
我们处理JSON数据的时候,需要将json构造成一个或者多个结构体的嵌套,那么假设我们有如下的JSON数据:
{
"rc": 0,
"wiki": {
"known_in_laguages": 63,
"description": {
"source": "tangible and intangible thing, except labor tied services, that satisfies human wants and provides utility",
"target": null
},
"id": "Q28877",
"item": {
"source": "good",
"target": "商品"
},
"image_url": "http://www.caiyunapp.com/imgs/link_default_img.png",
"is_subject": "true",
"sitelink": "https://www.caiyunapp.com/read_mode/?id=6354777915466339550246c5"
},
"dictionary": {
"prons": {
"en-us": "[gʊd]",
"en": "[gud]"
},
"explanations": [
"a.好的;善良的;快乐的;真正的;宽大的;有益的;老练的;幸福的;忠实的;优秀的;完整的;彻底的;丰富的",
"n.利益;好处;善良;好人",
"ad.=well"
],
"synonym": [
"excellent",
"fine",
"nice",
"splendid",
"proper"
],
"antonym": [
"bad",
"wrong",
"evil",
"harmful",
"poor"
],
"wqx_example": [
[
"to the good",
"有利,有好处"
],
[
"good, bad and indifferent",
"好的,坏的和一般的"
],
[
"good innings",
"长寿"
],
[
"good and ...",
"很,颇;完全,彻底"
],
[
"do somebody's heart good",
"对某人的心脏有益,使某人感到愉快"
],
[
"do somebody good",
"对某人有益"
],
[
"be good for",
"对…有效,适合,胜任"
],
[
"be good at",
"在…方面(学得,做得)好;善于"
],
[
"as good as one's word",
"信守诺言,值得信赖"
],
[
"as good as",
"实际上,几乎等于"
],
[
"all well and good",
"也好,还好,很不错"
],
[
"a good",
"相当,足足"
],
[
"He is good at figures . ",
"他善于计算。"
]
],
"entry": "good",
"type": "word",
"related": [],
"source": "wenquxing"
}
}
让我们自己写结构体,也许能写但是回很麻烦,这个时候就需要用到上面的工具了JSON
转Go的结构体
转换出来:
type AutoGenerated struct {
Rc int `json:"rc"`
Wiki struct {
KnownInLaguages int `json:"known_in_laguages"`
Description struct {
Source string `json:"source"`
Target interface{
} `json:"target"`
} `json:"description"`
ID string `json:"id"`
Item struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"item"`
ImageURL string `json:"image_url"`
IsSubject string `json:"is_subject"`
Sitelink string `json:"sitelink"`
} `json:"wiki"`
Dictionary struct {
Prons struct {
EnUs string `json:"en-us"`
En string `json:"en"`
} `json:"prons"`
Explanations []string `json:"explanations"`
Synonym []string `json:"synonym"`
Antonym []string `json:"antonym"`
WqxExample [][]string `json:"wqx_example"`
Entry string `json:"entry"`
Type string `json:"type"`
Related []interface{
} `json:"related"`
Source string `json:"source"`
} `json:"dictionary"`
}
九、错误处理
Go
语言通过内置的错误接口提供了非常简单的错误处理机制。
error
类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
如果是函数内部抛出异常,那么对于当前函数就应该多一个errors
的接收变量,例如下面的代码示例:
type user struct {
name string
password 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")
}
当我们需要调用该函数的时候,需要如此调用:
us,err = findUser([]user{
{
"xie","1234abcd"}},"xie")
十、标准库
Go内置了非常丰富的标准库工具,常用的标准库包括字符串操作、字符串格式化、json处理、时间处理等。
字符串操作
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
字符串格式化
Println
最为常用作用为打印并换行,Printf
可以按指定格式打印字符串。
+v
可以打印字段和值详细信息。
#v
可以打印出整个结构体的构造以及详细信息。
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
json 处理
Go
在处理 json
时十分简单,只需要将结构体中的字段第一个字母变成大写就能用内置的JSON
工具进行处理。
Go
中一些特殊的类型,比如Channel
、complex
、function
是不能被解析成JSON
的。
Go
中JSON
对象只支持string
作为key
,对于map
,那么必须是map[string]T
这种类型,T
可以是 Go
语言中任意的类型。
Go
的json
处理是通过Marshal
、Unmarshal
方法来进行处理的。
Marshal
用于自定义编码json
的方法,也就是将变量、对象转成json
,转换之后需要用string
方法强转一下,否则会打印出的是 16 16 16 进制字符串。
Unmarshal
用于自定义解码json
方法,也就是将json
转为对象。
type userInfo struct {
Name string
Age int `json:"age"` // 自定义json输出的字段
Hobby []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 userInfo
err = 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
提供了很多常用的时间处理函数,例如 Now
、解析字符串、转字符串、获取时间戳等。在操作时间相关的方法时,需要导入time
包。
// 获取当前时间
now := time.Now()
fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
t := 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 UTC
// 获取时间的年月日
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
// 时间转字符串
fmt.Println(t.Format("2006-01-02 15:04:05")) // 2022-03-27 01:25:36
// 获取时间差
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
// 解析字符串
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) // true
// 获取时间戳
fmt.Println(now.Unix()) // 1648738080
十一、数据结构
Go
语言内置了一个数据结构,即map
Map
是一种无序的键(key
)值(value
)对的集合,也被称为映射、字典。
Map
是无序的,支持类似数组和切片的操作。
Map
的声明如下:
m := make(map[string]int)
其中,string
为键,int
为值,对元素的访问也是按照下标进行访问,对于不存在的键值对,返回0
,这里也有一个问题,假设一个键值对的key
也是0
,有的时候就可能存在误解,于是Go
在键值对的查询结果时,返回了两个值,一个是结果,一个是,是否存在,如下:
r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false
删除键值对,可以使用delete
函数
delete(m, "one")
另外有一个大佬用Go
实现了STL
GoSTL
是一个go
语言数据结构和算法库,类似C++
的STL
,但功能更强大。结合go
语言的特点,大部分数据结构都实现了协程安全,可以在创建对象的时候通过配置参数指定是否开启。
gostl:https://github.com/liyue201/gostl
参考博客
转载:https://blog.csdn.net/m0_46201544/article/details/128727128