原创文章,若需转载请注明出处!
欢迎扫码关注公众号「Golang来了」或者移步 www.seekload.net,查看更多精彩文章。

Go 中的字符串值得特别关注,与其他语言相比,Go 中的字符串实现方式有所不同。

字符串

在Go中,使用双引号 "" 声明字符串:

s := "Hello world"
fmt.Println("len(s):",len(s))
fmt.Println(s);

输出:

len(s): 11
Hello world

上面的代码声明了字符串 slen 函数返回字符串 s 的字节数(包括空格)。在 Go 中,字符串其实是只读的字节切片

s := "Hello world"
for i:=0;i<len(s);i++ {fmt.Print(s[i]," ")
}

你觉得上面的代码会输出什么,是一个个字母吗?其实不是:

72 101 108 108 111 32 119 111 114 108 100

输出的是每个字母在 ASCII 码表上对应的十进制数字。
正如大家熟知的,Go 语言采用 UTF-8 编码,这种编码方式与 ASCII 编码兼容,只不过 ASCII 编码只需 1 个字节,而 UTF-8 需要 1-4 个字节表示一个符号。

s := "Hello world"
for i:=0;i<len(s) ;i++  {fmt.Printf("%c ",s[i])
}
fmt.Println("")
for i:=0;i<len(s) ;i++  {fmt.Printf("%v ",s[i])
}
fmt.Println("")
for i:=0;i<len(s) ;i++  {fmt.Printf("%x ",s[i])
}
fmt.Println("")
for i:=0;i<len(s) ;i++  {fmt.Printf("%T ",s[i])
}

输出:

H e l l o   w o r l d
72 101 108 108 111 32 119 111 114 108 100
48 65 6c 6c 6f 20 77 6f 72 6c 64
uint8 uint8 uint8 uint8 uint8 uint8 uint8 uint8 uint8 uint8 uint8

上面的代码,%v 格式化输出字节对应的十进制值;%x 以十六进制输出;%T 格式化输出值的类型。从结果可以看出,值的类型都是 uint8byte 类型,typeuint8 的别名,byte 类型在我的文章中有所介绍。
我们来看下:一个字符串中包含非 ASCII 码的字符 会是怎样的情况。

s := "Hellõ World"
fmt.Println("len(s):", len(s))
for i := 0; i < len(s); i++ {fmt.Printf("%c ", s[i])
}
fmt.Println("")
for i := 0; i < len(s); i++ {fmt.Printf("%v ", s[i])
}
fmt.Println("")
for i := 0; i < len(s); i++ {fmt.Printf("%x ", s[i])
}

输出:

len(s): 12
H e l l à µ   W o r l d
72 101 108 108 195 181 32 87 111 114 108 100
48 65 6c 6c c3 b5 20 57 6f 72 6c 64

上面的例子中,将 o 替换成 õ 。从结果可以看出,字符串的字节长度是 12 ,说明 õ 占用两个字节。然而 õ 的输出变成了 Ã µõ
Unicode 码点是 U+00F5 ,其 UTF-8 编码 占两个字节 c3b5for 循环按字节读取,c3 (十进制 195 )对应字符 Ãb5 (十进制 181 )对应字符 µ (详见这里)。
熟悉 ASCIIUTF-8Unicode 更有利于理解这些知识,关于这些知识,不会在本文展开细讲,有兴趣的可以参考这里。
UTF-8 编码中,一个码点占用至少一字节,如果我们还是以一个码点占用一个字节去打印字符肯定会出问题,就像上面的例子一样。那有没有办法解决这个问题,好在 Go 为我们提供了 rune

Rune

rune 是 Go 的内置数据类型,是 int32 的别名,表示 Go 中的 Unicode 代码点。用 rune 数据类型,开发人员就不必关心代码点占用几个字节了。

s := "Hellõ World"
r := []rune(s)fmt.Println("len(r):", len(r))
for i := 0; i < len(r); i++ {fmt.Printf("%c ", r[i])
}
fmt.Println("")
for i := 0; i < len(r); i++ {fmt.Printf("%v ", r[i])
}
fmt.Println("")
for i := 0; i < len(r); i++ {fmt.Printf("%x ", r[i])
}
fmt.Println("")
for i := 0; i < len(r); i++ {fmt.Printf("%T ", r[i])
}

输出:

len(r): 11
H e l l õ   W o r l d
72 101 108 108 245 32 87 111 114 108 100
48 65 6c 6c f5 20 57 6f 72 6c 64
int32 int32 int32 int32 int32 int32 int32 int32 int32 int32 int32

上面的代码,字符串 s 通过类型转化成了 rune 切片。 õ
Unicode 码点就是 U+00F5 ,对应十进制的 245 ,参考这。切片 r 的长度就是:11 ;输出的 int32 ,印证了 runeint32 的别名。

for range 字符串

上面的例子已经很好解决了之前遇到的问题,有种更好的方式 – range string 。使用 range 循环一个字符串,将返回 rune 类型的字符和字节索引。

s := "HellõWorld"
for index, char := range s {fmt.Printf("%c starts at byte index %d \n", char,index)
}

输出:

H starts at byte index 0
e starts at byte index 1
l starts at byte index 2
l starts at byte index 3
õ starts at byte index 4
W starts at byte index 6
o starts at byte index 7
r starts at byte index 8
l starts at byte index 9
d starts at byte index 10

从输出结果可以看出,õ 占用了两个字节:索引 4 和 5。

文章读到这,可能会有个疑问,怎么获取字符串的长度呢?

Length of the string

可以使用 RuneCountInString() 函数,原型是这样的:

func RuneCountInString(s string) (n int)

返回字符串中 rune 字符的个数。

s := "Hellõ 中国"
length := utf8.RuneCountInString(s)
fmt.Println(length)

输出:8

字符串是不可变的

前面我们已经说过,字符串是只读的字节切片,一旦创建,是不可更改的。如果强制修改,就会报错:

s := "Hello World"
s[0] = "h"

报错:cannot assign to s[0]

这篇文章有几个比较重要的点:

  1. 字符串是只读的字节切片;
  2. rune 表示 Go 中的 Unicode 代码点;
  3. Go 采用 UTF-8 编码,这种编码方式是 Unicode 的实现方式之一;
  4. 熟悉 ASCIIUTF-8Unicode ,可以参考 这,更有利于理解这篇文章;

希望这篇文章能够解决你对 Go string 的一些疑问,有不懂的可以留言讨论!

(全文完)

扫描上方二维码,欢迎关注公众号「Golang来了」,获取最新精彩文章!

Strings、bytes and runes -- 就要学习Go语言相关推荐

  1. Strings、bytes and runes -- 就要学习 Go 语言

    Go 中的字符串值得特别关注,与其他语言相比,Go 中的字符串实现方式有所不同. 字符串 在Go中,使用双引号 "" 声明字符串: s := "Hello world&q ...

  2. Strings, bytes, runes and characters in Go

    本文翻译自golang官方 ,英文文章原地址 https://blog.golang.org/strings    ,主要介绍了 go中的 strings .bytes. runes .charact ...

  3. 学习Go语言必备案例 (6)

    51.数字解析 从字符串中解析数字在很多程序中是一个基础常见的任务,在Go 中是这样处理的. package mainimport ("fmt""strconv" ...

  4. HDL4SE:软件工程师学习Verilog语言(十四)

    14 RISC-V CPU初探 前面我们介绍了verilog语言的基本语法特征,并讨论了数字电路设计中常用的状态机和流水线结构,然后我们借鉴SystemC的做法,引入了HDL4SE建模语言,以及相应的 ...

  5. 程序员之路──如何学习C语言并精通C语言

    程序员之路──如何学习C语言 学习C语言不是一朝一夕的事情,但也不需要花费十年时间才能精通.如何以最小的代价学习并精通C语言是本文的主题.请注意,即使是"最小的代价",也绝不是什么 ...

  6. go interface 转int_大神是如何学习 Go 语言之反射的实现原理

    反射是 Go 语言比较重要的一个特性之一,虽然在大多数的应用和服务中并不常见,但是很多框架都依赖 Go 语言的反射机制实现一些动态的功能.作为一门静态语言,Golang 在设计上都非常简洁,所以在语法 ...

  7. c语言不定长数组_学习C语言这三块“硬骨头”不搞定学了也是白学

    C语: C语言在嵌入式学习中是必备的知识,审核大部分操作都要围绕C语言进行,而其中有三块"难啃的硬骨头"几乎是公认级别的. 01指针 C语言 指针公认最难理解的概念,也是让很多初学 ...

  8. 干货 | 如何系统学习 C 语言?

    关注.星标公众号,直达精彩内容 C 语言应该是绝大部分同学的编程第一课. 对于非 CS 专业的同学,学 C 语言主要是掌握一些基本的编程方法,C 语言只是媒介. 但是对于 CS 科班的同学,C 语言是 ...

  9. 第六篇:如何学习C语言?

    现在很多人初学者直接选择C语言的人已经变得越来越少了,主要原因还是在招聘岗位数量上无法和java,php等高级语言想媲美,但并不代表C语言已经穷途末路没有前景了,C语言的角色从前台变成了后台服务,在一 ...

最新文章

  1. Ubuntu系统执行shell 脚本的方法
  2. 用于RGB-D显著目标检测的自监督表示学习
  3. K8s 1.14 发布了,Release Note 该怎么读?
  4. 对象的继承关系在数据库中的实现方式和PowerDesigner设计
  5. 如何解决push commit conflict
  6. 软件构造学习笔记-第十一周
  7. 谈一谈自己对依赖、关联、聚合和组合之间区别的理解
  8. 前端学习(1483):在vue发送网络请求
  9. CSS3质感分析——表面线性渐变
  10. python查找指定文件夹并重命名_python获取指定文件夹下的所有文件名,并删选指定类型文件进行重命名以及撤销重命名...
  11. 引用和指针自增的不同
  12. 私有api调用审核失败 prefs:root
  13. Operation和OperationQueue实战:异步下载图片并给图片加滤镜
  14. 《程序设计导引及在线实践》学习
  15. 二阶有源低通滤波器幅频特性
  16. 公众号排名优化技巧分享
  17. 单片机课设波形发生器 产生方波、三角波、正弦波、锯齿波 波形幅度可调、频率可调
  18. 删除后别人的微信号变成wxid_如何找回已删除的微信好友?卓师兄神助攻
  19. 生命的质量,在于你拥有多少内在的和平与喜悦
  20. author-头文字注释

热门文章

  1. 【NOIP模拟】我的天
  2. 首次!TinyML低功耗边缘侧机器学习技术论坛-亚洲分会来到中国!
  3. Android 实现涂鸦笔效果
  4. excel处理几十万行数据_EXCEL的重生!处理百万行数据竟如此简单
  5. 超好的数据结构算法可视化网站
  6. linux 目录 问号 原因,linux – 在目录的ls中显示的问号. IO错误也是如此
  7. ‘class QFontMetrics‘ has no member named ‘horizontalAdvance‘
  8. LDPC译码:和积译码算法(SPA)、最小和算法(MSA)、分层译码算法(LBP)、动态信息更新策略IDS(含RBP、NW-RBP、SVNF-RBP)的MATLAB实现
  9. python五角星符号怎么打出来_如何使用python输出连续星号?
  10. 算法导论(三)--分治法