Go字符串初探

周小路 · · 559 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

这篇博文的知识,主要是阅读了Go的官方博客在2013年发表的一篇,名为《Strings, bytes, runes and characters in Go》的文章,里面解释了Go语言中的stringbyte以及rune类型以及Go中的编码方式等内容。

相关概念的辨析

  1. 字符串、字符、字节、位
    1. bitbit是计算机中最小的存储单位,一个bit表示一个二进制位,存储01
    2. 字节byte:一个byte由8个bit组成。在Go中,byte也是一种类型,其底层实际上是一种uint8类型的别名,主要是为了区分字节类型和uint8类型,可以指代一个ASCII的字符。
    3. 字符:字符表示一个可以正常显示的一个符号,譬如一个字符串abc,其中abc都是字符,在Go中,一个字符对应一个rune类型值。
    4. 字符串stringGo中的字符串,实际上是只读的字节切片。
  2. Unicode码点:实际上,字符的概念非常难以定义,在Unicode标准中,使用码点来代指,一个Unicode表示的个体。其表示是码点,其值是一串数字。
  3. rune类型:其是Go中,用以表示一个字符的类型,是int32类型的别名,为了区别表示字符类型以及int32类型。

字符串是字节的切片

我们都知道,在Go当中,字符串是字节的切片。跟JavaScipt有非常大的不同,主要体现在下面这个例子当中:

func test() {
  s := "Hello,世界"
  fmt.Println(len(s))
}
复制代码

这个函数的输出是12,按照直觉来讲,输出应该是8才对。但是,正因为字符串是字节的切片,故取字符串Hello,世界,实际上是在取其对应的字节切片的长度。而在Go中是采用utf-8编码,一个汉字对应着3个字节,因此才得到了12这个数字。

为了验证这个说法,我们取其对应字节的输出:

func test() {
  s := "Hello,世界"
  for i := 0; i < len(s); i++ {
    fmt.Printf("% x", s[i])
  }
}
复制代码

输出为,12个字节:

48 65 6c 6c 6f 2c e4 b8 96 e7 95 8c
复制代码

Unicodeutf-8

我们知道,Go中,都是采用utf-8来对Unicode进行编码的,这种编码方式有着动态大小的特点,借用Go语言圣经中的一张图来解释:

https://user-gold-cdn.xitu.io/2020/4/30/171cbcbc0e0a0705?w=1290&h=220&f=jpeg&s=46730

表示不同范围Unicode值,编码采用了不同个数的字节,我们常用的汉字,就一般是使用3个字节去表示的。

https://user-gold-cdn.xitu.io/2020/4/30/171cbcbc0e5f5eb6?w=642&h=1038&f=jpeg&s=29540

上面这个例子展示了字符,是如何编码和解码的。其用Unicode表示,为2个字节,编码为utf-8则为3个字节。

rune类型

如果我们始终用若干字节去表示一个字符,那未免也太过繁琐。同时,我们上面知道取一个字符串长度,取得的是这个字符串对应字节的长度,那么我们用传统的for循环,取去的字符串中的每个字符,其结果是绝对不符合我们的期望的。

Go就专门为字符定义了字符类型rune。它是int32类型的别名,主要就是用来处理Unicode字符的。之所以,其对应的是int32类型,这主要是因为utf-8编码,最大会产生4个字节的大小的值,故对应了int32类型。

Go中想要按照正常的方式,遍历一个字符串,可以采用使用for range的形式,来对一字符串遍历:

func test() {
  s := "Hello,世界"
  for _, val := range s {
    fmt.Printf("%#U\n", val)
  }
}
复制代码

使用for range循环,实际上就是在字符串中对字符rune进行遍历。输出为:

U+0048 'H'
U+0065 'e'
U+006C 'l'
U+006C 'l'
U+006F 'o'
U+002C ','
U+4E16 '世'
U+754C '界'
复制代码

其次,也可以使用标准库对字符串进行处理,如使用unicode/utf8库中的DecodeRuneInString方法,输入一个字节切片,返回一个rune值和其使用utf-8编码的字节宽度。

为什么要编码

实际上,现在Unicode已经收录了,超过12万个字符,我们如果直接使用一个int32的序列来存储所有的字符就已经足够使用,为什么还要采用utf-8这种编码模式?

主要是因为,如果统一使用固定长度bit来存储所有的字符,会导致存储空间的极大浪费。我们在日常的使用中,大部分时候,都是使用到了ASCII可以表示的一些字符,只有少数情况下,使用的字符会超出ASCII的表示范围。因此,我们需要使用utf-8这种变长的编码工具,来为我们节约空间。

参考

  1. Strings,bytes,runes and characters in Go
  2. Go语言程序设计

有疑问加站长微信联系(非本文作者)

本文来自:掘金

感谢作者:周小路

查看原文:Go字符串初探

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

559 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传