变量的声明
func main() {
var a = "initial"
var b = true
var c, d int = 1, 2 //var会自动推导使用的类型,也可以手动添加类型
c, d := 1, 2 //简短声明
var e float64 //双精度
f := float32(e) //单精度
g := a + "foo"
fmt.Println(a, b, c, d, e, f, g) // initial 1 2 true 0 0 initialfoo
}
其中简短声明比var更方便。但是只能在函数内部生成,在函数外部还是使用var来声明(全局变量)
常量的声明
使用const来声明常量,在Go语言中常量没有固定的类型,它会根据使用的上下文来自动确定类型
if else 语句
if 1%1 == 0 {
...
} else if 11%2 == 0{
...
}
其中需要注意的是,if后面的左大括号必须和if关键字在同一行;如果有多条分支,if的右大括号必须和else if关键字在同一行,这是编译器强制规定的。
循环
go语言中只有for循环
i:=1
for {
fmt.Println( "1" )
break
}
for j := 7;j<9; j++ {
fmt.Println(j)
}
for n := 0; n <5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
for i <= 3 {
fmt.Println(i)
i=i+1
}
go语言的for循环可以不加条件语句直接使用,也可以像其他语言中的for循环一样,在for循环上加一些条件语句和用break或者continue进行跳出循环或者继续循环,或者进行循环嵌套。
分支结构语句switch
var l = "aaa"
switch l {
case "bbb", "aaa": //Here
fmt.Println(1)
case "ccc":
fmt.Println(2)
default:
fmt.Println(3)
}
在go语言中,switch中的每个case都是独立,所以不用加break也可以做到跳出case语句,而不是接着执行下一个case语句。 而且一个case可以满足多个条件(相当于多个case语句和在了一起)。
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println(1)
}
case后面不止可以放常量,也可以像if一样添加表达式,如果要这样写的话switch后面不用添加判断变量
数组
var a [5]int
a[4] = 100
fmt.Println(a[4],len(a))
b := [5]int{1,2,3,4,5}
fmt.Println(b)
平平无奇的两种声明方式,也可以通过双层for循环给数组赋值。
切片
数组的长度是固定的,非常滴不方便 而切片是对数组的一个连续片段的引用,所以切片是一个引用类型,任意时刻可更改长度,更灵活。
- 切片的创建
- 可以从数组或切片生成新的切片
- 直接声明新的切片(创建数组时不加大小)
- 或者可以动态的创建切片,用make函数创建切片,赋值与数组一样
s := make([]string, 3)
s[0] = "a"
fmt.Println(s[0], len(s)) //a 3
华丽的分界线-------------------------------------------------
s = append(s, "d", "f")
fmt.Println(s, len(s)) //[a d f] 5
可以用append函数向切片中动态增加长度,注意是在原有的长度上增加,并不会覆盖原来没有值的位置
c := make([]string, len(s))
copy(c, s)
fmt.Println(c) // [a d f]
可以用copy函数将一个切片复里到另一个切片 假如一个长度为3的切片想复制长度为5的切片,只能复制前三个 后两个就会丢失
fmt.Println(s[2:5]) // [ d f] 查看2-5个元素
fmt.Println(s[:5]) // [a d f] 查看第五个之前的2元素
fmt.Println(s[2:]) // [ d f] 查看第二个之后的元素
切片操作
map
- map的创建:
- 初始化创建
m := map[string]int{"one" : 1,"two" : 2} - 先定义后使用make创建
- 直接使用make创建
m := make(map[string]int)//也是采用key-value的方法
m["one"] = 1
m["two"] = 2
fmt.Println(m,len(m), m["one"], m["unknow"])
delete(m,"one")//map 的删除操作
由于碰到key为unknow时不知道这个key是否存在可以用r, b := m["unknow"],若b返回的是false则不存在这个key。
注意:map的遍历是无序的,和插入,字母顺序无关
range
对于一个切片(数组)和map,可以用range快速遍历,使代码更整洁
对于数组
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num", num)
}
fmt.Println("i:", i, "num", num, "sum", sum)
}
和增强for差不多作用 注意:索引和值都可以单独存在
对于map
m := map[string]string{"a": "A", "b": "B"}
for k, v := range m {
fmt.Println(k, v)
}
for k := range m { //只想拿key的情况下遍历
fmt.Println("key", k)
}
函数
func 函数名(形参)(返回值){
函数体
}
func add(a int, b int) int{
return a+b
}
调用方式sum := add(1,2)
和方法差不多,但是他的变量类型是后置的
而且可以返回多个值(在实际的业务逻辑一般返回两个值,一个真正的返回结果,一个错误(错误处理)的返回结果)
指针
主要用途:对传入参数进行修改
func add(n int) {
n += 4
fmt.Println(n) // 5
}
func addptr(n *int) {
*n += 2
}
func main() {
n := 1
add(n)
fmt.Println(n) // 1
addptr(&n) //这里要记得加&哦
fmt.Println(n) //3
}
因为add函数中传入的参数是拷贝类型,修改的是拷贝。 这个时候需要加上指针,来确定改的是传入的参数
结构体
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 (u user) checkPassword(un string, password string) bool {
return u.password == password && u.name == un
}
func checkPassword2(u user, 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("wang", "2048"))
fmt.Println(checkPassword2(a, "2048"))
}
体会下两种不同检查密码的方式,一个是把一个普通的函数变成了结构体方法(有点像类里面的方法),可以像a.checkPassword这样调用,还有一个是checkPassword2(a, "2048")函数里面的只是参数是结构体。
而在实现结构体的方法的时候根据是否带指针有两种写法,带指针则可以修改结构体,反之则不可以修改。
错误处理
go语言的异常:用一个单独返回值传递错误信息(并不像java那样try catch),这样就能很清晰的知道哪个函数返回了错误,可以用if else处理错误. 补充:bool(初始值)为 false,数值类型初始值为0,字符串类型初始值为空字符串“''”,而指针、切片、映射、通道、函数和接口的初始值则是nil。
func findUser(users []user, name string) (v *user, err error) {
//返回值原来可以这样写 !!(这里用v让下面的看起来u更清晰)(用u也不影响)
for _, u := range users {
//前面的_相当于占位符,因为返回值有两个,这里只想获得name,所以pass..就用下划线代替了
if u.name == name {
return &u, nil
//直接放一个结构体是没有nil的,加个*变指针才能有nil
}}
return nil, errors.New("not found")
}
func main() {
u, err := findUser([]user{{"wang", "1024"}}, "wang")
//这里是结构体初始化和数组初始化放一起了
if err != nil{
fmt.Println(err)
return
} //如果没找到直接return了,不往下走了,防止出现空指针异常.
fmt.Println(u.name)
//底下的这里是将上面两个操作合了起来,使代码更加整洁(这个分号很灵性)
if u,err := findUser([]user{{"wang","1024"}},"li"); err != nil{
fmt.Println(err)
return
}else{
fmt.Println(u.name)
}
}
字符串操作
a := "hello"
| 字符串/结果 | 方法 | 解释 | 返回值 |
|---|---|---|---|
| strings.Compare(a, "hello") | 字符串比较 0,a等于b -1,a小于b 1,a大于b | int | |
| true | strings.Contains(a, "ll") | 检测字符串ll是否存在a中 | bool |
| 2 | strings.Count(a, "l") | 统计字符串a中有多个个l(若统计空字符串,返回a的长度+1) | int |
| true | strings.HasPrefix(a, "he") | 检测字符串a是否以字符串 he 作为前缀 | bool |
| true | strings.HasSuffix(a, "llo") | 检测字符串a是否以字符串 llo 作为后缀。若没有出现,返回-1 | int |
| 2 | strings.Index(a, "ll") | 返回字符串 substr 在字符串 s 中第一次出现的索引位置,若没有出现,返回-1 | int |
| he-llo | strings.Join([]string{"he", "llo"}, "-") | 使用分隔符 - 连接几个字符串 | string |
| hellohello | string.Repeat(a,2) | 重复字符串a 2次 | string |
| hEllo | string.Replace(a,"e","E",-1) | 在字符串 a 使用新字符串E代替旧字符串e,数字是替换次数,-1代表无限制替换。返回替换结果 | string |
| [a b c] | string.Split("a-b-c","-") | 按照分隔-将字符串拆开 | []string |
| hello/HELLO | string. ToLower(a)/ToUpper(a) | 全变成小写/大写 | string |
| 5 | len(a) | 字符串长度(但如果是中文的话,有可能会一个中文对应几个字符) | int |
一些常用的操作
字符串格式化
格式化规则有很多比如下面这里 blog.csdn.net/weixin_3805…
JSON处理
type userInfo struct {
Name string
Age int `json:"age"`//这里这里!
Hobby []string
}
结构体中json标签的作用可以看这里blog.csdn.net/hyrylt/arti…
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "Java"}}
buf, err := json.Marshal(a) //序列化
if err != nil {
panic(err) //宕机(panic)——程序终止运行
}
fmt.Println(buf) //[123 34..]
fmt.Println(string(buf)) //{"Name":"wang","age":18,"Hobby":["Golang","Java"]}
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)//%v打印任意类型变量,%+v详细结果 %#v更详细
//main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "Java"}}
- 注意事项:
- 首先就是结构体中的字段需要大写,这样才能用json.Marshal()方法去序列化
- 因为序列化后16进制数组,所以打印的话需要用string()转化一下
时间处理
now := time.Now()
fmt.Println(now)
t := time.Date(2021, 4, 5, 1, 11, 11, 0, time.UTC)
t2 := time.Date(2026, 2, 3, 1, 11, 11, 0, time.UTC)
fmt.Println(t, t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
fmt.Println(t.Format("2006-01-02 15:04:05"))
diff := t2.Sub(t)
fmt.Println(diff, diff.Hours(), diff.Minutes())
fmt.Println(now.Unix())
if _, err := time.Parse("2012-05-11 12:44:02", "2023-04-5 1:11:11"); err != nil {
panic(err)
}
- time.Now()查看当前时间
- time.Date()自己创建一个时间,可以查看其中具体分秒时
- t.Format("2006-01-02 15:04:05")给出一个时间格式,下面的时间都会按照这个格式输出
- t2.Sub(t) 两个时间可以做和运算
- time.Parse()给出一个示例时间格式,将后面的时间转成示例的时间格式
- now.Unix()可以用这个来实现时间戳
数字解析
- strconv.ParseFloat("1.234", 64)转换为字符串,64是bitsize
- strconv.ParseInt("1.234", 10, 64)转换为10进制整型,如果第二个地方写0,代表着自动推测进制
- strconv.Atoi("123")将一个十进制字符串转换为数字
- strconv.Atoi("AAA")不合法的话会返回错误
- strconv.Itoa(123)将数字转换为字符串
进程信息
fmt.Println(os.Args)
fmt.Println(os.Getenv("PATH"))
fmt.Println(os.Setenv("AA", "BB"))
buf, err := exec.Command("grep", "127.0.0.1", "/etc/host").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf))
- os.Args得到程序执行时指定的命令行参数
- os.Getenv("PATH")读取环境变量
- os.Setenv("AA", "BB")获取环境变量
- exec.Command()快速启动子进程,并且获得输入输出
- cmd.CombinedOutput会返回错误和外部命令的输出,