Golang 入门 : 字符串及底层字符类型
字符串
基本使用
在 Go 语言中,字符串是一种基本类型,默认是通过 UTF-8 编码的字符序列,当字符为 ASCII 码时则占用 1 个字节,其它字符根据需要占用 2-4 个字节,比如中文编码通常需要 3 个字节。
声明和初始化
字符串的声明和初始化非常简单,举例如下:
var str string // 声明字符串变量
str = "Hello World" // 变量初始化
str2 := "你好呀" // 也可以同时进行声明和初始化
格式化输出
还可以通过 Go 语言内置的 len() 函数获取指定字符串的长度,以及通过 fmt 包提供的 Printf 进行字符串格式化输出:
fmt.Printf("The length of \"%s\" is %d \n", str, len(str))
fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)
转义字符
Go 语言的字符串不支持单引号,只能通过双引号定义字符串字面值,如果要对特定字符进行转义,可以通过 \ 实现,就像我们上面在字符串中转义双引号和换行符那样,常见的需要转义的字符如下所示:
\n:换行符\r:回车符\t:tab 键\u或 \U :Unicode 字符\\:反斜杠自身
所以,上述打印代码输出结果为:
The length of "Hello world" is 11
The first character of "Hello world" is H.
除此之外,你可以通过如下方式在字符串中包含 ":
label := `Search results for "Golang":`
多行字符串
对于多行字符串,也可以通过 ` 构建:
results := `Search results for "Golang":
- Go
- Golang
Golang Programming
`
fmt.Printf("%s", results)
打印结果如下:
Search results for "Golang":
- Go
- Golang
- Golang Programming
当然,使用 + 连接符也是可以的:
results := "Search results for \"Golang\":\n" +
"- Go\n" +
"- Golang\n" +
"- Golang Programming\n"
fmt.Printf("%s", results)
打印结果是一样的,但是要多输入不少字符,也不如上一种实现优雅。
不可变值类型
虽然可以通过数组下标方式访问字符串中的字符:
ch := str[0] // 取字符串的第一个字符
但是和数组不同,在 Go 语言中,字符串是一种不可变值类型,一旦初始化之后,它的内容不能被修改,比如看下面这个例子:
str := "Hello world"
str[0] = 'X' // 编译错误
编译器会报类似如下的错误:
cannot assign to str[0]
字符编码
Go 语言中字符串默认是 UTF-8 编码的 Unicode 字符序列,所以可以包含非 ANSI 字符,比如「Hello, 世界」可以出现在 Go 代码中。
但需要注意的是,如果你的 Go 代码需要包含非 ANSI 字符,保存源文件时请注意编码格式必须选择 UTF-8。特别是在 Windows 下一般编辑器都默认保存为本地编码,比如中国地区可能是 GBK 编码而不是 UTF-8,如果没注意到这点在编译和运行时就会出现一些意料之外的情况。
字符串的编码转换是处理文本文档(比如 TXT、XML、HTML 等)时非常常见的需求,不过 Go 语言默认仅支持 UTF-8 和 Unicode 编码,对于其他编码,Go 语言标准库并没有内置的编码转换支持。所幸的是我们可以很容易基于 iconv 库包装一个,这里有一个开源项目可供参考:https://github.com/qiniu/iconv
字符串操作
字符串连接
Go 内置提供了丰富的字符串函数,常见的操作包含连接、获取长度和指定字符,获取长度和指定字符前面已经介绍过,字符串连接只需要通过 + 连接符即可:
str = str + ", 世界"
str += ", 世界" // 上述语句也可以简写为这样,效果完全一样
另外,还有一点需要注意的是如果字符串长度较长,需要换行,则 + 连接符必须出现在上一行的末尾,否则会报错:
str = str +
", 世界"
字符串切片
在 Go 语言中,可以通过字符串切片实现获取子串的功能:
str := "hello, world"
str1 := str[:5] // 获取索引5(不含)之前的子串
str2 := str[7:] // 获取索引7(含)之后的子串
str3 := str[0:5] // 获取从索引0(含)到索引5(不含)之间的子串
fmt.Println("str1:", str1)
fmt.Println("str2:", str2)
fmt.Println("str3:", str3)
Go 切片区间可以对比数学中的区间概念来理解,它是一个左闭右开的区间,比如上述 str[0:5] 对应到字符串元素的区间是 [0,5),str[:5] 对应的区间是 [0,5)(数组索引从 0 开始),str[7:] 对应的区间是 [7:len(str)](这是闭区间,是个例外,因为没有指定区间结尾)。
所以,上述代码打印结果如下:
str1: hello
str2: world
str3: hello
综上所述,字符串切片通过 : 连接的起始点和结束点索引对字符串进行切片,冒号之前的数字代表起始点,为空表示从 0 开始,之后的数字代表结束点,为空表示到字符串最后,而不是子串的长度。所以 str[:] 会打印出完整的字符串来。
此外 Go 字符串也支持字符串比较、是否包含指定字符/子串、获取指定子串索引位置、字符串替换、大小写转换、trim 等操作,更多操作 API,请参考标准库 strings 包,这里就不一一展示了。
字符串遍历
Go 语言支持两种方式遍历字符串。
一种是以字节数组的方式遍历:
str := "Hello, 世界"
n := len(str)
for i := 0; i < n; i++ {
ch := str[i] // 依据下标取字符串中的字符,ch 类型为 byte
fmt.Println(i, ch)
}
这个例子的输出结果为:
0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 228
8 184
9 150
10 231
11 149
12 140
可以看出,这个字符串长度为 13,尽管从直观上来说,这个字符串应该只有 9 个字符。这是因为每个中文字符在 UTF-8 中占 3 个字节,而不是 1 个字节。
另一种是以 Unicode 字符遍历:
str := "Hello, 世界"
for i, ch := range str {
fmt.Println(i, ch) // ch 的类型为 rune
}
输出结果为:
0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 19990
10 30028
这个时候,打印的就是 9 个字符了,因为以 Unicode 字符方式遍历时,每个字符的类型是 rune,而不是 byte。
看到这里可能你有点懵,会好奇 Go 底层到底是如何存储字符串的,为什么不同遍历方式获取的结果不同呢?下面就来给大家简单掰扯掰扯。
底层字符类型
Go 语言对字符串中的单个字符进行了单独的类型支持,在 Go 语言中支持两种字符类型:
- 一种是
byte,代表UTF-8编码中单个字节的值(它也是uint8类型的别名,两者是等价的,因为正好占据 1 个字节的内存空间),它可用于区分字节值和8位无符号整数值。; - 另一种是
rune,代表单个Unicode字符(它也是uint32类型的别名,因为正好占据 4 个字节的内存空间。关于rune相关的操作,可查阅 Go 标准库的unicode包),它可用于区分字符值和整数值。。
rune 、 byte 和 string 都是 Go 的内置类型。string 是所有8位字节字符串的集合,通常但不一定代表UTF-8编码的文本,字符串可能为空,但是不能为 nil,字符串类型的值是不可变的。
由上面得解释我们大概可以明白,rune 可以表示得比 byte 多,string 类型的底层是一个byte 数组
字节和字符
刚刚上面标注了字节和字符,现在我们来梳理字符和字节的概念
存储单位 字节
- 计算机存储信息的最小单位,称之为位 bit,二进制的一个0或1叫一位
- 计算机存储容量基本单位是字节 Byte,8个二进制位组成 1 个字节
信息表示单位 字符
- 字符是一种符号,像 英文a和中文阿 就是不同字符
- 不同的字符在不同的编码格式下,所需要的存储单位不一样
- ASCLII 编码中一个英文字母一字节,一个汉字两字节
- UTF-8 编码中 一个英文字母一字节,一个常见汉字3字节,不常用的超大字符集汉字4字节
UTF-8 和 Unicode 的区别
说到这里,我们需要区分 UTF-8 和 Unicode 的区别。
Unicode 是一种字符集,囊括了目前世界上所有语言的所有字符,与之类似的术语还有 ASCII 字符集(仅包含 256 个字符)、ISO 8859-1 字符集等(包含所有西方拉丁字母),广义的 Unicode 既包含了字符集,也包含了编码规则,比如 UTF-8、UTF-16、UTF8MB4、GBK 等。
因此 UTF-8 是 Unicode 字符集的实现方式之一,它会将 Unicode 字符以某种方式进行编码。在具体实现时,UTF-8 是一种变长的编码规则,从 1~4 个字节不等,比如英文字符是 1 个字节,中文字符是 3 个字节。通过 UTF-8 编码的 Unicode 字符以最大长度 4 个字节作为单个字符固定占据的内存空间,在 Go 语言中可以通过 unicode/utf8 包进行 UTF-8 和 Unicode 之间的转换。
所以如果从 Unicode 字符集的视角看,字符串的每个字符都是一个字符的独立单元,但如果从 UTF-8 编码的视角看,一个字符可能是由多个字节编码而来的。
我们通过 len 函数获取到的是字符串的字节长度,再据此通过字符数组的方式遍历字符串时,是以 UTF-8 编码的角度切入的;而当我们通过 range 关键字遍历字符串时,又是从 Unicode 字符集的角度切入的,如此一来就得到了不同的结果。
出于简化语言的考虑,Go 语言的多数 API 都假设字符串为 UTF-8 编码。
Go 源码文件默认采用Unicode字符集,Unicode码点和内存中字节序列的变换实现使用了UTF-8,这使得Go编程无需考虑编码转换的问题非常方便
从编码上来分析
byte用来强调一个字节代表的数据(例如字符 a 就是 97),而不是数字;byte的操作单位是一个字节,可以理解为一个英文字符rune用来表示Unicode的码点,即一个字符rune的操作单位是一个字符,不管这个字符是什么字符
通俗一点
byte只能操作简单的字符,不支持中文操作rune能操作任何字符
将 Unicode 编码转化为可打印字符
如果你想要将 Unicode 字符编码转化为对应的字符,可以使用 string 函数进行转化::
str := "Hello, 世界"
for i, ch := range str {
fmt.Println(i, string(ch))
}
对应的打印结果如下:
0 H
1 e
2 l
3 l
4 o
5 ,
6
7 世
10 界
UTF-8 编码不能这样转化,英文字符没问题,因为一个英文字符就是一个字节,中文字符则会乱码,因为一个中文字符编码需要三个字节,转化单个字节会出现乱码。
Golang 入门 : 字符串及底层字符类型的更多相关文章
- Golang 入门 : 字符串
在 Golang 中,字符串是一种基本类型,这一点和 C 语言不同.C 语言没有原生的字符串类型,而是使用字符数组来表示字符串,并以字符指针来传递字符串.Golang 中的字符串是一个不可改变的 UT ...
- golang中字符串的底层实现原理和常见功能
1. 字符串的底层实现原理 package main import ( "fmt" "strconv" "unicode/utf8" ) f ...
- golang的字符串拼接
常用拼接方法 字符串拼接在日常开发中是很常见的需求,目前有两种普遍做法: 一种是直接用 += 来拼接 s1 := "Hello" s2 := "World" s ...
- 转 Go语言基本类型 —— 字符类型
https://blog.csdn.net/FHD994603831/article/details/92435724 字符类型Golang中没有专门的字符类型,如果要存储单个字符(字母),一般使用b ...
- 【笨嘴拙舌WINDOWS】字符类型与字符串
"我将用C语言作为工具,开始WINDOWS API的使用" windows NT 从底层开始支持unicode. 1.字符类型 WINDOWS的字符类型在WINNT.H和CTYPE ...
- 2016年11月3日JS脚本简介数据类型: 1.整型:int 2.小数类型: float(单精度) double(双精度) decimal () 3.字符类型: chr 4.字符串类型:sting 5.日期时间:datetime 6.布尔型数据:bool 7.对象类型:object 8.二进制:binary 语言类型: 1.强类型语言:c++ c c# java 2.弱类型语
数据类型: 1.整型:int 2.小数类型: float(单精度) double(双精度) decimal () 3.字符类型: chr 4.字符串类型:sting 5.日期时间:datetime 6 ...
- 字符类型char、字符串与字符数组、字符数组与数据数组区别
字符类型是以ASCII码值运算的:小写字母比相应的大写字母大32,其中A=65,a=97 Esc键 27(十进制).'\x1B'(十六进制).'\33'(八进制) 转义字符:\0 空字符 AS ...
- python中是否有单独的字符类型,通过下标的方式表示字符串中的字符
说明: 在python中,没有单独的字符类型,一个字符呢就是一个大小为1的字符串. 并且可以通过下标的方式,表示字符串中的字符. 操作过程: 1.通过[ ]的方式表示字符串中的第几个字符 >&g ...
- python入门:数字型和字符串换行要同类型 注意连接符
#!/usr/bin/env python # -*- coding: utf-8 -*- #数字型和字符串换行要同类型 注意连接符 a = 1 b = 2 print(str(a) + " ...
- golang的数据类型之字符类型
字符类型使用细节 1)字符常量是用单引号('')括起来的单个字符.例如:var c1 byte = 'a' var c2 int = '中' var c3 byte = '9' 2) Go中允许使用转 ...
随机推荐
- Qt编写跨平台视频监控系统(64通道占用7%CPU/支持win_linux_mac等)
一.前言 视频监控组件经历过数十年的迭代,从最初的只简单播放个rtsp视频流,到现在支持各种音频视频文件格式(mp3.wav.mp4.asf.rm.rmvb.mkv等).支持各种视频流格式(rtp.r ...
- Qt音视频开发28-Onvif信息获取
一.前言 严格意义上来说,Onvif处理这块算不上音视频开发的内容,为何重新整理放在音视频开发这个类别,主要是为了方便统一管理,而且在视频监控处理这块,通过onvif来拿到音视频流这是必经的阶段,也算 ...
- [转]Bundle Adjustment简述
原文链接:https://optsolution.github.io/archives/58892.html或https://blog.csdn.net/optsolution/article/det ...
- 即时通讯技术文集(第30期):IM开发综合技术合集(Part3) [共16篇]
为了更好地分类阅读 52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第30 期. [- 1 -] 全面掌握移动端主流图片格式的特点.性能.调优等 [链接] htt ...
- 基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v8.0版已发布
关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级.高度提炼,一套API优雅支持 UDP .TCP .WebSocket 三种协议,支持 iOS ...
- 阿里IM技术分享(五):闲鱼亿级IM消息系统的及时性优化实践
本文由阿里闲鱼技术团队有攸分享,原题"向消息延迟说bybye:闲鱼消息及时到达方案",有修订和改动,感谢作者的分享. 1.引言 IM消息作为闲鱼用户重要的交易咨询工具,核心目标有两 ...
- WPF 动态加载嵌入主程序的DLL
WPF 动态加载嵌入主程序的DLL,好处是节省文件数量,坏处是启动影响加载速度. 首先将DLL添加进项目,选择添加现有项,设置生成操作为"嵌入资源". 代码: public App ...
- Storm基本概念
storm简介 场景 伴随着信息科技日新月异的发展,信息呈现出爆发式的膨胀,人们获取信息的途径也更加多样.更加便捷,同时对于信息的时效性要求也越来越高.举个搜索场景中的例子,当一个卖家发布了一条宝 ...
- 远程连接Windows
远程桌面连接 限制 1.同网段 (1)服务器关闭防火墙 (2)服务器端 右键点击'我的电脑'进入'属性'点击左侧菜单栏中的'远程设置': 把远程桌面选项设置成'允许运行任意版本远程桌面的计算机连接'. ...
- Ubuntu13 安装vim
问题 由于系统没有vim,只有vi,而vi 编辑文件时比较麻烦,不易操作,还没有关键词高亮显示等,故想安装vim 输入命令: sudo apt install vim 报错,找不到 apt 命令,即没 ...