一、字符串
《快学 Go 语言》第 7 课 —— 字符串
go语言中是按utf8来存储字符串的,从在线查看字符编码中可以看到“嘻哈”的utf8编码是e598bb e59388。


1.通过下标来访问内部字节数组具体位置上的字节
func main() {
var s = "嘻哈china"
for i:=0;i<len(s);i++ {
fmt.Printf("%x ", s[i])
}
}
-----------
e5 98 bb e5 93 88 63 68 69 6e 61
2.对字符串进行 range 遍历,每次迭代出两个变量 codepoint 和 runeValue。codepoint 表示字符起始位置,runeValue 表示对应的 unicode 编码(类型是 rune)。
package main
import "fmt"
func main() {
var s = "嘻哈china"
for codepoint, runeValue := range s {
fmt.Printf("%d %d ", codepoint, int32(runeValue))
}
}
-----------
0 22075 3 21704 6 99 7 104 8 105 9 110 10 97
对字符串进行 range 遍历,每次迭代出两个变量 codepoint 和 runeValue。codepoint 表示字符起始位置,runeValue 表示对应的 unicode 编码(类型是 rune)。
3.字符串是只读的
你可以使用下标来读取字符串指定位置的字节,但是你无法修改这个位置上的字节内容。如果你尝试使用下标赋值,编译器在语法上直接拒绝你。
package main
func main() {
var s = "hello"
s[0] = 'H'
}
--------
./main.go:5:7: cannot assign to s[0]
4.字节切片和字符串的相互转换
在使用 Go 语言进行网络编程时,经常需要将来自网络的字节流转换成内存字符串,同时也需要将内存字符串转换成网络字节流。Go 语言直接内置了字节切片和字符串的相互转换语法。
package main
import "fmt"
func main() {
var s1 = "hello world"
var b = []byte(s1) // 字符串转字节切片
var s2 = string(b) // 字节切片转字符串
fmt.Println(b)
fmt.Println(s2)
}
--------
[104 101 108 108 111 32 119 111 114 108 100]
hello world
从节省内存的角度出发,你可能会认为字节切片和字符串的底层字节数组是共享的。但是事实不是这样的,底层字节数组会被拷贝。如果内容很大,那么转换操作是需要一定成本的。
那为什么需要拷贝呢?因为字节切片的底层数组内容是可以修改的,而字符串的底层字节数组是只读的,如果共享了,就会导致字符串的只读属性不再成立。
5.修改字符串
在 Go 语言中,字符串的内容是不能修改的,也就是说,你不能用 s[0] 这种方式修改字符串中的 UTF-8 编码,如果你一定要修改,那么你可以将字符串的内容复制到一个可写的缓冲区中,然后再进行修改。这样的缓冲区一般是 []byte 或 []rune。如果要对字符串中的字节进行修改,则转换为 []byte 格式,如果要对字符串中的字符进行修改,则转换为 []rune 格式,转换过程会自动复制数据。
angel := "Heros never die"
angleBytes := []byte(angel)
for i := 5; i <= 10; i++ {
angleBytes[i] = ' '
}
fmt.Println(string(angleBytes))
字符串不可变有很多好处,如天生线程安全,大家使用的都是只读对象,无须加锁;再者,方便内存共享,而不必使用写时复制(Copy On Write)等技术;字符串 hash 值也只需要制作一份。
6.关于中文
(1)使用%q打印,使用utf8包
for i, r := range "Hello, 世界" {
fmt.Printf("%d\t%q\t%d\n", i, r, r)
}
ss := "Hello, 世界"
fmt.Println(len(ss)) // "13"
fmt.Println(utf8.RuneCountInString(ss)) // "9"
(2)golang截取中文字符串
在golang中可以通过切片截取一个数组或字符串,但是当截取的字符串是中文时,可能会出现的问题是:由于中文一个字不只是由一个字节组成,所以直接通过切片可能会把一个中文字的编码截成两半,结果导致最后一个字符是乱码。
例如: 想要截取前四个字
name := "我是胡八一"
fmt.Println("name[:4] = ",name[:4])
执行后得到的结果会是这样的:
name[:4] = 我?
解决方法: 先将其转为[]rune,再截取后,转会string
nameRune := []rune(name)
fmt.Println("string(nameRune[:4]) = ",string(nameRune[:4]))
运行结果:
string(nameRune[:4]) = 我是胡八
import "strconv" //先导入strconv包
// string到int
int, err := strconv.Atoi(string)
// string到int64
int64, err := strconv.ParseInt(string, 10, 64)
// int到string
string := strconv.Itoa(int)
// int64到string
string := strconv.FormatInt(int64,10)
二、字典
《快学 Go 语言》第 6 课 —— 字典
1.make
func main() {
var m map[int]string = make(map[int]string)
fmt.Println(m, len(m))
}
----------
map[] 0
如果你可以预知字典内部键值对的数量,那么还可以给 make 函数传递一个整数值,通知运行时提前分配好相应的内存。这样可以避免字典在长大的过程中要经历的多次扩容操作。
var m = make(map[int]string, 16)
2.初始化
func main() {
var m map[int]string = map[int]string{
90: "优秀",
80: "良好",
60: "及格", // 注意这里逗号不可缺少,否则会报语法错误
}
fmt.Println(m, len(m))
}
---------------
map[90:优秀 80:良好 60:及格] 3
3.读写
func main() {
var fruits = map[string]int {
"apple": 2,
"banana": 5,
"orange": 8,
}
// 读取元素
var score = fruits["banana"]
fmt.Println(score)
// 增加或修改元素
fruits["pear"] = 3
fmt.Println(fruits)
// 删除元素
delete(fruits, "pear")
fmt.Println(fruits)
}
-----------------------
5
map[apple:2 banana:5 orange:8 pear:3]
map[orange:8 apple:2 banana:5]
删除操作时,如果对应的 key 不存在,delete 函数会静默处理。遗憾的是 delete 函数没有返回值,你无法直接得到 delete 操作是否真的删除了某个元素。这时候必须使用字典的特殊语法,如下
func main() {
var fruits = map[string]int {
"apple": 2,
"banana": 5,
"orange": 8,
}
var score, ok = fruits["durin"]
if ok {
fmt.Println(score)
} else {
fmt.Println("durin not exists")
}
fruits["durin"] = 0
score, ok = fruits["durin"]
if ok {
fmt.Println(score)
} else {
fmt.Println("durin still not exists")
}
}
-------------
durin not exists
0
4.遍历
这个和数组一样的
func main() {
var fruits = map[string]int {
"apple": 2,
"banana": 5,
"orange": 8,
}
for name, score := range fruits {
fmt.Println(name, score)
}
for name := range fruits {
fmt.Println(name)
}
}
------------
orange 8
apple 2
banana 5
apple
banana
orange
奇怪的是,Go 语言的字典没有提供诸于 keys() 和 values() 这样的方法,意味着如果你要获取 key 列表,就得自己循环一下,如下
func main() {
var fruits = map[string]int {
"apple": 2,
"banana": 5,
"orange": 8,
}
var names = make([]string, 0, len(fruits))
var scores = make([]int, 0, len(fruits))
for name, score := range fruits {
names = append(names, name)
scores = append(scores, score)
}
fmt.Println(names, scores)
}
----------
[apple banana orange] [2 5 8]
5.hash算法
网友评论