1. 数字
1.1. 整形
1.1.1. 常用的整形数据类型
Go的整形分为两大类:有符号和无符号两类,每一类分别由8、16、32、64位整形组成,一般无特殊情况,开发中直接使用int较多,因为不用涉及类型转换。
类型 | 描述 | 范围 |
---|---|---|
uint8 | 无符号的8位整形 | [0,2^8-1] —> [0,255] |
uint16 | 无符号的16位整形 | [0,2^16-1] —> [0,65535] |
uint32 | 无符号的32位整形 | [0,2^32-1] —-> [0,4294967295] |
uint64 | 无符号的64位整形 | [0,2^64-1] —-> [0,18446744073709551615] |
int8 | 有符号的8位整形 | [-2^7,2^7-1] —-> [-128,127] |
int16 | 有符号的16位整形 | [-2^15,2^15-1] —> [-32768,32767] |
int32 | 有符号的32位整形 | [-2^31,2^31-1] —-> [-2147483648,2147483647] |
int64 | 有符号的64位整形 | [-2^63,2^63-1] —-> [-9223372036854775808,9223372036854775807] |
类型 | 描述 |
---|---|
uint | 无符号的整形,32位操作系统最大值位2^32,64位操作系统位2^64,不建议跨平台使用 |
int | 有符号的整形,32位操作系统位int32,64位操作系统位int64。默认整数类型为int |
uinptr | 无符号整形,用于存放指针 |
1.1.2. 进制转换
自从Go 1.13之后,引入了数字字面量语法,这样方便直接定义不同进制的数字:
进制 | 定义方式 | 对应十进制数字 | 对应 fmt.Printf() 中符号 |
---|---|---|---|
二进制 | 0b110 0100 | 100 | %b |
八进制 | 0o144 | 100 | %o |
十进制 | 100 | 100 | %d |
十六进制 | 0x64 | 100 | %x |
1.1.3. 案例
package main
import "fmt"
func main() {
// 十进制转换
n1 := 100
fmt.Printf("bin:%b; oct:%o; hex:%x\n", n1, n1, n1)
// 二进制转换
var n2 int8
n2 = 0b1100100
fmt.Printf("oct:%o; dec:%d; hex:%x\n", n2, n2, n2)
// 八进制转换
var n3 uint8 = 0o144
fmt.Printf("bin:%b; dec:%d; hex:%x\n", n3, n3, n3)
// 十六进制
var n4 = 0x64
fmt.Printf("bin:%b; oct:%o; dec:%d\n", n4, n4, n4)
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\02-num>go run main.go
bin:1100100; oct:144; hex:64
oct:144; dec:100; hex:64
bin:1100100; dec:100; hex:64
bin:1100100; oct:144; dec:100
1.2. 浮点数
Go语言支持两种类型的浮点数,float32和float64,没有float类型!其范围如下:
浮点数类型 | 大小 | 最大值(常量) |
---|---|---|
float32 | 约为3.4x10^38,即3.4e38 | math.MaxFloat32 |
float64 | 约为1.8x10^308,即1.8e308。默认float类型 | math.MaxFloat64 |
package main
import (
"fmt"
"math"
)
func main() {
f1,f2 := 1001.0,1e3
fmt.Println("f1-f2 =", f1-f2)
fmt.Printf("%.2f\n", math.Pi)
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\02-num>go run main.go
f1-f2 = 1
3.14
2. 布尔值
Go中的bool与Python不一样,Python中True的值1,False的值为0,而Go中true和false为单独的数据类型,不能与整形进行转换和运算!默认的布尔值为false.
3. 字符串
3.1. Go中字符串定义
3.1.1. 自定义字符串
Go语言中字符串必须用双引号 " "
或者反引号 `` 引用起来,**不能使用单引号**,单引号为字符!<br />Go语言内部使用的是UTF-8编码,因此可以直接定义中文字符串而不需要声明他的类型为UTF-8。Go语言的字符串中涉及特殊字符,如换行符、制表符、单引号、双引号、反斜线等,转义方式和其它语言一致。
`` 中的所有字符都以文本格式表示,不涉及任何转义。
3.1.2. 字符串案例
package main
import "fmt"
func main() {
// 打印windowns中路径
s1 := "e:\\OneDrive\\Projects\\Go\\src\\gitee.com\\studygo\\day01\\03-strings"
s2 := `e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\03-strings`
fmt.Println("path:", s1)
fmt.Println("path:", s2)
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\03-strings>go run main.go
path: e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\03-strings
path: e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\03-strings
3.1.3. 字符串的数据结构
一个字符串是一个不可改变的字节序列,字符串通常是用来包含人类可读的文本数据。和数组不同的是,字符串的元素不可修改,是一个只读的字节数组。
每个字符串的长度虽然也是固定的,但是字符串的长度并不是字符串类型的一部分。字符串的底层是一个结构体!因此不同长度的和内容的字符串变量可以相互赋值,因为赋值的是结构体。也因为如此,在传值或者赋值时,不需要使用指针替代字符串本身。
type StringHeader struct {
Data uintptr // 指向底层字节数组的指针
Len int // 字符串的字节长度
}
3.2. 字符
3.2.1. 字符和字符串
Go中的字符本质是字符切片组成的,字符可以是一个字母、符号或者汉字。而根据字符的编码方式,即ASCII和Unicode,将字符类型分为两类,在实际使用中,如果涉及到中文就不能用byte方式处理,否则会因为字节切割异常导致乱码。
查询UTF-8编码网页: http://www.mytju.com/classcode/tools/encode_utf8.asp
字符类型 | 描述 |
---|---|
byte | ASCII码表中数字,实际上是uint8类型的别名 |
rune | UTF-8码表中数字,实际是int32类型的别名 |
3.2.2. 字符串拆分成字节串
package main
import "fmt"
func main() {
var c0 byte = 'a' // 指定byte类型的字符,即 Ascii 格式编码,底层为uint8类型
var c1 rune = 'a' // 指定rune类型的字符,即 UTF-8 格式编码,底层为int32类型
var c2 = 'a' // 不指定类型则为 rune 类型的字符
fmt.Printf("%T %v %c\n", c0, c0, c0)
fmt.Printf("%T %v %c\n", c1, c1, c1)
fmt.Printf("%T %v %c\n", c2, c2, c2)
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\03-strings>go run main.go
uint8 97 a
int32 97 a
int32 97 a
3.2.3. 字符串的本质
string的底层是一个字节数组,因此可以 string,[]byte,[]rune
可以进行转换,字符串也因此支持切片操作。
package main
import "fmt"
func main() {
str0 := "Hello world!"
str1 := "你好,世界!"
fmt.Printf("str0 --> []byte:%v, str0 --> []rune:%v\n", []byte(str0), []rune(str0))
fmt.Printf("str1 --> []byte:%v, str1 --> []rune:%v\n", []byte(str1), []rune(str1))
// []byte和[]rune转字符串
s0 := []byte{72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33}
fmt.Println("s0=", string(s0))
s1 := []rune{20320, 22909, 65292, 19990, 30028, 65281}
fmt.Println("s1=", string(s1))
// 修改字符串中的字符: "Hello world!" --> "Hello World!"
tmpByte := []byte(str0)
tmpByte[6] = 'W'
fmt.Println(string(tmpByte))
}
[root@heyingsheng 10-slice]# go run str_byte.go
str0 --> []byte:[72 101 108 108 111 32 119 111 114 108 100 33], str0 --> []rune:[72 101 108 108 111 32 119 111 114 108 100 33]
str1 --> []bye:[228 189 160 229 165 189 239 188 140 228 184 150 231 149 140 239 188 129], str1 --> []rune:[20320 22909 65292 19990 30028 65281]
s0= Hello world!
s1= 你好,世界!
Hello World!
3.3. 常见的字符串操作方式
字符类型 | 描述 |
---|---|
len(s) | 求长度,返回的类型为 int。针对字符串而言,取得是字节数 |
utf8.RuneCountInString | 按UTF-8编码格式计算字符串长度 |
+或fmt.Sprintf | 拼接字符串,其中fmt.Sprintf是返回拼接后的字符串 |
strings.Split | 分割字符串 |
strings.Contains | 判断字符串中是否包含某一段字符串 |
strings.HasPrefix | 判断字符串开头 |
strings.HasSuffix | 判断字符串结尾 |
strings.Index | 从前向后匹配,取第一次匹配到的字符串开头索引,-1表示无法匹配子串 |
strings.LastIndex | 从后向前匹配,取最后一次匹配到的字符串开头索引,-1表示无法匹配子串 |
strings.Join | 以特定的字符拼接切片 |
3.4. 案例
3.4.1. 求字符串长度
在Go语言中,字符串是以UTF-8编码方式,而在UTF-8编码下,不同类型的字符对应的长度又不一样,导致中文和英文字符串的长度与预期有差异:
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
s0, s1 := "hello world", "你好,世界"
fmt.Printf("s0:%d\ts1:%d\n", len(s0), len(s1)) // len()对string类型求的长度为字节数量
fmt.Printf("s0:%d\ts1:%d\n", utf8.RuneCountInString(s0), utf8.RuneCountInString(s1)) // 正确计算utf-8字符串长度方式
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\03-strings>go run main.go
s0:11 s1:15
s0:11 s1:5
3.4.2. 截取字符串
在对字符串操作时,字符串截取很常用,但是在Go中,对字符截取比较繁琐,因为索引是按照字节来计算的,而不是字符数量。
package main
import (
"fmt"
"strings"
)
func main() {
var s0 string = "中国加油,武汉加油,黄冈加油!"
// 截取 s0 中逗号之后的内容
start := strings.Index(s0, ",") + len(",")
fmt.Println(s0[start:], start)
// 截取最后一个"加油"字符串到末尾的值
start = strings.LastIndex(s0, "加油")
fmt.Println(s0[start:], start)
// 截取第二个"加油"字符串,并打印索引
indexOne := strings.Index(s0, "加油") + len("加油") // 第一次"加油"的结束索引
indexTwo := indexOne + strings.Index(s0[indexOne:], "加油") // 第二次"加油"的开始索引
indexThree := indexTwo + len("加油") // 第二次加油的结束索引
fmt.Println(s0[indexTwo:indexThree], indexTwo, indexThree)
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\03-strings>go run main.go
武汉加油,黄冈加油! 15
加油! 36
加油 21 27
3.4.3. 修改字符串
字符串是不可变数据类型,无法直接修改,一般是构造新的字符串重新赋值给这个变量!
package main
import (
"fmt"
"strings"
)
func main() {
var s1 string = "gzip: compress log content in buffer,then write to disk." // 修改gzip为Gzip
startIndex := strings.Index(s1, "gzip") + len("gzip")
s1 = "Gzip" + s1[startIndex:]
fmt.Println(s1)
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day01\03-strings>go run main.go
Gzip: compress log content in buffer,then write to disk.
3.4.4. 字符串反转和回文判断
package main
import (
"fmt"
"strings"
)
func check(str string) {
s1 := strings.Split(str, "")
s2 := make([]string, 0, len(s1))
for i := len(s1) - 1; i >= 0; i-- {
s2 = append(s2, s1[i])
}
reverseStr := strings.Join(s2, "")
if str == reverseStr {
fmt.Printf("%v --> 是回文\n", str)
} else {
fmt.Printf("%v --> 不是回文\n", str)
}
}
func main() {
check("上海自来水来自海上")
check("上海自来水来自海上x")
check("x上海自来水来自海上x")
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day03\01-homework>go run main.go
上海自来水来自海上 --> 是回文
上海自来水来自海上x --> 不是回文
x上海自来水来自海上x --> 是回文
3.4.5. 字符串拼接
package main
import (
"fmt"
"strings"
)
func repeatStrings(s string, c int) {
fmt.Println("strings.Repeat:", strings.Repeat(s, c)) // 重复一个指定的字符串,效率非常高
}
func addStrings(s string, c int) {
var res string
for i:=0;i<c;i++ {
res += s // 使用 + 也能实现,方法简单,但是效率低。
}
fmt.Println("+:", res)
}
func builderStrings(s string, c int) {
var res strings.Builder
for i:=0;i<c;i++ {
res.WriteString(s)
}
fmt.Println("+:", res.String())
}
func main() {
repeatStrings("x", 20)
addStrings("x", 20)
builderStrings("x", 20)
}
4. 指针
指针就是内存地址,是16进制的一个数字,Go语言中的指针不能进行偏移和运算,后期的函数和方法涉及到指针操作非常多,一般涉及三个方面:
- 定义指针类型的变量
- 取变量的内存地址: &name
- 根据内存地址取出对应的值: *ptrName
4.1. 指针的操作
Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如: *int
、 *int64
、 *string
等。
4.1.1. 通过变量取指针
package main
import "fmt"
func main() {
var var1 string
var var2 uint8 = 255
var var3 = [8]int{7: 1}
p1, p2, p3 := &var1, &var2, &var3
fmt.Printf("p1 --> type:%T\tvalue:%v\n", p1, p1)
fmt.Printf("p2 --> type:%T\tvalue:%v\n", p2, p2)
fmt.Printf("p3 --> type:%T\tvalue:%v\n", p3, p3)
}
e:\OneDrive\Projects\Go\src\gitee.com\studygo\day02\04-mem-pointer>go run main.go
p1 --> type:*string value:0xc0000881e0
p2 --> type:*uint8 value:0xc0000a0068
p3 --> type:*[8]int value:&[0 0 0 0 0 0 0 1]
4.1.2. 通过指针取值
package main
import "fmt"
func main() {
var var0 uint8 = 255
var p0 *uint8 = &var0
fmt.Printf("var0:%v p0:%v *p0:%v\n", var0, p0, *p0) // var0:255 p0:0xc0000100b0 *p0:255
}
4.1.3. 空指针
package main
func main() {
var a *int8 // panic: runtime error: invalid memory address or nil pointer dereference
*a = 100 // 声明了指针变量却没有初始化,所以提示空指针
}
4.2. make vs new
4.2.1. new案例
package main
import "fmt"
func main() {
a1 := new(int) // 初始化类型指针,该指针对应的地址为该类型的默认值,如int:0,bool:false
*a1 = 255
fmt.Printf("type:%T value:%v *a1:%v\n", a1, a1, *a1) // type:*int value:0xc0000100b0 *a1:255
}
4.2.2. make案例
参考:https://www.yuque.com/duduniao/vnmzv5/lz5sgr#Zdim8
4.2.3. make和new对比
- 二者都是用来做内存分配的。
- make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身,一般用于初始化
- new用于内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针,一般用于创建指针类型的变量
4.3. 值类型和引用类型
4.3.1. 值类型
值类型是指变量对应的内存空间直接存值。Go中的值类型有: 数字(整形和浮点数)、布尔值、字符串、数组、结构体。这类数据类型通常在栈中分配内存!
4.3.2. 引用类型
变量存储的是一个地址,这个地址指向的目标内存才是真正的值。Go中的值类型有:指针、切片、映射、管道、interface。这类数据通常存储在堆中!
5. 基本数据类型转换
Golang 和java / c 不同,Go 在不同类型的变量之间赋值时需要显式转换。也就是说 Golang 中数据类型不能自动转换。
5.1. 数字之间的转换
数字之间可以用 type()
进行转换,如 int64(100)
,需要注意的是: type()
并不是函数。大范围数据类型转为小范围数据类型时,会按照溢出处理,不会报错,但是结果可能是错误的!
package main
import "fmt"
func main() {
a, b := 1000, 3.141592652
fmt.Printf("type(a):%T\ttype(b):%T\n", a, b) // 默认整数和浮点数类型
// int8 超出长度结果异常
fmt.Printf("a --> int8:%v\tint64:%v\tfloat32:%v\n", int8(a), int64(a), float32(a))
fmt.Printf("b --> int8:%v\tint64:%v\t\tfloat32:%v\n", int8(b), int64(b), float32(b))
}
[root@heyingsheng 2020-03-12]# go run 05.go
type(a):int type(b):float64
a --> int8:-24 int64:1000 float32:1000
b --> int8:3 int64:3 float32:3.1415927
5.2. 其它数据类型转string
5.2.1. 使用fmt.Sprintf()函数
package main
import "fmt"
func main() {
a, b, c := 12345, true, '中'
var str string
str = fmt.Sprintf("%v", a)
fmt.Printf("str: type=%T value=%q\n", str, str)
str = fmt.Sprintf("%v", b)
fmt.Printf("str: type=%T value=%q\n", str, str)
str = fmt.Sprintf("%v", c)
fmt.Printf("str: type=%T value=%q\n", str, str)
}
[root@heyingsheng day01]# go run 03-strings/main.go
str: type=string value="12345"
str: type=string value="true"
str: type=string value="20013"
5.2.2. 使用strconv包
func Itoa(i int) string // 将int转为十进制的string,内部调用的还是 FormatInt()
func FormatInt(i int64, base int) string // 将64位整形转为base进制下的字符串,base支持2-32之间的整数
func FormatUint(i uint64, base int) string // FormatInt的无符号版本
func Itoa(i int) string // 将十进制的int转为string
func FormatBool(b bool) string // 将bool转为字符串
// 将浮点数转为字符串
// f 表示待转换的浮点数;fmt表示格式,一般用'f';prec表示小数位数;bitSize表示目标数据类型精度,支持32和64
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
package main
import (
"fmt"
"strconv"
)
func main() {
a, b, c := 12345, 3.1415926, true
var str string
str = strconv.FormatInt(int64(a), 10)
fmt.Printf("str: type=%T value=%q\n", str, str)
str = strconv.FormatFloat(b, 'f', 2, 32 )
fmt.Printf("str: type=%T value=%q\n", str, str)
str = strconv.FormatBool(c)
fmt.Printf("str: type=%T value=%q\n", str, str)
}
[root@heyingsheng day01]# go run 03-strings/main.go
str: type=string value="12345"
str: type=string value="3.14"
str: type=string value="true"
5.3. string转其它数据类型
# 如果类型转换失败,会返回对应的数据类型的零值!!!
func ParseInt(s string, base int, bitSize int) (i int64, err error) // 将string转为整形,base位进制,bitSize为数据类型,如64表示int64,0表示int
func ParseUint(s string, base int, bitSize int) (n uint64, err error) // 将string转为无符号整形
func ParseFloat(s string, bitSize int) (f float64, err error) // 将string转为浮点数,bitSize表示数据类型,如32表示float32
func ParseBool(str string) (value bool, err error) // 将string转为bool
package main
import (
"fmt"
"strconv"
)
func main() {
a, b, c := "123456789", "3.14", "false"
var (
aRes int64
bRes float64
cRes bool
dRes bool
)
aRes, _ = strconv.ParseInt(a, 10, 64)
bRes, _ = strconv.ParseFloat(b, 64)
cRes, _ = strconv.ParseBool(c)
dRes, _ = strconv.ParseBool("Hello")
fmt.Printf("str: type=%T value=%q\n", aRes, aRes)
fmt.Printf("str: type=%T value=%q\n", bRes, bRes)
fmt.Printf("str: type=%T value=%q\n", cRes, cRes)
fmt.Printf("str: type=%T value=%q\n", dRes, dRes) // 转为对应的零值!
}
[root@heyingsheng day01]# go run 03-strings/main.go
str: type=int64 value=%!q(int64=123456789)
str: type=float64 value=%!q(float64=3.14)
str: type=bool value=%!q(bool=false)
str: type=bool value=%!q(bool=false)