深入理解 Golang 的字符串


Posted in Golang onMay 04, 2022

字符串的本质

在编程语言中,字符串发挥着重要的角色。字符串背后的数据结构一般有两种类型:

  • 一种在编译时指定长度,不能修改
  • 一种具有动态的长度,可以修改。

比如:与Python 中的字符串一样,Go 语言中的字符串不能被修改,只能被访问。
在 Python 中,如果改变一个字符串的值会得到如下结果:

>>> hi = "Hello"
>>> hi
'Hello'
>>> hi[0] = 'h'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>>

同理,在 Go 中也一样:

package main
import "fmt"
func main() {
var hello = "Hello"
hello[1] = 'h'
fmt.Println(hello)
}
// # command-line-arguments
// string_in_go/main.go:8:11: cannot assign to hello[1] (strings are immutable)

字符串的终止方式有两种:

  • 一种是 C 语言的隐式声明,以字符 “\0” 作为终止符
  • 一种是 Go 语言的显式声明

Go 语言的 string 的表示结构如下:

type StringHeader struct {
Data uintptr // Data 指向底层的字符数组
Len int // Len 用来表示字符串的长度
}

字符串的本质上是一串字符数组,每个字符都在存储时对应了一个或多个整数。用这些整数来表示字符,比如打印 hello 的字节数组如下:

package main
import "fmt"
func main() {
var hello = "Hello"
for i := 0; i < len(hello); i++ {
fmt.Printf("%x ", hello[i])
}
}
// Output: 48 65 6c 6c 6f

字符串的底层原理

字符串有特殊标识,有两种声明方式:

var s1 string = `hello world`
var s2 string = "hello world"

字符串常量在词法解析阶段最终会被标记为 StringLit 类型的 Token 并被传递到编译的下一个阶段。
在语法分析阶段,采取递归下降的方式读取 UTF-8 字符,单撇号或双引号是字符串的标识。

分析的逻辑位于  syntax/scanner.go 文件中:

func (s *scanner) stdString() {
ok := true
s.nextch()
for {
if s.ch == '"' {
s.nextch()
break
}
if s.ch == '\\' {
s.nextch()
if !s.escape('"') {
ok = false
}
continue
}
if s.ch == '\n' {
s.errorf("newline in string")
ok = false
break
}
if s.ch < 0 {
s.errorAtf(0, "string not terminated")
ok = false
break
}
s.nextch()
}
s.setLit(StringLit, ok)
}
func (s *scanner) rawString() {
ok := true
s.nextch()
for {
if s.ch == '`' {
s.nextch()
break
}
if s.ch < 0 {
s.errorAtf(0, "string not terminated")
ok = false
break
}
s.nextch()
}
// We leave CRs in the string since they are part of the
// literal (even though they are not part of the literal
// value).
s.setLit(StringLit, ok)
}

从上面的代码可以看到,Go 中有两种字符串的检查:一种是标准字符串以双引号定义 "",如 "Hello,World", 还有一种是原始字符串,用 \\ 定义的, 因此针对两种字符串有两种语法分析函数:

  • 如果是单撇号,则调用 rawString 函数
  • 如果是双引号,则调用 stdString 函数
Golang 相关文章推荐
golang中的空slice案例
Apr 27 Golang
Golang Gob编码(gob包的使用详解)
May 07 Golang
Golang全局变量加锁的问题解决
May 08 Golang
入门学习Go的基本语法
Jul 07 Golang
Golang表示枚举类型的详细讲解
Sep 04 Golang
Go 中的空白标识符下划线
Mar 25 Golang
简单聊聊Golang中defer预计算参数
Mar 25 Golang
Go gRPC进阶教程gRPC转换HTTP
Jun 16 Golang
GoFrame框架数据校验之校验结果Error接口对象
Jun 21 Golang
Go语言编译原理之变量捕获
Aug 05 Golang
Go语言编译原理之源码调试
Aug 05 Golang
Golang入门之计时器
May 04 #Golang
Golang 入门 之url 包
May 04 #Golang
Golang解析JSON对象
Apr 30 #Golang
Golang 并发编程 SingleFlight模式
Golang 实现 WebSockets 之创建 WebSockets
Apr 24 #Golang
Golang 实现WebSockets
Golang ort 中的sortInts 方法
Apr 24 #Golang
You might like
php中关于普通表单多文件上传的处理方法
2011/03/25 PHP
PHP+jQuery 注册模块开发详解
2014/10/14 PHP
PHP如何实现Unicode和Utf-8编码相互转换
2015/07/29 PHP
document.all与WEB标准
2020/05/13 Javascript
用js计算页面执行时间的函数
2006/12/07 Javascript
javascript实现的一个随机点名功能
2014/08/26 Javascript
node.js中的console.time方法使用说明
2014/12/09 Javascript
JavaScript基于ajax编辑信息用法实例
2015/07/15 Javascript
Jquery实现瀑布流布局(备有详细注释)
2015/07/31 Javascript
JavaScript操作URL的相关内容集锦
2015/10/29 Javascript
jQuery添加和删除输入文本框标签代码
2016/05/20 Javascript
bootstrap多种样式进度条展示
2016/12/20 Javascript
jQuery实现三级联动效果
2017/03/02 Javascript
JS表格组件神器bootstrap table使用指南详解
2017/04/12 Javascript
Vue实现点击后文字变色切换方法
2018/02/11 Javascript
Angular搜索场景中使用rxjs的操作符处理思路
2018/05/30 Javascript
opencv 识别微信登录验证滑动块位置
2018/08/07 Javascript
angularJS自定义directive之带参方法传递详解
2018/10/09 Javascript
微信小程序实现按字母排列选择城市功能
2019/11/25 Javascript
js实现简单五子棋游戏
2020/05/28 Javascript
bootstrap-table后端分页功能完整实例
2020/06/01 Javascript
初学Python实用技巧两则
2014/08/29 Python
使用wxPython获取系统剪贴板中的数据的教程
2015/05/06 Python
浅析Python四种数据类型
2018/09/26 Python
wxpython+pymysql实现用户登陆功能
2019/11/19 Python
HTML5播放实现rtmp流直播
2020/06/16 HTML / CSS
日本无添加化妆品:HABA
2016/08/18 全球购物
大学生求职简历的自我评价范文
2013/10/12 职场文书
中学生操行评语大全
2014/04/24 职场文书
《春雨》教学反思
2014/04/24 职场文书
学习保证书范文
2014/04/30 职场文书
公安交警个人对照检查材料思想汇报
2014/10/01 职场文书
2014年财务工作总结与计划
2014/12/08 职场文书
护理工作个人总结
2015/03/03 职场文书
创业计划之特色精品店
2019/08/12 职场文书
python如何利用cv2.rectangle()绘制矩形框
2022/12/24 Python