「Go」byte、rune与string

不住隔壁的王老师 · · 2847 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

不积跬步,无以至千里;不积小流,无以成江海。

码字不易,点赞再看。

byte

占用1个节字,也就是 8 个比特位,所以它和 uint8 类型本质上没有区别,它表示的是 ACSII 表中的一个字符。
如下这段代码,分别定义了 byte 类型和 uint8 类型的变量 a 和 b

import "fmt"

func main() {
    var a byte = 65 
    // 8进制写法: var c byte = '\101'     其中 \ 是固定前缀
    // 16进制写法: var c byte = '\x41'    其中 \x 是固定前缀

    var b uint8 = 66
    fmt.Printf("a 的值: %c \nb 的值: %c", a, b)
    // 或者使用 string 函数
    // fmt.Println("a 的值: ", string(a)," \nb 的值: ", string(b))
}

在 ASCII 表中,由于字母 A 的ASCII 的编号为 65 ,字母 B 的ASCII 编号为 66,所以上面的代码也可以写成这样

import "fmt"

func main() {
    var a byte = 'A'
    var b uint8 = 'B'
    fmt.Printf("a 的值: %c \nb 的值: %c", a, b)
}

两段代码输出的结果都一样

a 的值: A 
b 的值: B
rune

占用4个字节,共32位比特位,所以它和 int32 本质上也没有区别。它表示的是一个 Unicode字符(Unicode是一个可以表示世界范围内的绝大部分字符的编码规范)由于 byte 类型能表示的值是有限,只有 2^8=256 个。当需要处理中文日文或者其他复合字符时,则需要用到rune类型。

import (
    "fmt"
    "unsafe"
)

func main() {
    var a byte = 'A'
    var b rune = 'B'
    fmt.Printf("a 占用 %d 个字节数\nb 占用 %d 个字节数", unsafe.Sizeof(a), unsafe.Sizeof(b))
}

输出:

a 占用 1 个字节数
b 占用 4 个字节数

不管是 byte 还是 rune ,都是使用 单引号,而没使用 双引号

注意:在 Go 中单引号与 双引号并不是等价的
单引号用来表示 字符,在上面的例子里,如果你使用双引号,就意味着你要定义一个字符串,赋值时与前面声明的前面会不一致,这样在编译的时候就会出错。

 cannot use "B" (type untyped string) as type rune in assignment

####### 疑问

byte 和 uint8 没有区别,rune 和 int32 没有区别,那为什么还要多出 byte 和 rune 类型呢?不是多此一举

理由很简单,因为uint8 和 int32 ,直观上让人以为这是一个数值,但是实际上,它也可以表示一个字符,所以为了消除这种直观错觉,就诞生了 byte 和 rune 这两个别名类型。

string

Go语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型(int、bool、float32、float64 等)一样。 Go 语言里的字符串的内部实现使用UTF-8编码。 字符串的值为双引号(")中的内容,可以在Go语言的源码中直接添加非ASCII码字符,例如:

s1 := "hello"
s2 := "你好"

golang中的字符串底层实际上是一个 byte数组。因此可能会出现下面这种情况

str := "hello 世界"
fmt.Println(len(str)) //输出 12

我们期望得到的结果应该是8,原因是golang中的string底层是由一个byte数组实现的,而golang默认的编码是utf-8,一个中文字符占 3个字节,所以得到的长度是12,想要得到我们想要的结果也很简单,golang中的unicode/utf8包提供了用utf-8获取长度的方法

str := "hello 世界"
fmt.Println(utf8.RuneCountInString(str)) //8

上面说了byte类型实际上是一个int8类型,int8适合表达ascii编码的字符,而int32可以表达更多的数,可以更容易的处理unicode字符,因此,我们可以通过rune类型来处理unicode字符

str := "hello 世界"
str2 := []rune(str)
fmt.Println(len(str2)) //8

这里会将申请一块内存,然后将str的内容复制到这块内存,实际上这块内存是一个rune类型的切片,而str2拿到的是一个rune类型的切片的引用,我们可以很容易的证明这是一个引用

str := "hello 世界"
str2 := []rune(str)
t := str2
t[0] = 'w'
fmt.Println(string(str2)) //“wello 世界”

通过把str2赋值给t,t上改变的数据,实际上是改变的是t指向的rune切片,因此,str也会跟着改变

字符串的遍历
// 遍历字符串
func traversalString() {
    s := "hello 世界"
    for i := 0; i < len(s); i++ { //byte
        fmt.Printf("%v(%c) ", s[i], s[i])
    }
    fmt.Println()
    for _, r := range s { //rune
        fmt.Printf("%v(%c) ", r, r)
    }
    fmt.Println()
}
image.png
修改字符串

要修改字符串,需要先将其转换成[]rune或[]byte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组。

func changeString() {
    s1 := "big"
    // 强制类型转换
    byteS1 := []byte(s1)
    byteS1[0] = 'p'
    fmt.Println(string(byteS1))

    s2 := "白萝卜"
    runeS2 := []rune(s2)
    runeS2[0] = '红'
    fmt.Println(string(runeS2))
}
字符串转义符

Go 语言的字符串常见转义符包含回车、换行、单双引号、制表符等,如下表所示。


image.png

例如:

package main
import (
    "fmt"
)
func main() {
    fmt.Println("str := \"d:\\Code\\work\\go.exe\"")
}
多行字符串

Go语言中要定义一个多行字符串时,就必须使用反引号字符:

s1 := `第一行
        第二行
        第三行
        `
fmt.Println(s1)

反引号间换行将被作为字符串中的换行,但是 所有 的转义字符均无效,文本将会原样输出。

字符串的常用操作
image.png
点关注 不迷路

如果你从本篇内容有收获,求 点赞,求 关注,求 转发 ,让更多的人学习到。


如果本文有任何错误,请批评指教,不胜感激


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

本文来自:简书

感谢作者:不住隔壁的王老师

查看原文:「Go」byte、rune与string

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

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