一、前言
1、go语言介绍
Go 即Golang,是Google 公司2009 年11 月正式对外公开的一门编程语言。Go 语言不仅拥有静态编译语言的安全和高性能,而且又达到了动态语言开发速度和易维护性。有人形容Go 语言:Go = C + Python , 说明Go 语言既有C 语言程序的运行速度,又能达到Python 语言的快速开发。
2、Go 环境win 环境搭建
1、下载安装Golang
Go 官网下载地址:https://golang.org/dl/
Go 官方镜像站(推荐):Downloads - The Go Programming Language
2、安装软件
1、双击下一步下一步进行安装;
2、验证是否安装成功:go version;
3、查看Go环境
go env
说明:
Go1.11 版本之后无需手动配置环境变量,使用go mod 管理项目,也不需要非得把项目放到GOPATH 指定目录下,你可以在你磁盘的任何位置新建一个项目。
Go1.13 以后可以彻底不要GOPATH 了。
二、基本语法
1、变量、常量命名规则
(1)、变量名由字母、数字、下划线组成,其中首个字符不能为数字;
(2)、关键字和保留字不能用作变量名。
(3)、变量需要声明后才能使用,同一作用域内不支持重复声明。
(4)、Go 语言的变量声明后必须使用。
(5)、变量的名字是区分大小写的如: age 和Age 是不同的变量。
(6)、变量命名一般采用驼峰式,当遇到特有名词(缩写或简称,如DNS)的时候,特有名词根据是否私有全部大写或小写。
1.1、Go 语言代码风格
1、代码每一行结束后不用写分号( ;);
2、运算符左右建议各加一个空格 var username string = "xiaobai";
3、Go 语言程序员推荐使用驼峰式命名;
4、强制的代码风格——左括号必须紧接着语句不换行;
5、go fmt 主要用于格式化文档,让所有人的代码风格保持一致。
2、变量的声明
Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量若未指定初始值,则会被初始化成其类型的默认值,例如: 整型和浮点型变量的默认值为0。字符串变量的默认值为空字符串。布尔型变量默认为false。切片、函数、指针变量的默认为nil。
2.1、var 声明变量
var 变量名 类型 (= 表达式)
eg: var name string = "zhangsan"
2.2、变量类型自动推导
var 变量名 (= 表达式)
eg: var name = "zhangsan"
2.3、短变量声明法
变量名 := 表达式
注意:短变量只能用于声明局部变量,不能用于全局变量的声明
eg: name := "zhangsan"
2.4、变量的批量声明
var identifier1, identifier2 type
// 1、批量声明同类型变量
var name, area string
name = "wang"
area = "上海"
fmt.Printf("name:%v ;area:%v\n",name,area)
// 2、批量声明不同类型的变量
var (
user string
sex string
age int
)
user = "wang"
sex = "man"
age = 20
fmt.Printf("user:%v-%T;sex:%v-%T;age:%v-%T\n",user,user,sex,sex,age,age)
// 3、批量声明不同类型的变量,并赋值
var (
user2 = "wang"
sex2 = "man"
age2 = 20
)
user2 = "wang"
sex2 = "man"
age2 = 20
fmt.Printf("user:%v-%T;sex:%v-%T;age:%v-%T\n",user2,user2,sex2,sex2,age2,age2)
2.5、匿名变量
在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量(anonymous variable)。匿名变量用一个下划线_表示,例如:
func getInfo() (int, string) {
return 10, "张三"
}
func main() {
_, username := getInfo()
fmt.Println(username)
}
匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明。
注意事项:
1、函数外的每个语句都必须以关键字开始(var、const、func 等);
2、:= 不能使用在函数外;
3、_ 多用于占位,表示忽略值。
3、常量
相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。常量的声明和变量声明非常类似,只是把var 换成了const,常量在定义的时候必须赋值。
3.1、使用const 定义常量
// 1、常量定义
const pi = 3.1415926
const name = "wang"
// 2、批量多个常量
const (
user = "wang"
sex = "man"
)
// 3、const同时声明多个常量时,如果省略了值则表示和上面一行的值相同
const (
n1 = 100
n2
n3
)
3.2、iota
iota 是golang 语言的常量计数器,只能在常量的表达式中使用;
iota 在const 关键字出现时将被重置为0(const 内部的第一行之前);
const 中每新增一行常量声明将使iota 计数一次(iota 可理解为const 语句块中的行索引)。
3.2.1、iota 只能在常量的表达式中使用。
fmt.Println(iota)
编译错误: undefined: iota
3.2.2、每次const 出现时,都会让iota 初始化为0。【自增长】
const a = iota // a=0
const (
b = iota //b=0
c //c=1
)
3.2.3、const iota 使用_跳过某些值
const (
n1 = iota //0
n2 //1
_
n4 //3
)
3.2.4、iota 声明中间插队
const (
n1 = iota //0
n2 = 100 //100
n3 = iota //2
n4 //3
)
const n5 = iota //0
3.2.4、多个iota 定义在一行
const (
a, b = iota + 1, iota + 2 //1,2
c, d //2,3
e, f //3,4
)
4、数据类型介绍
Go 语言中数据类型分为:基本数据类型和复合数据类型
基本数据类型有:整型、浮点型、布尔型、字符串
复合数据类型有:数组、切片、结构体、函数、map、通道(channel)、接口等。
4.1、整数
整型分为以下两个大类: 有符号整形按长度分为:int8、int16、int32、int64 对应的无符号整型:uint8、uint16、uint32、uint64
%b 二进制 %c 对应的 Unicode 码位所代表的字符 %d 十进制 %o 八进制 %O 八进制,带 0o 前缀 %q 使用 Go 语法安全转义的单引号字符文字。 %x 十六进制,af用小写字母 %X 十六进制,AF为大写字母 %U Unicode 格式:U+1234;与 "U+%04X" 相同
类型 | 范围 | 占用空间(字节) | 有无符号 |
---|---|---|---|
int8 | (-128到127) -2^7 到2^7-1 | 1 | 有 |
int16 | (-32768 到32767) -2^15 到2^15-1 | 2 | 有 |
int32 | (-2147483648 到2147483647) | 4 | 有 |
int64 | (-9223372036854775808 到9223372036854775807)<br />-2^63 到2^63-1 | 8 | 有 |
uint8 | (0 到255) 0 到2^8-1 | 1 | 无 |
uint16 | (0 到65535) 0 到2^16-1 | 2 | 无 |
uint32 | (0 到4294967295) 0 到2^32-1 | 4 | 无 |
uint64 | (0 到18446744073709551615) 0 到2^64-1 | 8 | 无 |
关于字节: 字节也叫Byte,是计算机数据的基本存储单位。8bit(位)=1Byte(字节);1024Byte(字节)=1KB;1024KB=1MB 1024MB=1GB 1024GB=1TB 。
特殊整型
类型 | 描述 |
---|---|
uint | 32 位操作系统上就是uint32,64 位操作系统上就是uint64 |
int | 32 位操作系统上就是int32,64 位操作系统上就是int64 |
uintptr | 无符号整型,用于存放一个指针 |
注意:
在使用int 和uint 类型时,不能假定它是32 位或64 位的整型,而是考虑int 和uint可能在不同平台上的差异。
注意事项:
实际项目中整数类型、切片、map 的元素数量等都可以用int 来表示。在涉及到二进制传输、为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用int 和uint。
int不同长度直接的转换:
package main
import (
"fmt"
)
func main() {
var num1 int8
num1 = 127
num2 := int32(num1)
fmt.Printf("值:%v 类型%T", num2, num2) //值:127 类型int32
}
Go1.13 版本之后引入了数字字面量语法,这样便于开发者以二进制、八进制或十六进制浮点数的格式定义数字,例如:v := 0b00101101, 代表二进制的101101,相当于十进制的45。v := 0o377,代表八进制的377,相当于十进制的255。v := 0x1p-2,代表十六进制的1 除以2²,也就是0.25。而且还允许我们用_ 来分隔数字,比如说:v := 123_456 等于123456。我们可以借助fmt 函数来将一个整数以不同进制形式展示。
4.2、浮点型
Go 语言支持两种浮点型数:float32 和float64。
这两种浮点型数据格式遵循IEEE 754 标准:float32 的浮点数的最大范围约为3.4e38,可以使用常量定义:math.MaxFloat32。float64 的浮点数的最大范围约为1.8e308,可以使用一个常量定义:math.MaxFloat64。打印浮点数时,可以使用fmt 包配合动词%f,代码如下:
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("%v\n", math.Pi) //原样输出
fmt.Printf("%f\n", math.Pi) //默认保留6 位小数
fmt.Printf("%.2f\n", math.Pi) //保留2 位小数
}
Go 语言中浮点数64位系统中默认是float64
num := 1.1
fmt.Printf("值:%v--类型:%T", num, num) //值:1.1--类型:float64
4.2.1、float 精度丢失问题
几乎所有的编程语言都有精度丢失这个问题,这是典型的二进制浮点数精度损失问题,在定长条件下,二进制小数和十进制小数互转可能有精度丢失。
d := 1129.6
fmt.Println((d * 100)) //输出:112959.99999999999
var d float64 = 1129.6
fmt.Println((d * 100)) //输出:112959.99999999999
m1 := 8.2
m2 := 3.8
fmt.Println(m1 - m2) // 期望是4.4,结果打印出了4.399999999999999
使用第三方包来解决精度损失问题:
https://github.com/shopspring/decimal
4.2.2、科学计数法表示浮点类型
num8 := 5.1234e2 // ? 5.1234 * 10 的2 次方
num9 := 5.1234E2 // ? 5.1234 * 10 的2 次方shift+alt+向下的箭头
num10 := 5.1234E-2 // ? 5.1234 / 10 的2 次方0.051234
fmt.Println("num8=", num8, "num9=", num9, "num10=", num10)
4.3、布尔值
Go 语言中以bool 类型声明布尔型数据,布尔型数据只有true(真)和false(假)两个值。
注意:
-
布尔类型变量的默认值为false。
-
Go 语言中不允许将整型强制转换为布尔型.
-
布尔型无法参与数值运算,也无法与其他类型进行转换。
package main
import (
"fmt"
"unsafe"
)
func main() {
var b = true
fmt.Println(b, "占用字节:", unsafe.Sizeof(b))
}
4.4、字符串
Go 语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型(int、bool、float32、float64 等)一样。Go 语言里的字符串的内部实现使用UTF-8 编码。字符串的值为双引号(")中的内容,可以在Go 语言的源码中直接添加非ASCII 码字符,例如:s1 := "hello";s2 := "你好"
字符串转义符
Go 语言的字符串常见转义符包含回车、换行、单双引号、制表符等,如下表所示。
转义符 | 含义 |
---|---|
\r | 回车符(返回行首 |
\n | 换行符(直接跳到下一行的同列位置) |
\t | 制表符 |
\' | 单引号 |
\" | 双引号 |
\\ | 反斜杠 |
举个例子,我们要打印一个Windows 平台下的一个文件路径:
package main
import (
"fmt"
)
func main() {
fmt.Println("str := \"d:\\Code\\demo\\go.exe\"")
}
4.4.1、多行字符串
Go 语言中要定义一个多行字符串时,就必须使用反引号字符(tab键上的`):
s1 := `第一行
第二行
第三行
`
fmt.Println(s1)
反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。
4.4.2、字符串的常用操作
方法 | 介绍 |
---|---|
len(str) | 求长度(字节) |
+或fmt.Sprintf | 拼接字符串 |
strings.Split | 分割 |
strings.contains | 判断是否包含 |
strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 |
strings.Index(),strings.LastIndex() | 子串出现的位置,查找不到返回-1 |
strings.Join(a[]string, sep string) join | 操作把切片组合成字符串 |
// len(str)求字符串的长度
var str = "this is str"
fmt.Println(len(str))
// 字符串拼接
var str1 = "你好"
var str2 = "golang"
fmt.Println(str1 + str2)
var str3 = fmt.Sprintf("%v %v", str1, str2)
fmt.Println(str3)
// 字符串分割
var str = "123-456-789"
var arr = strings.Split(str, "-")
fmt.Println(arr)
// 字符串判断是否包含
var str = "this is golang"
var flag = strings.Contains(str, "golang")
fmt.Println(flag)
// 字符串判断首字符、尾字母是否包含指定字符
var str = "this is golang"
var flag = strings.HasPrefix(str, "this")
fmt.Println(flag)
var str = "this is golang"
var flag = strings.HasSuffix(str, "go")
fmt.Println(flag)
// 判断字符串出现的位置
var str = "this is golang"
var index = strings.Index(str, "is") //从前往后
fmt.Println(index)
var str = "this is golang"
var index = strings.LastIndex(str, "is") //从后网前
fmt.Println(index)
// Join 拼接字符串
var str = "123-456-789"
var arr = strings.Split(str, "-")
var str2 = strings.Join(arr, "*")
fmt.Println(str2)
4.4.3、修改字符串
要修改字符串,需要先将其转换成[]rune 或[]byte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组。
func changeString() {
s1 := "big"
// 强制类型转换
byteS1 := []byte(s1)
byteS1[0] = 'p'
fmt.Println(string(byteS1))
s2 := "白萝卜"
runeS2 := []rune(s2)
runeS2[0] = '红'
fmt.Println(string(runeS2))
}
4.5、byte 和rune 类型
组成每个字符串的元素叫做“字符”,可以通过遍历字符串元素获得字符。字符用单引号(’)包裹起来,如:
package main
import "fmt"
func main() {
a := 'a'
b := '0'
//当我们直接输出byte(字符)的时候输出的是这个字符对应的ASCII码值
fmt.Println(a)
fmt.Println(b)
//如果我们要输出这个字符,需要格式化输出
fmt.Printf("%c--%c", a, b) //%c 相应Unicode 码点所表示的字符
}
字节(byte):是计算机中数据处理的基本单位,习惯上用大写B来表示,1B(byte,字节)= 8bit(位)。
字符:是指计算机中使用的字母、数字、字和符号。
utf-8:一个汉子占用3 个字节一个字母占用一个字节
a := "m"
fmt.Println(len(a)) //1
b := "张"
fmt.Println(len(b)) //3
4.5.1、Go语言的字符有以下两种:
(1)、uint8 类型,或者叫byte 型,代表了ASCII 码的一个字符。
(2)、rune 类型,代表一个UTF-8 字符。当需要处理中文、日文或者其他复合字符时,则需要用到rune 类型。rune 类型实际是一个int32。Go 使用了特殊的rune 类型来处理Unicode,让基于Unicode 的文本处理更为方便,也可以使用byte 型进行默认字符串处理,性能和扩展性都有照顾。
// 遍历字符串
package main
import "fmt"
func main() {
s := "hello 张三"
for i := 0; i < len(s); i++ { //byte
fmt.Printf("%v(%c) ", s[i], s[i])
}
fmt.Println()
for _, r := range s { //rune
fmt.Printf("%v(%c) ", r, r)
}
fmt.Println()
}
输出:
104(h) 101(e) 108(l) 108(l) 111(o) 32( ) 229(å) 188(¼) 160( ) 228(ä) 184(¸) 137()
104(h) 101(e) 108(l) 108(l) 111(o) 32( ) 24352(张) 19977(三)
因为UTF8 编码下一个中文汉字由3 个字节组成,所以我们不能简单的按照字节去遍历一个包含中文的字符串,否则就会出现上面输出中第一行的结果。字符串底层是一个byte 数组,所以可以和[]byte 类型相互转换。字符串是不能修改的字符串是由byte 字节组成,所以字符串的长度是byte 字节的长度。rune 类型用来表示utf8 字符,一个rune 字符由一个或多个byte 组成。
c3 := "营"
c4 := '营'
fmt.Printf("C3 的类型%T--C4 的类型%T", c3, c4) //C3 的类型string--C4 的类型int32
4.6、GoLang 中基本数据类型之间的转换
4.6.1、关于golang 中的数据类型转换
Go 语言中只有强制类型转换,没有隐式类型转换。.
4.6.2、数值类型之间的相互转换
数值类型包括:整形和浮点型
package main
import "fmt"
func main() {
var a int8 = 20
var b int16 = 40
var c = int16(a) + b //要转换成相同类型才能运行
fmt.Printf("值:%v--类型%T", c, c) //值:60--类型int16
}
package main
import "fmt"
func main() {
var a float32 = 3.2
var b int16 = 6
var c = a + float32(b)
fmt.Printf("值:%v--类型%T", c, c) //值:9.2--类型float32
}
转换的时候建议从低位转成高位,高位转成低位的时,如果转换不成功就会溢出,和我们想的结果不一样,比如:
package main
func main() {
var a int16 = 129
var b = int8(a) // 范围-128 到127
println("b=", b) //b= -127 //错误
}
比如,使用math 包的Sqrt()函数,该函数接收的是float64 类型的参数,而变量a 和b 都是int 类型的,这个时候就需要将a 和b 强制类型转换为float64 类型。
var a, b = 3, 4
var c int
// math.Sqrt()接收的参数是float64 类型,需要强制转换
c = int(math.Sqrt(float64(a*a + b*b)))
fmt.Println(c)
4.6.3、其他类型转换成String 类型
1、sprintf 把其他类型转换成string 类型
注意:sprintf 使用中需要注意转换的格式int 为%d;float 为%f;bool 为%t;byte 为%c;
2、使用strconv 包里面的几种转换方法进行转换
package main
import (
"fmt"
"strconv"
)
func main() {
//1、int 转换成string
var num1 int = 20
s1 := strconv.Itoa(num1)
fmt.Printf("str type %T ,strs=%v \n", s1, s1)
// 2、float 转string
var num2 float64 = 20.113123
strs = fmt.Sprintf("%d", i)
fmt.Printf("str type %T ,strs=%v \n", strs, strs)
strs = fmt.Sprintf("%f", f)
fmt.Printf("str type %T ,strs=%v \n", strs, strs)
strs = fmt.Sprintf("%t", t)
fmt.Printf("str type %T ,strs=%v \n", strs, strs)
strs = fmt.Sprintf("%c", b)
fmt.Printf("str type %T ,strs=%v \n", strs, strs)
/*
参数1:要转换的值
参数2:格式化类型
'f'(-ddd.dddd)、
'b'(-ddddp±ddd,指数为二进制)、
'e'(-d.dddde±dd,十进制指数)、
'E'(-d.ddddE±dd,十进制指数)、
'g'(指数很大时用'e'格式,否则'f'格式)、
'G'(指数很大时用'E'格式,否则'f'格式)。
参数3: 保留的小数点-1(不对小数点格式化)
参数4:格式化的类型
*/
s2 := strconv.FormatFloat(num2, 'f', 2, 64)
fmt.Printf("str type %T ,strs=%v \n", s2, s2)
// 3、bool 转string
s3 := strconv.FormatBool(true)
fmt.Printf("str type %T ,strs=%v \n", s3, s3)
//4、int64 转string
var num3 int64 = 20
/*
第二个参数为进制
*/
s4 := strconv.FormatInt(num3, 10)
fmt.Printf("类型%T ,strs=%v \n", s4, s4)
}
4.6.4、String 类型转换成数值类型
1、string 类型转换成int 类型
var s = "1234"
i64, _ := strconv.ParseInt(s, 10, 64)
fmt.Printf("值:%v 类型:%T", i64, i64)
2、string 类型转换成float 类型
str := "3.1415926535"
v1, _ := strconv.ParseFloat(str, 32)
v2, _ := strconv.ParseFloat(str, 64)
fmt.Printf("值:%v 类型:%T\n", v1, v1)
fmt.Printf("值:%v 类型:%T", v2, v2)
3、string 类型转换成bool 类型(意义不大)
b, _ := strconv.ParseBool("true") // string 转bool
fmt.Printf("值:%v 类型:%T", b, b)
4、string 转字符
s := "hello 张三"
for _, r := range s { //rune
fmt.Printf("%v(%c) ", r, r)
}
fmt.Println()
4.6.5、数值类型没法和bool 类型进行转换
注意:在go 语言中数值类型没法直接转换成bool 类型bool 类型也没法直接转换成数值类型