Golang学习笔记

0x01程序结构

标识符

Go语言标识符是用于标识变量、函数或任何用户自定义项目的名称。

标识符有两种:

  • 用户定义的标识符,作为程序实体。一般来说同一个代码块中,不运行出现同名的程序实体,调用其他包中的实体需要指定包标识符,例如: fmt.Println。
  • 预定义标识符,在Go语言源代码中声明,例如数据类型: bool、byte、int; 内建函数名: append、cap、close等。

关键字

关键字也叫保留字,这些保留字不能作为常量、变量或任何其他标识符名称。

Go的关键字相比其他语言来说还算非常少的了,一共只有25个。

  • 包导入与声明:import、package。
  • 程序实体声明与定义:var、type、func、interface、map、 struct、chan、const。
  • 流程控制:if、continue、for、return、go、case、goto、 switch、select、else、break、default、defer、fallthrough、 range。

字面量

通俗的说就是用来表示值的,例如const b int = 10,这里的b是常量,10为字面量。

分隔符

用于分隔其他元素,例如() {} [] . , ; : ......,程序代码都是通过语句来实现结构化的,但是Go语言不需要以;结尾,Go语言编译器会自动完成。

运算符

运算符 说明
||
&&
!
== 相等判断
!= 不等判断
< 小于判断
> 大于判断
<= 小于等于判断
>= 大于等于判断
+ 求和
- 求差
* 乘积
/ 求商
| 按位或
^ 按位异或
% 求余
<< 按位左移
>> 按位右移
& 按位与
&^ 按位清楚

优先级

运算符 优先级
* / % << >> & &^ 最高
+ - | ^ 较高
== != < <= > >=
&& 较低
|| 最低

如果出现相同优先级的运算符,则从左到右运算。所有运算符均收到括号的影响。

注释

Go语言可以使用//单行注释,也可也使用/* */来多行注释,这与其他大多编程语言并无差异。

常量

常量使用关键字const定义,常量的定义格式为: const 标识符 [类型] = value,例如const Pi float32 = 3.1415926536。这里的类型是可以忽略的,因为Go语言编译器可以判断值的类型。

变量

变量声明的格式是: var 标识符 类型。

例如:

var a int
var b bool
var a,b int
//也可以通过如下方式进行批量定义
var(
	a,b int
	c bool
	str string
)

很有趣的一个点就是,如果想要交换两个变量的值,可以直接使用a,b=b,a,不再需要使用Temp变量去充当中间商hhhh。

变量的声明不仅可以通过var来定义,还可以直接在使用的时候通过:=定义并赋值,例如: a := 1

注意:

  • :=方式不能用于全局变量的声明和赋值
  • 同一个代码块内不能多次声明同一个变量
  • 函数体内属于独立代码块,可以再次声明变量a,可 以改变变量的类型,并且定义只在函数内部生效
  • 局部变量声明了就必须使用,全局变量可以声明了不被使用

在使用某个函数的时候,往往会出现多个返回值,但我们只需要其中的一个返回值,则可以使用_占位。

package main

import "fmt"

func main() {
	fmt.Println("Who are U?")
	Name, Age, _ := GetInfo()//用_匿名变量来接收Hobby的值
	fmt.Println("I am " + Name + ",and I am " + Age + "years old")
}

func GetInfo() (Name string, Age string, Hobby string) {
	return "Le1aaaaa", "20", "Security"
}

init函数

init函数执行优先级比main函数高,一个源文件只能有一个init函数。

package main

import (
   "fmt"
   "math"
)

var Pi float64

func init() {
   Pi = 4 * math.Atan(1)
}

func main() {
   DPi := Pi * Pi
   fmt.Println(Pi, DPi)
}
//3.141592653589793 9.869604401089358

0x02基础数据类型

整型

整型的表示

package main

import "fmt"

var value1 int32 //全局声明
func main() {
   value2 := 64 //函数内部声明
   fmt.Println("value2:" + fmt.Sprint(value2))

}

整型的运算

package main

import "fmt"

func main() {
   x, y := 2, 4
   if x == y {
      fmt.Println("x等于y")
   } else {
      fmt.Println("x不等于y")
   }
}
运算 例子 结果
x<<y,左移(x乘以2的y次方) 1<<2 4
x>>y,右移(x除以2的y次方) 12>>2 3
x ^ y,异或 1^2 3
x & y,与 1&2 0
x | y,或 |2 2
^x,取反 ^2 -3

浮点型

浮点型的表示

package main

import (
   "fmt"
   "reflect"
)

func main() {
   var value1 float64
   value1 = 1    //不管是否有小数点,value1都是浮点型
   value2 := 2   //如果没有小数点,则会被推导为整型
   value3 := 3.0 //如果有小数点,value3会被推导为浮点型
   // v := value1 + value2 //编译失败,因为类型不同
   v := value1 + value3
   fmt.Println(value1, value2, value3, v)
   fmt.Println("v的类型是: ", reflect.TypeOf(v))
}

浮点数的运算

package main

import "fmt"

func main() {
   var value1 float64
   value1 = 1
   value2 := 1.00000000000000000000001
   if value1 == value2 {
      fmt.Println("相等")
   }
}
//相等,因为精度限制在小数点后15位

字符与字符串

字符串的表示

package main

import "fmt"

var value1 float64

func main() {
   s := "aA你2"
   fmt.Println("字符串长度:", len(s))
   for i := 0; i < len(s); i++ {
      fmt.Println(s[i])
   }
   s = "你好, " 
   t := s
   s += "世界。" //字符串可以连接,但原字符串不会改变
   fmt.Println(s)//你好, 世界。
   fmt.Println(t)//你好, 

}

解释字符串

该类字符串使用""括起来,其中的相关转义字符将会被替换。

非解释字符串

该类字符串使用反引号括起来,可以跨行,并且会直接把引号内的所有内容直接输出。

package main

import "fmt"

func main() {
	str1 := `苟利国家生死以\n
			岂因祸福避趋之`
	str2 := "今天天气\n真好"
	fmt.Println(str1)
	fmt.Println(str2)
}

/*苟利国家生死以\n
		岂因祸福避趋之
今天天气
真好*/

遍历字符串

第一种方式输出了乱码,原因是使用的len()方法是不能用来遍历字符串的,因为Go使用UTF-8来保存字符串,所以每个字符由多个字节组成,只输出其中的一个字节是无法显示为一个完整字符的。所以需要使用range方法。

package main

import "fmt"

//遍历字符串
func main() {
   s := "我是中国人"
   for i := 0; i < len(s); i++ {
      fmt.Printf("%c", s[i])
   }
   fmt.Printf("\n")
   for _, v := range s {
      fmt.Printf("%c", v)
   }
   fmt.Println("\n")
}
/*
我是中国人
我是中国人
*/

字符串修改

Go语言字符串不能修改,也就不能使用s[i]的方法来修改字符串中的UTF-8编码。但是可以把字符串赋予给新的变量中,一般使用[]byte或者[]rune类型。如果修改字节,则转化为[]byte类型,如果修改字符,则转化为[]rune类型。

package main

import "fmt"

//修改字符串 byte[]类型
func main() {
   s := "Hello 世界!"
   b := []byte(s)
   b[5] = ','
   fmt.Printf("%s\n", s) //s的值不会被修改
   fmt.Printf("%s\n", b) //修改后的数据
   /*
   Hello 世界!
   Hello,世界!
   */
}
package main

import "fmt"

//修改字符串 []rune
func main() {
   s := "我爱世界"
   b := []rune(s)
   b[2] = '中'
   b[3] = '国'
   fmt.Printf("%s\n", s)
   fmt.Printf("%s\n", string(b))
   /*我爱世界
   我爱中国*/
}

strings包

这是一个处理字符串的包

包含判断

package main

import (
	"fmt"
	"strings"
)

//包含判断
func main() {
	a := "I am Le1aaaaa"
	fmt.Printf("%t\n", strings.HasPrefix(a, "I"))        //判断字符串a 是否以I开头
	fmt.Printf("%t\n", strings.HasSuffix(a, "Le1aaaaa")) //判断字符串a 是否以Le1aaaaa结尾
	fmt.Printf("%t\n", strings.Contains(a, "am"))        //判断字符串a 中是否存在am
	/*
		true
		true
		true
	*/
}

索引

strings.IndexRune(s,str string) int 用于Ascii编码

strings.LastIndex(s,str string) int 用于非Ascii编码

替换

一共有4个参数,其中第一个为原字符串,第二个表示需要被替换的子字符串,第三个表示替换内容,第四个则表示匹配到第几个,如果把上面的n改为数字-1,则表示匹配所有。

package main

import (
   "fmt"
   "strings"
)

//字符串替换
func main() {
   str := "你好世界,这个世界真好!"
   fmt.Println(strings.Replace(str, "世界", "地球", 1))
   //你好地球,这个世界真好!
}

出现频率

strings.Count()

package main

import (
   "fmt"
   "strings"
)

//统计字符串中字符出现的频率
func main() {
   a := "I am Le1aaaaa"
   fmt.Println("a出现了: " + fmt.Sprint(strings.Count(a, "a")) + "次!")
}

字符串总数

package main

import (
   "fmt"
   "unicode/utf8"
)

//统计字符串数量
func main() {
   s := "我爱中国"
   fmt.Printf("%d\n", len([]rune(s)))//4
   fmt.Println(utf8.RuneCountInString(s))//4
}

大小写转换

strings.ToLower(s)

strings.ToUpper(s)

package main

import (
   "fmt"
   "strings"
)

//大小写转换
func main() {
   s := "I am Le1aaaaa,and I am 21"
   fmt.Println(strings.ToLower(s)) //转化为小写
   fmt.Println(strings.ToUpper(s)) //转化为大写
   /*
      i am le1aaaaa,and i am 21
      I AM LE1AAAAA,AND I AM 21
   */
}

修剪

strings.Trim("今天天气真好","今天")//天气真好

分割

strings.Split("a,b,c",",")//以逗号来分割,得到["a","b","c"]

布尔型

布尔类型的表示

v:=true直接表示 或者 v:=(1==2)自动推导。go语言只有true和false,不能用0 1表示。