5.go基础入门-字符(byte、rune)、字符串(string)、布尔(bool)

前言

什么是字符?什么是字符串?

// 输出一句话
fmt.Println("ab吃饭")
/*
输出结果:
ab吃饭
*/

// 字符
a := 'a'

我们先看一下这句话 “ab吃饭”,这一句话就是字符串,这一串字符由四个字符组成,分别是 ‘a’,‘b’,‘吃’,‘饭’;所以单独拿出来,每一个字就是一个字符,字符类型单引号引用,并且只包含一个字符;而字符串,顾名思义就是可以多个字符串联在一起,双引号引用;(我们可以把字符串理解成是一个由双引号组成的容器,把它看做是长方形凹槽;可以把字符理解成乒乓球,一个字符就是一个乒乓球;我们可以往凹槽里放入乒乓球)

[ ‘a’, ‘b’, ‘吃’, ‘饭’ ]

  • 字符: 单引号组成、内容只有一个字符(它就是一个乒乓球,对应一个字符,所以内容必然是有值的,内容为空时会报错)
  • 字符串: 双引号组成(它只是一个容器,乒乓球放不放都行,所以里面没内容也不会报错)

字符 byte

我们在前面的篇章中说过,我们的一切数据,都是以二进制的形式存在于内存当中的,所以我们所说的字符类型也是如此;我们前面讲过 uint8 类型的整数,他是无符号,8个比特位(1个字符)的整数,所表示的范围是 0到255,一共256个整数;那这个 uint8 跟我们讲的 byte有什么关系呢?

还记得上一章我们讲的浮点数吗?虽然我们直观看到的是小数,但是实际上他是用IEEE754标准把浮点数转换成了二进制数据之后,再存储在内存中的;也就是说 我们的 byte 字符型数据也是通过某种方式,转换成了二进制,再存储进内存;所以我们在讲解 byte、rune、string之前,先把 ‘字符’ 转 ‘二进制’ 的方式讲明白,后面我们就能很简单理解这三种类型。

编码是信息从一种形式或格式转换为另一种形式的过程,也称为计算机编程语言的代码简称编码。这是什么意思呢?

我们计算机中有很多本字典,我们拿 ASCII 这本字典做个列子, 我们键盘上的 26个大写字母、26个小写字母、符号、数字0-9等这些字符都在ASCII这个字典中,每个字符在字典中都有一个位置,字符 ‘a’ 在 ASCII字典中的位置是 97,所以我们把 ‘a’ 转成 97,再把97转成二进制,然后存储到内存中;同理,我们拿到内存中的二进制值,再通过二进制转换成97,我们在 ASCII 字典中取出位置97的字符,我们就得到了字符 ‘a’。是不是既简单又易懂,所以我们把这种方式成为 编码(信息从一种形式或格式转为另一种形式的过程)。

ASCII(美国信息交换标准代码) 这本比较早的字典用于显示新英语和其他西欧语言,这本字典的大小是一个字节,也就是256个位置,范围是0-255,刚好和uint8一样,无符号256个数字;我们上面已经说过, uint8和byte大小和存储都是一样的,无符号256个数字,只是名称不一样;之所以byte是字符型,是因为byte中存储的数字其实是对应字典中的位置的,为了在字面上可以更直观的表达出字符型,所以没有使用 uint8 这个类型来存储,而是创建多一种名称不同,而实际与uint8一样的类型 byte。

其所我们的 byte 类型就是对应 ASCII 编码表的一种类型,他存储的是ASCII编码表的字符位置。

// 创建一个字符型 'a'
var a byte = 'a'
fmt.Println(a)
/*
输出结果:
97
*/

var b byte = ''
/* 报错 empty rune literal or unescaped ' */

var c byte = '措'
/* 报错 constant 25514 overflows byte */

我们 fmt.Println 函数打印 a 变量的值,结果是 97,而非 ‘a’,由此而知我们 a 变量存储的值实际上是 97,而我们所看到的 ‘a’,其实是计算机在编码表中取出来显示的符号。

变量 b 报错,上面说过 byte 中存储的实际上就是 ASCII 编码标准中的字符编号(位置),字符不能为空,所以报错。

变量 c 报错,byte 是一个字节 8 个bit,无符号 整型,256个数字,字符 ‘措’ 是 Unicode 编码标准(下文讲述)中的字符,他在 Unicode 标准中的字符编号(位置)是 25514, byte 所能存储的最大数字是 255,而我们把数字 25514 存储到 byte 类型中就会报错 “constant 25514 overflows byte”,就是溢出,放不下那么大的数字。

到这里我们应该明白 byte 类型了,我们下面做个小总结:

  • byte 1字节,无符号,256个正数,范围0-255
  • ASCII 编码表,256个字符,对应 byte,只适用美国使用电脑
  • 编码表字符与计算机二进制之间的转换,我们把这个过程称之为 编码

字符 rune

上面有一个重点,大家应该也注意到了就是, ASCII 编码,他主要用于 新英语和其他西欧语言的转换。常用的 符号、字母、数字的确可以使用 ASCII 编码就能满足,但如果是显示我们的中文呢?我们的中文汉字呢?只有256个怎么够我们使用?

汉字的数量并没有准确数字,大约将近十万个,日常所使用的汉字只有几千字。据统计,1000个常用字能覆盖约92%的书面资料,2000字可覆盖98%以上,3000字则已到99%,简体与繁体的统计结果相差不大。(百度百科)

所以 ASCII 编码只是适用于美国、西欧等国家,我们国家如果要使用电脑并且显示中文,就要有一个包含中文的编码标准,所以我们上面说了 技术机中有很多本字典;为了让电脑显示中文,我国家又发布了 GB2312-80 标准、GBK 编码标准、GB18030编码标准等;如果每个拥有自己独特字符的国家都去发布一份自己的标准,就会出现一种问题,比如我们国家电脑程序使用的是GBK进行编码的,那么程序和数据到别的国家的电脑上运行时就会出现乱码的问题,因为两台电脑使用的不是同一个编码标准。

为了解决上述问题,有一个国际组织提出创建 Unicode 编码标准(统一码、万国码、单一码),由世界各国合作把自己国家的字符都添加到这个编码标准中,这个标准有 1114112 码位,可以容纳全世界所有国家的字符,最多使用4个字节进行存储,前面部分兼容 ASCII 标准中的字符,然后再加上各国自己的字符;另外 Unicode 标准中使用 UTF-8 格式去编码时,中文是使用 3 个字节的,而我们的 Golang 是默认使用 UTF-8 格式的,我们编程的时候,如果我们的程序仅仅只用到中文、中文符号、英文、英文符号(使用的都是中国人),那么使用GBK进行编码也是可以的,因为GBK标准中,汉字字符的编码是使用 2 个字符的,比 Unicode UTF-8 要节省内存空间。(我们平常所说的 UTF-8,实际上说的是 使用Unicode编码标准UTF-8转换格式)

我们这里简单提一嘴UTF-8(UCS)格式,这里代表的是以1个byte1个byte地去处理,这样可以对不同范围的字符使用不同长度编码;比如Unicode前面部分 ASCII 字符,用一个字节就可以表示,这个时候我们 UTF-8 格式就能根据字符串度使用一个字节去处理;又比如某个汉字字符需要三个字节才能表示,这个时候 UTF-8 格式就用三个字节去处理;这样可以通俗的理解了吧?以前老版的Unicode只有两种编码格式 UCS2 和 UCS4 就是用2个字节表达或者用4个字节表达,后面使用了 UTF 的处理方式才真正让 Unicode 名副其实的称为万国码。 这里只简单的提一嘴,编码方面详细的知识如果有兴趣的可以自行查阅关于计算机编码方面的资料进行学习,这里就不再详细说了,后续找个时间再专门写一篇编码相关的文章。

言归正传 rune 字符型

我们讲 byte 字符型的时候说他其实就是 uint8类型,只是类型的名称不同,存储的数据都是一样的。
而我们的 rune 字符类型,4个字节、有符号、整型,是不是跟我们的 int32 一样?实际上 rune 等价于 int32 ,只是类型名称不同。rune 存储的是 Unicode 编码标准中的字符编号,Unicode 最大用 4 个字节来表示字符,而 rune 使用的是 4 个字节,意味着 rune 可以存储以及表示出 Unicode 中的所有字符。

// rune
var a rune = 'a'
fmt.Println(a)
/*
输出结果:
97
*/

var b rune = '措'
fmt.Println(b)
/*
输出结果:
25514
*/

所以 rune 能存储 ‘措’ 字符,自然也能存储下占用1个字节的 'a’字符,rune 跟 byte一样不能为空,单引号内一定要有内容。如果我们只是表示 ASCII 编码标准中的字符,直接使用 byte 类型就可以了,没必要使用 rune 类型,该类型是直接使用4个字节的,内存使用的是 byte 的四倍。

说白了, rune 就是一个空间更大容器,可以放得下编号更大字符, ‘措’ 字符的 编号是数字 25514,而我们的 rune 的空间是4个字节32个bit,存储数字 25514 绰绰有余。

有byte的铺垫,rune应该很容易看到,到此我们对 rune 做过一个小总结:

  • rune 4个字节 32bit、对应 int32 类型
  • Unicode 万国码、统一码、最大使用4个字节表示一个字符、有多种编码格式(UTF-8、UTF-16、UTF-32)
  • UTF-8 golang默认使用的编码方式、UTF-8格式使用3个字节表示1个汉字字符

字符串 string

字符串顾明思意,就是多个字符串联起来,使用双引号表示。

// string 类型
var a string = "a措"
fmt.Println(a)
fmt.Println("长度:", len(a))
/**
输出结果
a措
长度:4
**/

打印出内容 a措 的时候,是不是还能理解,当使打印出 长度:4 的时候是不是就有点懵了?有 byte 和 rune 的铺垫,我们再讲一下 string 字符串就能很简单的理解了。

我们上面篇章说过 Go 语言默认使用 Unicode UTF-8 编码的,UTF-8 格式 对不同范围的字符使用不同长度编码,变量 a 字符串中有两个字符 ‘a’ 和 ‘措’,字符 ‘a’ 所在的范围用1个字节就可以存储了;上面也说过 汉字字符,在使用 UTF-8 格式时,一个汉字字符使用3个字节表示。我们把这两个字符看做乒乓球,‘a’ 乒乓球的大小是1byte, ‘措’ 乒乓球的大小是3个byte,而我们的双引号就是一个凹槽容器,这时我们往凹槽容器依次放入乒乓球 ‘a’,再放入乒乓球 ‘措’ ,我们代码中获取长度的 len() 函数,其实是获取变量的 字节数量,那么我们的 ‘a’ 字符1byte,而 ‘措’ 字符3byte,所以打印的结果是 4。这能明白了吧,字符串之所以叫字符串,就是把字符串联起来。

而我们比喻的这个 凹槽容器,其实他也是一种类型,我们叫他 数组,但这是下一篇的内容,我们把它看做是一个容器就可以了。

“a措”[ ‘a’, ‘措’ ][ b1, b2, b3, b4 ]

  • “a措” 编码后我们所看到的字符串内容
  • [ ‘a’, ‘措’ ] 我们所理解的 凹槽中存放乒乓球 的样子
  • [ b1, b2, b3, b4 ] string类型变量 a 中实际的值,其中 b1 是存储 ‘a’ 字符编号的字节, b2 b3 b4 是用来存储 ‘措’ 字符所用的字节;这个 [ ] 中括号就是我们说的 数组,中括号内的 b1 b2 b3 b4 就是数组中的元素(值),数组中有4个byte字节类型的元素,那么他的大小就是 4,所以 len() 函数获取的是这个数组元素的数量,而我们数组中元素的类型都是字节型,所以我们称他为 字节型数组,也就是 byte[];这里就先这样简单的了解一下数组,下一章我们再详细的去说。

上面代码打印出 a 变量的长度是 4,这个我们已经可以理解了,但是我们该怎么用代码输出 字符 的数量呢?

a := "a措"

// 使用 utf8 包的函数
b := utf8.RuneCountInString(a)
fmt.Println(a)
/*
输出结果:
2
*/

// 把字符串转换成 rune类型的数组
c := []rune(a)
fmt.Println(len(c))
/*
输出结果:
2
*/
  • utf8.RuneCountInString(a) 函数使用 utf-8 的规则统计出 a 变量的字符数量,把统计出的数量 2,赋值给 b 变量,然后打印出来 输出结果:2
  • b := [] rune(a) 把 a字符串 转换成 rune类型数组,这个rune类型的数组可以理解成数组的内容只存储 rune字符,然后再赋值给 c 变量,所以此时的 c变量 [ ‘a’, ‘措’ ]
  • len(b) 函数获取数组中的元素数量,这是一个 rune字符类型的数组,里面有两个元素,所以结果是 2,打印出来 输出结果:2

我们字符串中所看到的内容是经过编码后显示出来的内容,实际上字符串的实际值就是一个 byte[] 数组,把每个字符通过 UTF-8 格式编码得到字节放到这个数组中,然后再把这数组的内容存储到内存中;反之,我们拿到这个数组的内容,再通过 UTF-8 编码又能再得到对应的字符,然后再把字符组成成字符串显示出来。

至此我们的字符串部分也讲完了,我们做一个小总结:

  • 字符串 多个字符串联在一起
  • 字符串的实际值是 byte[] 数组,当字符串为空时是允许的(a := “”),因为实际值是[ ],表示一个没有元素的数组,所以不会报错
  • Unicode UTF-8 格式把得到的字符转换成编号,并存储在字节中,然后再把字节存入数组中,这样 byte[] 就能跟 string 字符串通过使用 UTF-8 格式相互转换了。

至此,本章内容 byte字符、rune字符、string字符串,三种类型都讲解完成。
(java中的字符和字符串本质上也是一样的,不过java9之前String使用char[]数组去存储字符串,java9之后才使用了byte[]数组存储字符串,java中的char是固定的两个字节表示不了4个字节的字符,这点和go有一定的差别,有兴趣自行了解)

bool 布尔类型

这个是最简单的一种数据类型,该类型只有固定的两个值,就是关键字 truefalse ,默认是 false。

// 布尔变量
t := true
f := false
fmt.Println(t)
fmt.Println(f)
/*
输出结果:
true
false
*/

// 创建 a变量 赋值0
var a int32 = 0

// 条件不成立时,t=false
t = a == 1
// 条件成立时,f=true
f = a == 0
fmt.Println(t)
fmt.Println(f)
/*
输出结果:
false
true
*/

a == 1 两个等于号,是对比的意思,就是对比左右两边的值是否相等; a = 1 一个等于号,是赋值,之前说过; ! 感叹号,就是取反的意思, 0 != 1 这个条件的意思就是 0 不等于 1,所以这个条件是成立的,所以是 true;这里只简单提一嘴,后面会详细讲述这些符号,这里先知道怎么使用的就可以了。

bool类型的值是固定的 true 和 false,我们赋值的条件如果成立值为 true(1 == 1),如果条件不成立值为 false(0 != 0)。

进入我们的类型讲解环节,go 语言中的 bool 布尔类型使用1个字节存储,用 0 表示 false,用 1 表示 true,所以在内存中 false(0000 0000)、true(0000 0001),但我们的go语言做了特殊处理,虽然他们的二进制数对应我们的十进制数就是 0 和 1,而我们的 bool 类型只做了 true 和 false 的转换,并且限制了我们使用 整数 对比 bool 类型( t == 0 会报错)。所以我们的 bool 类型和其他语言是有差别的,不能用数值与 bool直接对比( java语言中 true == 1 是允许的)。

bool 类型:

  • 存储 使用 1 byte ,存储固定值 0 和 1
  • 值 固定值 true(1) 和 false(0),虽然 bool 类型存储的实际值是 0 和 1 ,但不能使用数值与 bool 类型比对

到这里我们的基本数据类型已经全部讲完了,下一章开始内容 数组。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值