04 基本数据类型
04 基本数据类型
Go 语言的数据类型分为两大类:基本数据类型和复合数据类型。
- 基本数据类型:整型、浮点型、布尔型、字符串。
- 复合数据类型:数组、切片、结构体、函数、map、channel、接口等。
本文主要介绍基本数据类型。
整型
这部分和其他语言一样,Go 语言也提供了多种整型,分为有符号和无符号两种,区别在于二进制的首位是否用于表示正负。
- 有符号整型:int8、int16、int32、int64。
- 无符号整型:uint8、uint16、uint32、uint64。
需要额外说的是,int 和 uint 的长度取决于操作系统的位数,32 位系统上是 32 位,64 位系统上是 64 位,在需要明确数据长度的场景下,如二进制传输、文件IO等,应明确使用指定位数的类型,而不能用这两种,以防止在不同平台间出现问题。
Go 1.13 版本后引入了更易读的数字字面量语法:
v1 := 0b00101101 // 二进制
v2 := 0o377 // 八进制
v3 := 0x1ABC // 十六进制浮点数
v4 := 123_456 // 使用下划线分隔
浮点型
Go 遵循 IEEE 754 标准,提供了 32 位和 64 位的浮点类型,也就是 float32 和 float64,默认的浮点类型是 float64,这个和 rust 是一致的。
浮点数的经典问题是精度损失,这一点 Go 语言也未能避免,为了避免精度损失,可以尝试 decimal 这个开源库。
同时,Go 也支持用科学计数法来表示浮点数:
var num1 float64 = 5.1234e2 // 5.1234 * 10^2
var num2 float64 := 5.1234e-2 // 5.1234 / 10^2
fmt.Println(num1, num2) // 512.34 0.051234
布尔值
Go 中 bool 的默认值是 false。
注意: 在其他语言里经常会看到 非零 == true 的操作,但是 Go 中的整型和布尔型之间不能进行强制转换。
字符串
Go 中的字符串是一个原生数据类型,其内部使用 UTF-8 编码,并且字符串是不可变的。
var s1 string = "hello"
多行字符串 可以使用反引号 `
来定义,其中的内容会按原样输出,转义字符无效。
s := `
这是一个
多行
字符串。
`
对字符串的常用操作也都是差不多这几种,主要依赖标准库 strings 包。
import "strings"
// 长度
len("hello, 你好") // 结果是 12,一个汉字是3字节
// 拼接,两种方式
str1 := "你好"
str2 := "世界"
result := str1 + ", " + str2
resultFmt := fmt.Sprintf("%s, %s", str1, str2)
// 分割
parts := strings.Split("a-b-c", "-") // ["a", "b", "c"]
// 包含判断
strings.Contains("seafood", "foo") // true
// 前后缀判断
strings.HasPrefix("image.png", "image") // true
strings.HasSuffix("image.png", ".png") // true
// 查找子串位置
strings.Index("chicken", "ken") // 4
strings.LastIndex("apple pie", "p") // 6
// 拼接切片
s := []string{"a", "b", "c"}
resultJoin := strings.Join(s, "-") // "a-b-c"
字符类型
在处理字符串中的字符时,Go 提供了两种重要的类型:
- byte: uint8 的别名,代表一个 ASCII 字符。
- rune: int32 的别名,代表一个 UTF-8 字符。
由于 Go 字符串底层是 byte 数组,直接按下标遍历包含多字节字符的字符串会导致乱码。
s := "hello 张三"
// 1. 按字节遍历 (byte)
for i := 0; i < len(s); i++ {
fmt.Printf("%c ", s[i])
}
// h e l l o å ¼ ä ¸
// 2. 按字符遍历 (rune)
for _, r := range s {
fmt.Printf("%c ", r)
}
// h e l l o 张 三
range 关键字在遍历字符串时会自动进行 UTF-8 解码,将每个字符作为 rune 类型返回,在处理多语言文本时推荐用这种方式处理。
那如果我们想修改字符串呢?前面我们提到了字符串是不可变的,因此修改字符串需要先将其转换为可变类型,修改后再转换回来。
如果字符串只包含 ASCII 字符,使用 []byte 效率更高,但是一旦存在中文等多字节字符,那就必须使用 []rune 了,否则操作就是错误的。
// 修改 ASCII 字符串
s1 := "big"
byteS1 := []byte(s1)
byteS1[0] = 'p'
fmt.Println(string(byteS1)) // "pig"
// 修改包含中文的字符串
s2 := "白萝卜"
runeS2 := []rune(s2)
runeS2[0] = '红'
fmt.Println(string(runeS2)) // "红萝卜"
类型转换
Go 语言中只有强制类型转换,没有隐式类型转换。
数值类型转换
数值类型之间可以通过 T(v)
的方式进行转换,其中 T 是目标类型,v 是要转换的值。
var a int8 = 20
var b int16 = 40
var c = int16(a) + b // 必须转换成相同类型才能运算
fmt.Printf("值:%v 类型:%T\n", c, c) // 值:60 类型:int16
注意精度损失:从高位类型转换为低位类型时可能会发生溢出。
var a int16 = 129
var b = int8(a) // int8 范围:-128 到 127
println("b=", b) // b= -127,发生了溢出
转换为字符串
Go 提供了两种主要方式将其他类型转换为字符串:
可以使用 fmt.Sprintf:
var i int = 20
var f float64 = 12.456
var b bool = true
str1 := fmt.Sprintf("%d", i) // "20"
str2 := fmt.Sprintf("%.2f", f) // "12.46"
str3 := fmt.Sprintf("%t", b) // "true"
更推荐的是使用 strconv 包,因为性能更好:
import "strconv"
// 整数转字符串
s1 := strconv.Itoa(20) // "20"
s2 := strconv.FormatInt(int64(20), 10) // "20",第二个参数是进制
// 浮点数转字符串
s3 := strconv.FormatFloat(20.113, 'f', 2, 64) // "20.11" 按照64位浮点转换为两位小数
// 布尔值转字符串
s4 := strconv.FormatBool(true) // "true"
字符串转数值类型
// 字符串转整数
s := "1234"
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
// 处理转换错误
}
// 字符串转浮点数
str := "3.14159"
f, err := strconv.ParseFloat(str, 64)
// 字符串转布尔值
b, err := strconv.ParseBool("true")