# 1.4 数据类型:byte、rune与字符串
## 1. byte 与 rune
**byte**,占用1个节字,就 8 个比特位(2^8 = 256,因此 byte 的表示范围 0->255),所以它和 `uint8` 类型本质上没有区别,它表示的是 ACSII 表中的一个字符。
如下这段代码,分别定义了 byte 类型和 uint8 类型的变量 a 和 b
```go
import "fmt"
func main() {
var a byte = 65
// 8进制写法: var a byte = '\101' 其中 \ 是固定前缀
// 16进制写法: var a byte = '\x41' 其中 \x 是固定前缀
var b uint8 = 66
fmt.Printf("a 的值: %c \nb 的值: %c", a, b)
// 或者使用 string 函数
// fmt.Println("a 的值: ", string(a)," \nb 的值: ", string(b))
}
```
> 明哥注:fmt.Printf 中的 %c 表示输入为单个字符,详情请查看:[5.1 fmt.Printf 方法详解](http://golang.iswbm.com/en/latest/c05/c05_01.html)
在 ASCII 表中,由于字母 A 的ASCII 的编号为 65 ,字母 B 的ASCII 编号为 66,所以上面的代码也可以写成这样
```go
import "fmt"
func main() {
var a byte = 'A'
var b uint8 = 'B'
fmt.Printf("a 的值: %c \nb 的值: %c", a, b)
}
```
他们的输出结果都是一样的。
```
a 的值: A
b 的值: B
```
**rune**,占用4个字节,共32位比特位,所以它和 `uint32` 本质上也没有区别。它表示的是一个 Unicode字符(Unicode是一个可以表示世界范围内的绝大部分字符的编码规范)。
```go
import (
"fmt"
"unsafe"
)
func main() {
var a byte = 'A'
var b rune = 'B'
fmt.Printf("a 占用 %d 个字节数\nb 占用 %d 个字节数", unsafe.Sizeof(a), unsafe.Sizeof(b))
}
```
输出如下
```
a 占用 1 个字节数
b 占用 4 个字节数
```
由于 byte 类型能表示的值是有限,只有 2^8=256 个。所以如果你想表示中文的话,你只能使用 rune 类型。
```ro
var name rune = '中'
```
或许你已经发现,上面我们在定义字符时,不管是 byte 还是 rune ,我都是使用单引号,而没使用双引号。
对于从 Python 转过来的人,这里一定要注意了,在 Go 中单引号与 双引号并不是等价的。
单引号用来表示字符,在上面的例子里,如果你使用双引号,就意味着你要定义一个字符串,赋值时与前面声明的前面会不一致,这样在编译的时候就会出错。
```go
cannot use "A" (type string) as type byte in assignment
```
上面我说了,byte 和 uint8 没有区别,rune 和 uint32 没有区别,那为什么还要多出一个 byte 和 rune 类型呢?
理由很简单,因为uint8 和 uint32 ,直观上让人以为这是一个数值,但是实际上,它也可以表示一个字符,所以为了消除这种直观错觉,就诞生了 byte 和 rune 这两个别名类型。
## 2. 字符串
字符串,可以说是大家很熟悉的数据类型之一。定义方法很简单
```go
var mystr string = "hello"
```
上面说的byte 和 rune 都是字符类型,若多个字符放在一起,就组成了字符串,也就是这里要说的 string 类型。
比如 `hello` ,对照 ascii 编码表,每个字母对应的编号是:104,101,108,108,111
```go
import (
"fmt"
)
func main() {
var mystr01 string = "hello"
var mystr02 [5]byte = [5]byte{104, 101, 108, 108, 111}
fmt.Printf("mystr01: %s\n", mystr01)
fmt.Printf("mystr02: %s", mystr02)
}
```
输出如下,mystr01 和 mystr02 输出一样,说明了 string 的本质,其实是一个 byte数组
```
mystr01: hello
mystr02: hello
```
通过以上学习,我们知道字符分为 byte 和 rune,占用的大小不同。
这里来考一下大家,`hello,中国` 占用几个字节?
要回答这个问题,你得知道 Go 语言的 string 是用 uft-8 进行编码的,英文字母占用一个字节,而中文字母占用 3个字节,所以 `hello,中国` 的长度为 5+1+(3*2)= 12个字节。
```go
import (
"fmt"
)
func main() {
var country string = "hello,中国"
fmt.Println(len(country))
}
// 输出
12
```
以上虽然我都用双引号表示 一个字符串,但这并不是字符串的唯一表示方式。
除了双引号之外 ,你还可以使用反引号。
大多情况下,二者并没有区别,但如果你的字符串中有转义字符`\` ,这里就要注意了,它们是有区别的。
使用反引号号包裹的字符串,相当于 Python 中的 raw 字符串,会忽略里面的转义。
比如我想表示 `\r\n` 这个 字符串,使用双引号是这样写的,这种叫解释型表示法
```go
var mystr01 string = "\\r\\n"
```
而使用反引号,就方便多了,所见即所得,这种叫原生型表示法
```go
var mystr02 string = `\r\n`
```
他们的打印结果 都是一样的
```go
import (
"fmt"
)
func main() {
var mystr01 string = "\\r\\n"
var mystr02 string = `\r\n`
fmt.Println(mystr01)
fmt.Println(mystr02)
}
// output
\r\n
\r\n
```
如果你仍然想使用解释型的字符串,但是各种转义实在太麻烦了。你可以使用 fmt 的 `%q` 来还原一下。
```go
import (
"fmt"
)
func main() {
var mystr01 string = `\r\n`
fmt.Print(`\r\n`)
fmt.Printf("的解释型字符串是: %q", mystr01)
}
```
输出如下
```go
\r\n的解释型字符串是: "\\r\\n"
```
同时反引号可以不写换行符(因为没法写)来表示一个多行的字符串。
```go
import (
"fmt"
)
func main() {
var mystr01 string = `你好呀!
我的公众号是: Go编程时光,欢迎大家关注`
fmt.Println(mystr01)
}
```
输出如下
```
你好呀!
我的公众号是: Go编程时光,欢迎大家关注
```
---
- 第一章:基础知识
- 1.1 一文搞定开发环境的搭建
- 1.2 五种变量创建的方法
- 1.3 数据类型:整型与浮点型
- 1.4 数据类型:byte、rune与字符串
- 1.5 数据类型:数组与切片
- 1.6 数据类型:字典与布尔类型
- 1.7 数据类型:指针
- 1.8 流程控制:if-else
- 1.9 流程控制:switch-case
- 1.10 流程控制:for 循环
- 1.11 流程控制:goto 无条件跳转
- 1.12 流程控制:defer 延迟语句
- 1.13 流程控制:理解 select 用法
- 1.14 异常机制:panic 和 recover
- 1.15 语法规则:理解语句块与作用域
- 第二章:面向对象
- 2.1 面向对象:结构体与继承
- 2.2 面向对象:接口与多态
- 2.3 面向对象:结构体里的 Tag 用法
- 2.4 学习接口:详解类型断言
- 2.5 学习接口:Go 语言中的空接口
- 2.6 学习接口:接口的三个"潜规则"
- 2.7 学习反射:反射三定律
- 2.8 学习反射:全面学习反射的函数
- 2.9 详细图解:静态类型与动态类型
- 2.10 关键字:make 和 new 的区别?
- 第三章:项目管理
- 3.1 依赖管理:包导入很重要的 8 个知识点
- 3.2 依赖管理:超详细解读 Go Modules 应用
- 3.3 开源发布:如何开源自己写的包给别人用?
- 3.4 代码规范:Go语言中编码规范
- 第四章:并发编程
- 4.1 学习 Go 函数:理解 Go 里的函数
- 4.2 学习 Go 协程:goroutine
- 4.3 学习 Go 协程:详解信道/通道
- 4.4 学习 Go 协程:WaitGroup
- 4.5 学习 Go 协程:互斥锁和读写锁
- 4.7 学习 Go 协程: 信道死锁经典错误案例
- 4.7 学习 Go 协程:如何实现一个协程池?
- 4.8 理解 Go 语言中的 Context
- 4.9 学习一些常见的并发模型
- 第五章:学标准库
- 5.1 fmt.Printf 方法详解
- 5.2 os/exec 执行命令的五种姿势
- 第六章:开发技能
- 6.1 Go 命令:go test 工具详解
- 6.2 单元测试:如何进行单元测试?
- 6.3 调试技巧:使用 GDB 调试 Go 程序
- 6.4 Go 命令: Go 命令指南
- 第七章:暂未分类
- 7.1 20 个学习 Go 语言的精品网站
