[Go语言入门] 07 Go语言字符串

07 Go语言字符串

7.1 字符串类型

在Go语言中,字符串属于基本类型,因此字符串也是值类型。

字符串的值不可变,创建某个字符串后无法修改这个字符串的内容,更深入地讲,字符串是字节的定长数组。

实际上,字符串的底层实现是一个定长的字节序列。该字节序列中可以包含任意数值,包括0值字节。


s1 := "Hello World"

var s2 string = "Hello World"

var s3 string
s3 = "Hello World"

7.2 Unicode & UTF-8

Unicode

早期,美国发明计算机的时候,只需要存储英文字符。那时,只需要使用7个比特位就可以表示所有的英文字符了(7个比特位可以表示128个字符)。当时发明了一个编码:ASCII码(美国信息交换标准码)。ASCII码使用7位标识128个字符,其中有:大小写英文字母、数字、标点符号和一些设备控制符。但随着信息计数的发展,世界上许多使用其他语言的人没有办法在计算机上使用自己的文字字符,于是,后来又搞出了Unicode码。

Unicode为目前世界上所有的文字系统中的符号都进行了编码,为每个字符赋予了一个叫Unicode码点的数值。为了保持对ASCII码的兼容,0到127之间的码点对应的字符与ASCII码完全一致。

Unicode包含的字符简直太多了,要表示所有的字符,需要用到32个位(4个字节)。在Go中,这样一个Unicode码点被称作rune。Go还专门提供了一个类型rune,用来存放Unicode码点。实际上,在Go中类型rune是int32的别名。

我们可以使用int32序列来表示字符串,这样,在该字符串中可以出现世界上所有的文字。这种编码方式有个专门的名字,叫UTF-32,即,每个Unicode码点都用4字节来存放。由于UTF-32是定长编码(每个码点用相同长度来存放),因此这种编码方式很便于程序处理。但UTF-32方式的缺点是:即使存放码点的值为0到127之间的字符,也要用4个字节。这太浪费了。因此,又搞出来一个UTF-8。


UTF-8

UTF-8以字节为单位对Unicode码点做变长编码。

每个文字符号使用1~4个字节来表示。首字节高位指明了后面还有多少字节。

  • 如果最高位为0,则表示仅占1字节,该字节的低7位是ASCII码,这与早期出现的ASCII保持一致。
  • 如果最高位为110,则表示占2字节,并规定第二个字节需要以10开始。
  • 如果最高位为1110,则表示占3字节,并规定后续字节需要以10开始。
  • 如果最高位位11110,则表示占4字节,并规定后续字节需要以10开始。

见下面所示:

0xxxxxxx									码点0~127				ASCII
110xxxxx 10xxxxxx							码点128~2047			
1110xxxx 10xxxxxx 10xxxxxx					码点2048~65535
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx			码点65536~0x10ffff

由于绝大部分的文本中出现的字符的Unicode码点都小于65536,甚至对于纯英文文本中的字符来说,Unicode码点小于128。因此使用UTF-8与UTF-32比起来更加节省空间。而且,要在UTF-8编码中定位一个字符的起始位置时,最多向前追溯3个字节即可找到。


Go语言的源文件需要以UTF-8编码存放,而且Go语言字符串的底层字节序列中也是以UTF-8编码存放数据。

示例:

s := "Hello World"
fmt.Println(s)


// 按字节比较
r1 := s == "Hello World"
r2 := s > "Hi"


// 改变字符串内容 - 编译错误: 字符串不可改变
// s[0] = 'A'	

7.3 字符串字面量

字符串字面量:直接以字符串值得形式出现在代码中。也就是带双引号的字符序列,例如:

"Hello, World!"

有些特殊字符无法用键盘输入,那么如何在字符串字面量中表示呢。Go语言提供了字符转义的功能。

在字符串字面量中,使用反斜杠(\)后跟特定字符来指明一些特殊ASCII字符。比如:

\a		响铃符
\b		退格符
\f		换页符
\n		换行符
\r		回车符
\t		制表符
\v		垂直制表符
\'		单引号
\"		双引号
\\		反斜杠

也可以用(\x)或(\o)来后跟16进制或8进制数来表示单个字节。注意,是单个字节。常用于在字符串中安放任意的字节序列,比如:

"\x2c\x30\x45\x66"
"\o123\o46\o55"

也可以使用(\u)或(\U)后跟16位或32位Unicode码点值来表示任意的Unicode字符。注意,实际占几个字节取决与该Unicode码点的UTF-8编码占多少字节。比如:

"\u4e16\u754c"
"\U00004e16\U0000754c"

原生的字符串字面量,以两个反引号(`)包含的字符序列称作原生字符串,它里面的任何内容都不会被转义,而是原模原样存放在字符串中。原生字符串适合于HTML模板、JSON字面量、文本提示信息等等:

`<html>
<body>
Hello World
</body>
</html>`

`{
 "greeting": "Hello"
 "to": "World"
}`

7.4 字符串基本操作

返回字节数:使用函数len()
s := "Hello World"

// 返回字符串的字节数  -- 记住是字节数,不是字符数,一个UTF8编码的字符可能用1~4个字节表示
cnt := len(s)
fmt.Println(cnt)

索引操作:使用[n]
s := "Hello World"

// 返回第n个字节的值  -- 返回的是第n个字节,不是字符
fmt.Println(s[0], s[1])

// 不可越界访问,越界访问会触发宕机异常
// c := s[len(s)]

截取子串:使用[m:n]
s := "Hello World"

//截取子串  -- 返回的字串是一个新字符串
s1 := s[2:5]   	// 从下标2到5,含2不含5
s2 := s[2:]		// 从下标2到最后
s3 := s[:5]		// 从开始到下标5,不含5

// 截取字串不能越界,越界会触发宕机异常
// s4 := s[2:30]

连接字符串:使用 + 号
s1 := "Hello "
s2 := "World"

// +号连接两个字符串生成一个新字符串
s := s1 + s2

字符串比较
s1 := "Hello World"
s2 := "hello world"

fmt.Println(s1 == s2)	// false
fmt.Println(s1 != s2)	// true
fmt.Println(s1 < s2)	// true
fmt.Println(s1 > s2)	// false
fmt.Println(s1 <= s2)	// true
fmt.Println(s1 >= s2)	// false

遍历字符串

方式1,使用下标遍历(按字节遍历字符串):

s := "Hello 世界"

for i := 0; i < len(s); i++ {
    fmt.Printf("%c", s[i])
}

// 输出
// Hello 世界

方式2,使用for-range遍历(按字符遍历字符串):

s := "Hello 世界"

for _, v := range s {
    fmt.Printf("%c", v)
}

// 输出
// Hello 世界

字符串转[]byte

在Golang中,不能修改字符串的内容,如果要对字符串中的字节进行修改,需要先转换为[]byte类型。

[]byte类型是字节切片类型,后面的课程将详细介绍。

s := "Hello 世界"
b := []byte(s) // 转为[]byte类型,会自动复制数据到[]byte中(暂时可以这样理解)
b[5] = ','
fmt.Printf("%s\n", s)
fmt.Printf("%s\n", b)

// 输出
// Hello 世界
// Hello,世界
字符串转[]rune

[]rune类型是rune切片类型。前面已经介绍过了rune用于存放Unicode码点。

s := "Hello 世界"
b := []rune(s)    // 转换为[]rune类型,数据被自动复制到[]rune中。
b[6] = '中'
b[7] = '国'
fmt.Println(s)
fmt.Println(string(b))

// 输出
// Hello 世界
// Hello 中国

7.5 strings包简介

Go语言的标准库中的strings包,专门提供了用于处理字符串的函数。

strings.Contains
// 判断s中是否包含子串substr
func Contains(s, substr string) bool

示例:

s := "Hello,世界!!!!!"
b := strings.Contains(s, "!!")
fmt.Println(b) // true
b = strings.Contains(s, "!?")
fmt.Println(b) // false
b = strings.Contains(s, "")
fmt.Println(b) // true

strings.ContainsAny
// 判断s中是否包含 chars 中的任何一个字符
func ContainsAny(s, chars string) bool

示例:

s := "Hello,世界!"
b := strings.ContainsAny(s, "abc")
fmt.Println(b) // false
b = strings.ContainsAny(s, "def")
fmt.Println(b) // true
b = strings.Contains(s, "")
fmt.Println(b) // true
strings.ContainsRune
// 判断s中是否包含字符 r
func ContainsRune(s string, r rune) bool

示例:

s := "Hello,世界!"
b := strings.ContainsRune(s, '\n')
fmt.Println(b) // false
b = strings.ContainsRune(s, '界')
fmt.Println(b) // true
b = strings.ContainsRune(s, 0)
fmt.Println(b) // false

strings.Index
// 返回子串 sep 在字符串 s 中第一次出现的位置
// 如果找不到,则返回 -1,如果 sep 为空,则返回 0。
func Index(s, sep string) int

示例:

s := "Hello,世界!"
i := strings.Index(s, "h")
fmt.Println(i) // -1
i = strings.Index(s, "!")
fmt.Println(i) // 12
i = strings.Index(s, "")
fmt.Println(i) // 0

strings.LastIndex
// 返回子串 sep 在字符串 s 中最后一次出现的位置
// 如果找不到,则返回 -1,如果 sep 为空,则返回字符串的长度
func LastIndex(s, sep string) int

示例:

s := "Hello,世界! Hello!"
i := strings.LastIndex(s, "h")
fmt.Println(i) // -1
i = strings.LastIndex(s, "H")
fmt.Println(i) // 14
i = strings.LastIndex(s, "")
fmt.Println(i) // 20

strings.IndexRune
// 返回字符 r 在字符串 s 中第一次出现的位置
// 如果找不到,则返回 -1
func IndexRune(s string, r rune) int

示例:

s := "Hello,世界! Hello!"
i := strings.IndexRune(s, '\n')
fmt.Println(i) // -1
i = strings.IndexRune(s, '界')
fmt.Println(i) // 9
i = strings.IndexRune(s, 0)
fmt.Println(i) // -1

strings.IndexAny
// 返回字符串 chars 中的任何一个字符在字符串 s 中第一次出现的位置
// 如果找不到,则返回 -1,如果 chars 为空,则返回 -1
func IndexAny(s, chars string) int

示例:

s := "Hello,世界! Hello!"
i := strings.IndexAny(s, "abc")
fmt.Println(i) // -1
i = strings.IndexAny(s, "dof")
fmt.Println(i) // 1
i = strings.IndexAny(s, "")
fmt.Println(i) // -1

strings.LastIndexAny
// 返回字符串 chars 中的任何一个字符在字符串 s 中最后一次出现的位置
// 如果找不到,则返回 -1,如果 chars 为空,也返回 -1
func LastIndexAny(s, chars string) int

示例:

s := "Hello,世界! Hello!"
i := strings.LastIndexAny(s, "abc")
fmt.Println(i) // -1
i = strings.LastIndexAny(s, "def")
fmt.Println(i) // 15
i = strings.LastIndexAny(s, "")
fmt.Println(i) // -1

strings.HasPrefix
// 判断字符串 s 是否以 prefix 开头
func HasPrefix(s, prefix string) bool

示例:

s := "Hello 世界!"
b := strings.HasPrefix(s, "hello")
fmt.Println(b) // false
b = strings.HasPrefix(s, "Hello")
fmt.Println(b) // true

strings.HasSuffix
// 判断字符串 s 是否以 prefix 结尾
func HasSuffix(s, suffix string) bool

示例:

s := "Hello 世界!"
b := strings.HasSuffix(s, "世界")
fmt.Println(b) // false
b = strings.HasSuffix(s, "世界!")
fmt.Println(b)

strings.ToUpper、strings.ToLower
// 将 s 中的所有字符修改为其大写格式
func ToUpper(s string) string

// 将 s 中的所有字符修改为其小写格式
func ToLower(s string) string

示例:

s := "heLLo worLd Abc"
us := strings.ToUpper(s)
ls := strings.ToLower(s)
fmt.Printf("%q\n", us) // "HELLO WORLD ABC"
fmt.Printf("%q\n", ls) // "hello world abc"

strings.TrimSpace
// 删除 s 首尾连续的的空白字符
func TrimSpace(s string) string

示例:

s := " Hello 世界! "
ts := strings.TrimSpace(s)
fmt.Printf("%q\n", ts) // "Hello 世界!"

strings.EqualFold
// EqualFold 判断 s 和 t 是否相等。忽略大小写,同时它还会对特殊字符进行转换
// 比如将“ϕ”转换为“Φ”、将“DŽ”转换为“Dž”等,然后再进行比较
func EqualFold(s, t string) bool

示例:

s1 := "Hello 世界! ϕ DŽ"
s2 := "hello 世界! Φ Dž"
b := strings.EqualFold(s1, s2)
fmt.Printf("%v\n", b) // true

Copyright@2022 , 359152155@qq.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空旅客er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值