这是我参与【第五届青训营】伴学笔记创作活动的第1天
Go语言语法基础
定义变量
Go语言是静态类型语言,因此变量(variable)是有明确类型的,编译器也会检查变量类型的正确性。
变量声明:
-
var name type
-
名字 := 表达式
注意:当定义一个变量后又使用该符号初始化变量,就会产生编译错误
//val := 1相等于 var val int val =1
如果没有初始化,则变量默认为零值。
如果没有指定变量类型,可以通过变量的初始值来判断变量类型。
var d = true
多变量声明:
可以同时声明多个类型相同的变量(非全局变量)
var x, y int
var c, d int = 1, 2
g, h := 123, "hello"
关于全局变量的声明如下:var ( vname1 v_type1 vname2 v_type2 )
var (
a int
b bool
)
匿名变量:
匿名变量的是一个下画线_,被称为空白标识符。任何类型都可以赋值给它,但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。
使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。
func GetData() (int, int) {
return 10, 20
}
func main(){
a, _ := GetData()
_, b := GetData()
fmt.Println(a, b)
}
指针
变量其实是一种使用方便的占位符,用于引用计算机内存地址。Go 语言中的的取地址符是&,放到一个变量前使用就会返回相应变量的内存地址。
指针变量其实就是用于存放某一个对象的内存地址。
指针声明和初始化
声明格式如下:var var_name var-type,其中的var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。
代码举例如下:
var n1 *int /* 指向整型*/
var n2 *float32 /* 指向浮点型 */
指针的初始化就是取出相对应的变量地址对指针进行赋值,具体如下:
var a int= 20 /* 声明实际变量 */
var p *int /* 声明指针变量 */
p = &a /* 指针变量的存储地址 */
空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil,也称为空指针。它概念上和其它语言的null、NULL一样,都指代零值或空值。
package main
import "fmt"
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
}
数组
声明数组
Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:
var variable_name [SIZE] variable_type
var nums [10] float32
初始化数组
-
直接进行初始化:var nums= [5]float32{2.0, 3.4, 7.0, 50.0}
-
通过字面量在声明数组的同时快速初始化数组:nums:= [5]float32{2.0, 3.4, 7.0, 50.0}
-
数组长度不确定,编译器通过元素个数自行推断数组长度,在[ ]中填入...,
举例如下:var nums= [...]float32{ 2.0, 3.4, 7.0, 50.0}和nums:= [...]float32{2.0, 3.4, 7.0, 50.0}
在c语言中我们知道数组名在本质上是数组中第一个元素的地址,而在go语言中,数组名仅仅表示整个数组,是一个完整的值,一个数组变量即是表示整个数组。在go中一个数组变量被赋值或者被传递的时候实际上就会复制整个数组。如果数组比较大的话,这种复制往往会占有很大的开销。所以为了避免这种开销,往往需要传递一个指向数组的指针,这个数组指针并不是数组。
数组指针
通过数组和指针的知识我们就可以定义一个数组指针
var a = [...]int{1, 2, 3} // a 是一个数组
var b = &a // b 是指向数组的指针
数组指针除了可以防止数组作为参数传递的时候浪费空间,还可以利用其和for range来遍历数组
for i, v := range b { // 通过数组指针迭代数组的元素
fmt.Println(i, v)
}
结构体
声明结构体:
type struct_variable_type struct {
member definition
...
member definition
}
使用该结构体:
variable_name := structure_variable_type {value1, value2...valuen}
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
结构体指针:
定义一个结构体变量的语法:var struct_pointer *user。
使用结构体指针访问结构体成员仍然使用.操作符。格式如下:struct_pointer.name
实例代码:
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
//声明方式1
a := user{name: "wang", password: "1024"}
//2
b := user{"wang", "1024"}
//3
c := user{name: "wang"}
c.password = "1024"
//4
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false
a.resetPassword("2048")
fmt.Println(a) //{wang 2048}
fmt.Println(a.checkPassword("2048")) // true
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
字符串
一个字符串是一个不可改变的字节序列,字符串通常是用来包含人类可读的文本数据。和数组不同的是,字符串的元素不可修改,是一个只读的字节数组。
相关函数:
package main
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
}
slice切片
类似python中的切片
定义
可以声明一个未指定大小的数组来定义切片。切片不需要说明长度。
var identifier []type
或使用 make() 函数来创建切片。
var slice []type = make([]type, len)
或
slice := make([]type, len)
添加元素
append() :内置的泛型函数,可以向切片中增加元素。
1.在切片尾部追加N个元素
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包
2.在切片开头位置添加元素
var a = []int{1,2,3}
a = append([]int{0}, a...) // 在开头位置添加1个元素
a = append([]int{-3,-2,-1}, a...) // 在开头添加1个切片
3.append和copy组合
a = append(a, 0) // 切片扩展1个空间
copy(a[i+1:], a[i:]) // a[i:]向后移动1个位置
a[i] = x // 设置新添加的元素
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
c := make([]string, len(s))
copy(c, s) //复制一份s到c中
删除元素
1.从开头位置删除;
直接移动数据指针
a = []int{1, 2, 3, ...}
a = a[1:] // 删除开头1个元素(相当于截取第1个元素到最后一个元素的部分)
a = a[N:] // 删除开头N个元素
2.从中间位置删除; 对于删除中间的元素,需要对剩余的元素进行一次整体挪动,同样可以用append或copy原地完成: append删除操作如下:
a = []int{1, 2, 3, ...}
a = append(a[:i], a[i+1], ...)
a = append(a[:i], a[i+N:], ...)
copy删除操作如下:
a = []int{1, 2, 3}
a = a[:copy(a[:i], a[i+1:])] // 删除中间1个元素
a = a[:copy(a[:i], a[i+N:])] // 删除中间N个元素
3.从尾部删除;
a = []int{1, 2, 3, ...}
a = a[:len(a)-1] // 删除尾部1个元素 (相当于截取第1个元素到第len(a)-1一个元素的部分)
a = a[:len(a)-N] // 删除尾部N个元素
map
package main
import "fmt"
func main() {
//定义1
m := make(map[string]int)
//定义2
m2 := map[string]int{"one": 1, "two": 2}
//定义3
var m3 = map[string]int{"one": 1, "two": 2}
//插入数据
m["one"] = 1
m["two"] = 2
//获取数据 ok用来判断该map中是否存在该key
r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false
//删除数据
delete(m, "one")
fmt.Println(m) // map[one:1 two:2]
fmt.Println(len(m)) // 2
fmt.Println(m["one"]) // 1
fmt.Println(m["unknow"]) // 0
fmt.Println(m2, m3) //map[one:1 two:2] map[one:1 two:2]
}
函数
具名函数:就和c语言中的普通函数意义相同,具有函数名、返回值以及函数参数的函数。
func fuction_name([parameter list])[return types]{
函数体
}
func Add(a, b int) int {
return a+b
}
匿名函数:指不需要定义函数名的一种函数实现方式,它由一个不带函数名的函数声明和函数体组成。
var Add = func(a, b int) int {
return a+b
}
package main
import "fmt"
func 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) // 3
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok) // A True
}
json
json.Marshal(userInfo):将数据结构转换为json字符串
json.MarshalIndent(userInfo, "", "\t"):将数据结构转换为json字符串(打印出来比较好看)
json.Unmarshal(str, &stu):将json字符串转换为数据结构
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string
Age int `json:"age"` //即把Age改为age
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))
//{
// "Name": "wang",
// "age": 18,
// "Hobby": [
// "Golang",
// "TypeScript"
// ]
//}
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"}}
}
strconv
数字解析包
1.string转int
可以使用func Atoi(s string) (i int, err error),Atoi代表Ascii to Integer。
可以使用func ParseInt(s string, base int, bitSize int) (i int64, err error)
- base为进制,值为2~36,如果为0,则会根据字符串自动判断,前置为"0x"的是16进制,前置为"0"的是8进制,其余的为10进制,
- bitSize是返回的整数类型,0、8、16、32分别代表int、int8、int16、int32。
2.int转string
使用func Itoa(i int) string
3.string转bool
func ParseBool("TRUE")
4.string转float
func ParseFloat(s string, bitSize int) (f float64, err error)
5.float转string
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
- fmt表示格式,’f’(-ddd.dddd)、’b’(-ddddp±ddd,指数为二进制)、’e’(-d.dddde±dd,十进制指数)、’E’(-d.ddddE±dd,十进制指数)、’g’(指数很大时用’e’格式,否则’f’格式)、’G’(指数很大时用’E’格式,否则’f’格式);
- prec表示控制的精度(排除指数部分):对’f’、’e’、’E’,它表示小数点后的数字个数;对’g’、’G’,它控制总的数字个数。如果prec 为-1,则代表使用最少数量的、但又必需的数字来表示f。
package main
import (
"fmt"
"strconv"
)
func main() {
f, _ := strconv.ParseFloat("1.234", 64) //string转float
fmt.Println(f) // 1.234
n, _ := strconv.ParseInt("111", 10, 64) //string转int
fmt.Println(n) // 111
n, _ = strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n) // 4096
n2, _ := strconv.Atoi("123") //string转int
fmt.Println(n2) // 123
n2, err := strconv.Atoi("AAA") //string转int
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
s:=strconv.Itoa(1234) //int转string
fmt.Println(s) //1234
str:=strconv.FormatFloat(45.3210,'f',-1,64) //float转string
fmt.Println(str) //45.321
str2:=strconv.FormatFloat(45.3210,'f',5,64) //float转string
fmt.Println(str2) //45.32100
fmt.Println(strconv.ParseBool("t")) //true <nil>
fmt.Println(strconv.ParseBool("true")) //true <nil>
fmt.Println(strconv.ParseBool("True")) //true <nil>
fmt.Println(strconv.ParseBool("0")) //false <nil>
fmt.Println(strconv.ParseBool("f")) //false <nil>
}
总结:
总体来说,Go语言对于有其他语言基础的同学来说上手不算困难,Go语言很像c语言+python的结合体,对基础的语法进行了解学习后,即可上手进行简单的开发。