Go语言基础语法(1)| 青训营笔记

94 阅读9分钟

变量的声明

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大于bint
truestrings.Contains(a, "ll")检测字符串ll是否存在a中bool
2strings.Count(a, "l")统计字符串a中有多个个l(若统计空字符串,返回a的长度+1)int
truestrings.HasPrefix(a, "he")检测字符串a是否以字符串 he 作为前缀bool
truestrings.HasSuffix(a, "llo")检测字符串a是否以字符串 llo 作为后缀。若没有出现,返回-1int
2strings.Index(a, "ll")返回字符串 substr 在字符串 s 中第一次出现的索引位置,若没有出现,返回-1int
he-llostrings.Join([]string{"he", "llo"}, "-")使用分隔符 - 连接几个字符串string
hellohellostring.Repeat(a,2)重复字符串a 2次string
hEllostring.Replace(a,"e","E",-1)在字符串 a 使用新字符串E代替旧字符串e,数字是替换次数,-1代表无限制替换。返回替换结果string
[a b c]string.Split("a-b-c","-")按照分隔-将字符串拆开[]string
hello/HELLOstring. ToLower(a)/ToUpper(a)全变成小写/大写string
5len(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会返回错误和外部命令的输出,