前言

字符串(string) 作为 go 语言的基本数据类型,在开发中必不可少,我们务必深入学习一下,做到一清二楚。

本文假设读者已经知道切片(slice)的使用,如不了解,可阅读 Go 切片 基本知识点

为了更好的理解后文,推荐先阅读 Unicode 字符集,UTF-8 编码

是什么

In Go, a string is in effect a read-only slice of bytes.

在 go 语言中,字符串实际上是一个只读字节切片,其数据结构定义如下:

// runtime/string.go
type stringStruct struct {
str unsafe.Pointer // 指向底层字节数组的指针
len int // 字节数组的长度
}

注意:byte 其实是 uint8 的类型别名

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

怎么用

func main() {
// 使用字符串字面量初始化
var a = "hi,狗"
fmt.Println(a) // 可以使用下标访问,但不可修改
fmt.Printf("a[0] is %d\n", a[0])
fmt.Printf("a[0:2] is %s\n", a[0:2])
// a[0] = 'a' 编译报错,Cannot assign to a[0] // 字符串拼接
var b = a + "狗"
fmt.Printf("b is %s\n", b) // 使用内置 len() 函数获取其长度
fmt.Printf("a's length is: %d\n", len(a)) // 使用 for;len 遍历
for i := 0; i < len(a); i++ {
fmt.Println(i, a[i])
} // 使用 for;range 遍历
for i, v := range a {
fmt.Println(i, v)
}
} /* output
hi,狗 a[0] is 104
a[0:2] is hi b is hi,狗狗 a's length is: 6 0 104
1 105
2 44
3 231
4 139
5 151 0 104
1 105
2 44
3 29399
*/

如果读者在看上面的代码时有疑惑,不用着急,下文将会挨个解读。

只读

字符串常量会在编译期分配到只读段,对应数据地址不可写入,相同的字符串常量不会重复存储

func main() {
var a = "hello"
fmt.Println(a, &a, (*reflect.StringHeader)(unsafe.Pointer(&a)))
a = "world"
fmt.Println(a, &a, (*reflect.StringHeader)(unsafe.Pointer(&a)))
var b = "hello"
fmt.Println(b, &b, (*reflect.StringHeader)(unsafe.Pointer(&b)))
} /* output
字符串字面量 该变量的内存地址 底层字节切片
hello 0xc0000381f0 &{5033779 5}
world 0xc0000381f0 &{5033844 5}
hello 0xc000038220 &{5033779 5}
*/

可以看到 hello 在底层只存储了一份

for;len 遍历

go 的源代码都是 UTF-8 编码格式的,上例中的”狗“字占用三个字节,即 231 139 151(Unicode Character Table),所以上例的运行结果很清楚。

于此同时,也可以将字符串转化为字节切片

func main() {
var a = "hi,狗"
b := []byte(a)
fmt.Println(b) // [104 105 44 231 139 151]
}

for;range 遍历

The Unicode standard uses the term "code point" to refer to the item represented by a single value.

在 Unicode 标准中,使用术语 code point 来表示由单个值表示的项,通俗点来说,U+72D7(十进制表示为 29399)代表符号 ”狗“

"Code point" is a bit of a mouthful, so Go introduces a shorter term for the concept: rune.

code point 有点拗口,所以在 go 语言中专门有一个术语来代表它,即 rune

注意:rune 其实是 int32 的类型别名

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

在对字符串类型进行 for;range 遍历时,其实是按照 rune 类型来解码的,所以上例的运行结果也很清晰。

与此同时,也可以将字符串转化为 rune 切片

func main() {
// 使用字符串字面量初始化
var a = "hi,狗"
r := []rune(a)
fmt.Println(r) // [104 105 44 29399]
}

当然我们也可以使用 "unicode/utf8" 标准库,手动实现 for;range 语法糖相同的效果

func main() {
var a = "hi,狗"
for i, w := 0, 0; i < len(a); i += w {
runeValue, width := utf8.DecodeRuneInString(a[i:])
fmt.Printf("%#U starts at byte position %d\n", runeValue, i)
w = width
}
} /* output
U+0068 'h' starts at byte position 0
U+0069 'i' starts at byte position 1
U+002C ',' starts at byte position 2
U+72D7 '狗' starts at byte position 3
*/

参考

Strings, bytes, runes and characters in Go

为什么说go语言中的string是不可变的?

字符咋存?utf8咋编码?string啥结构?

Go string 一清二楚的更多相关文章

  1. 死啃了String源码之后

    Java源码之String 说在前面: 为什么看源码: 最好的学习的方式就是模仿,接下来才是创造.而源码就是我们最好的模仿对象,因为写源码的人都不是一般的人,所以用心学习源码,也就可能变成牛逼的人.其 ...

  2. Go string 详解

    前言 字符串(string) 作为 go 语言的基本数据类型,在开发中必不可少,我们务必深入学习一下,做到一清二楚. 本文假设读者已经知道切片(slice)的使用,如不了解,可阅读 Go 切片 基本知 ...

  3. 透过WinDBG的视角看String

    摘要 : 最近在博客园里面看到有人在讨论 C# String的一些特性. 大部分情况下是从CODING的角度来讨论String. 本人觉得非常好奇, 在运行时态, String是如何与这些特性联系上的 ...

  4. JavaScript String对象

    本编主要介绍String 字符串对象. 目录 1. 介绍:阐述 String 对象的说明以及定义方式. 2. 实例属性:介绍 String 对象的实例属性: length. 3. 实例方法:介绍 St ...

  5. ElasticSearch 5学习(9)——映射和分析(string类型废弃)

    在ElasticSearch中,存入文档的内容类似于传统数据每个字段一样,都会有一个指定的属性,为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成字符串值,Elasticsearc ...

  6. [C#] string 与 String,大 S 与小 S 之间没有什么不可言说的秘密

    string 与 String,大 S 与小 S 之间没有什么不可言说的秘密 目录 小写 string 与大写 String 声明与初始化 string string 的不可变性 正则 string ...

  7. js报错: Uncaught RangeError: Invalid string length

    在ajax请求后得到的json数据,遍历的时候chrome控制台报这个错误:Uncaught RangeError: Invalid string length,在stackoverflow查找答案时 ...

  8. c# 字符串连接使用“+”和string.format格式化两种方式

    参考文章:http://www.liangshunet.com/ca/201303/218815742.htm 字符串之间的连接常用的两种是:“+”连接.string.format格式化连接.Stri ...

  9. 【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

    之前以为BinaryWriter写string会严格按构造时指定的编码(不指定则是无BOM的UTF8)写入string的二进制,如下面的代码: //将字符串"a"写入流,再拿到流的 ...

随机推荐

  1. history附上时间戳,history命令_Linux history命令:查看和执行历史命令

    起因是这样的,一台机器客户反馈连接不上,说没有任何操作.好吧,排查吧. 1.第一步先看网络是否通: 从图中可以看到一开始是一直不通的.然后就通了,问了客户有没操作重启什么的结果说没有任何操作,还让给个 ...

  2. (SqlServe)关于字符串长度被截断的问题

    1. 问题描述 在同步数据时常常会发现一个错误:将截断字符串或二进制数据. 2. 问题原因 这个问题出现的原因是:要插入的数值字段的长度超出了数据库中字段的长度.比如:插入的字符串字节长度是40,数据 ...

  3. GDB查看内存命令(x命令) 用gdb查看指定地址的内存内容

    GDB查看内存命令(x命令) - super119 - 博客园 https://www.cnblogs.com/super119/archive/2011/11/18/2254382.html 可以使 ...

  4. java的volatile关键字(笔记)

    Volatile修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值.而且,当成员变量发生变化时,强迫线程将变化值回写到主内存.这样在任何时刻,两个不同的线程总是看到某个成员变量的同一 ...

  5. MapReduce编程练习(四),统计多个输入文件学生的平均成绩,

    问题描述: 在输入文件中,有多个,其中每个输入文件代表一个学生的各科成绩,其中每行的数据形式为<科目,成绩>,你需要将每个文件中的每科目的成绩进行统计,然后求平均值. 输入文件格式: 这里 ...

  6. linux 系统磁盘管理(主分区和逻辑分区)

    摘要:linux系统磁盘管理主分区和逻辑分区 1.linux系统分区应了解的常识 硬盘分区实质上是对硬盘的一种格式化,然后才能使用硬盘保存各种信息,在创建分区时,就已经设置好了硬盘的各项物理参数,指定 ...

  7. WPF 一种带有多个子集的类ComBox 解决方法

    在最近的工作中遇到很多,类似这种layUI风格的Combox: 因为WPF原本的控件,并不具备这种功能,尝试重写Combox的模板,发现无从下手. 于是尝试从多个控件组合来实现这个功能. 这里使用了P ...

  8. C#脚本引擎RulesEngine

    当编写应用程序时,经常性需要花费大量的时间与精力处理业务逻辑,往往业务逻辑的变化需要重构或者增加大量代码,对开发测试人员很不友好. 之前在这篇文章说过,可以使用脚本引擎来将我们需要经常变化的代码进行动 ...

  9. Codeforces Round #340 (Div. 2) E. XOR and Favorite Number

    time limit per test 4 seconds memory limit per test 256 megabytes input standard input output standa ...

  10. Codeforces Round #687 (Div. 2, based on Technocup 2021 Elimination Round 2) A. Prison Break

    题意:有一张\(n\)x\(m\)的图,图中每个点都关押着罪犯,在坐标\((r,c)\)处有一个出口,每名罪犯每秒可以可以像上下最有移动一个单位或者不动,问所有罪犯能够逃离监狱的最少时间. 题解:直接 ...