GO 语言中的字符串
Go 语言中的字符串是由单个字节连接起来的字符序列;字符串的字节使用 UTF-8 编码来表示 Unicode 文本。在 Go 中,字符串类型为 string
;无论是字符串常量、字符串变量或是代码中出现的字符串字面值,它们的类型都被统一设置为 string
;字符串是一种值类型;底层是字节的定长数组。
概念
字符串的声明和初始化
使用双引号将多个字符包起来;将单个 Unicode 字符字面值一个接一个地连在一起,并用双引号包裹起来就构成了字符串字面值。
str := "this is a string"
字符串的组成
Go 语言中的字符串值也是一个可空的字节序列,字节序列中的字节个数称为该字符串的长度。string
类型其实是一个“描述符”,它本身并不真正存储字符串数据,而是由一个指向底层存储的指针和字符串的长度字段组成的。
字符串的转义
在 Go 语言中字符串字面量使用英文双引号(")或者反引号(`)来创建。
-
双引号用来创建可解析的字符串,支持转义,但不能用来引用多行。
package main import "fmt" func main() { str := "这是个神马\"玩意\"" fmt.Println(str) // 这是个神马"玩意" }
-
反引号可创建原生的字符串字面量,可以由多行组成,但不支持转义,并且可以包含除反引号外的其他所有字符。
package main import "fmt" func main() { str := `人生得以须尽欢, 莫使金樽空对月。` fmt.Println(str) // 打印后是两行 }
特性
-
string 类型的数据是不可变的,提高了字符串的并发安全性和存储利用率。
var s string = "hello" s[0] = 'k' // 错误:字符串的内容是不可改变的 s = "gopher" // ok
-
没有C语言定义字符串的结尾’\0’,而且获取长度的时间复杂度是常数时间,消除了获取字符串长度的开销。
-
原生支持“所见即所得”的原始字符串,大大降低构造多行字符串时的心智负担,定义方式双引号或反引号。
-
对非 ASCII 字符提供原生支持,消除了源码在不同环境下显示乱码的可能。
rune 类型与字符字面值
rune 类型
-
rune
类型表示一个 Unicode 码点,rune
本质上是int32
类型的别名类型,它与int32
类型是完全等价的。// $GOROOT/src/builtin.go type rune = int32
-
一个
rune
实例就是一个 Unicode 字符。 -
可以通过字符串字面量来初始化一个
rune
变量。
字符串的表示法
-
单引号括起的字符串字面值。
package main func main() { ascii := 'a' // ASCII字符 unicodeChar := '中' // Unicode字符集中的中文字符 specialChar := '\n' // 换行字符 singleChar := '\'' // 单引号字符 }
-
Unicode 专用的转义字符
\u
或\U
作为前缀。\u
后面接四个十六进制数。如果是用四个十六进制数无法表示的 Unicode 字符,我们可以使用\U
,\U
后面可以接八个十六进制数来表示一个 Unicode 字符。package main import "fmt" func main() { str := "\u4e2d" fmt.Println(str) // 中 str = "\U00004e2d" fmt.Println(str) // 中 str = "\u0027" fmt.Println(str) // 单引号字符 ' }
-
由于表示码点的
rune
本质上就是一个整型数,所以我们还可用整型值来直接作为字符字面值给rune
变量赋值。package main import "fmt" func main() { char := '\x27' // 使用十六进制表示的单引号字符 fmt.Println(char) // 39 char = '\047' // 使用八进制表示的单引号字符 fmt.Println(char) // 39 }
UTF-8字符编码
- UTF-8 编码解决的是 Unicode 码点值在计算机中如何存储和表示(位模式)的问题。
- 与UTF-32相比,UTF-32 编码方案固定使用 4 个字节表示每个 Unicode 字符码点;这种编码方案使用 4 个字节存储和传输一个整型数的时候,需要考虑不同平台的字节序问题;由于采用 4 字节的固定长度编码,与采用 1 字节编码的 ASCII 字符集无法兼容;所有 Unicode 字符码点都用 4 字节编码,显然空间利用率很差。
- UTF-32编码方案,将所有 Unicode字符的码点都按照 4 字节编码。 UTF-8编码方案:根据 Unicode 字符的码点序号不同,所编码的字节数不同。1~4 个字节。
字符串的常见操作
下标操作
在字符串的实现中,真正存储数据的是底层的数组。字符串的下标操作本质上等价于底层数组的下标操作。
package main
import "fmt"
func main() {
var s = "中国人"
fmt.Printf("0x%x\n", s[0]) // 0xe4:字符“中” utf-8编码的第一个字节
}
拼接操作
Go 原生支持通过
+
、+=
操作符进行字符串连接,还提供了strings.Builder
、strings.Join
、fmt.Sprintf
等函数来进行字符串连接操作。
package main
import "fmt"
func main() {
s := "Rob Pike, "
s = s + "Robert Griesemer, "
s += " Ken Thompson"
fmt.Printf("founder: %s\n", s) // result: Rob Pike, Robert Griesemer, Ken Thompson
}
比较操作
Go 字符串类型支持各种比较关系操作符,包括
==
、!=
、>=
、<=
、>
和<
。在字符串的比较上,Go 采用字典序的比较策略,分别从每个字符串的起始处,开始逐个字节地对两个字符串类型变量进行比较。当两个字符串之间出现了第一个不相同的元素,比较就结束了,这两个元素的比较结果就会做为字符串最终的比较结果。如果出现两个字符串长度不同的情况,长度比较小的字符串会用空元素补齐,空元素比其他非空元素都小。
package main
import "fmt"
func main() {
// ==
s1 := "世界和平"
s2 := "世界" + "和平"
fmt.Println(s1 == s2) // true
// !=
s1 = "Go"
s2 = "JavaScript"
fmt.Println(s1 != s2) // true
// < and <=
s1 = "12345"
s2 = "23456"
fmt.Println(s1 < s2) // true
fmt.Println(s1 <= s2) // true
// > and >=
s1 = "12345"
s2 = "123"
fmt.Println(s1 > s2) // true
fmt.Println(s1 >= s2) // true
}
类型转换
Go 支持字符串与字节切片、字符串与 rune 切片的双向转换,并且这种转换无需调用任何函数,只需使用显式类型转换。
package main
import "fmt"
func main() {
var s string = "中国人"
// string -> []rune
rs := []rune(s)
fmt.Printf("%x\n", rs) // [4e2d 56fd 4eba]
// string -> []byte
bs := []byte(s)
fmt.Printf("%x,%s\n", bs, string(bs)) // e4b8ade59bbde4baba,中国人
// []rune -> string
s1 := string(rs)
fmt.Println(s1) // 中国人
// []byte -> string
s2 := string(bs)
fmt.Println(s2) // 中国人
}
-
字符串转数字
使用
strconv.ParseInt()
函数将字符串转换为整数类型。使用
strconv.ParseFloat()
函数将字符串转换为浮点数类型。package main import ( "fmt" "reflect" "strconv" ) func main() { str := "12345" num, err := strconv.ParseInt(str, 10, 64) if err != nil { fmt.Println("Error:", err) return } fmt.Printf("num: %d, typeof: %s\n", num, reflect.TypeOf(num)) // num: 12345, typeof: int64 floatStr := "3.14" floatNum, err := strconv.ParseFloat(floatStr, 64) if err != nil { fmt.Println("Error:", err) return } fmt.Printf("float num: %f, typeof: %s \n", floatNum, reflect.TypeOf(floatNum)) // float num: 3.140000, typeof: float64 }
-
字符串转布尔
使用
strconv.ParseBool()
函数将字符串转换为布尔类型。package main import ( "fmt" "reflect" "strconv" ) func main() { strTrue := "true" boolValue, err := strconv.ParseBool(strTrue) if err != nil { fmt.Println("Error:", err) return } fmt.Printf("bool value: %t, typeof: %s\n", boolValue, reflect.TypeOf(boolValue)) // bool value: true, typeof: bool strFalse := "false" boolValue, err = strconv.ParseBool(strFalse) if err != nil { fmt.Println("Error:", err) return } fmt.Printf("bool value: %t, typeof: %s\n", boolValue, reflect.TypeOf(boolValue)) // bool value: false, typeof: bool }
字符串迭代
Go 有两种迭代形式:常规
for
迭代与for range
迭代。
package main
import "fmt"
func main() {
str := "programming"
chars := []rune(str)
for _, char := range chars {
fmt.Println(string(char))
}
str = "I am a gopher"
for index, char := range str {
fmt.Printf("%d %U %c\n", index, char, char)
}
}
字符串修改
Go 语言中不能直接修改字符串的内容;如果要修改字符串的内容。则需要先将字符串的内容复制到一个可写的变量中(一般是 []byte
或者 []rune
类型的变量),然后再进行修改
-
修改字节(
[]byte
)package main import "fmt" func main() { str := "programming" by := []byte(str) by[0] = 'P' fmt.Printf("%s\n", str) // programming fmt.Printf("%s", by) // Programmming }
-
修改字符(
[]rune
)package main import "fmt" func main() { str := "programming" ru := []rune(str) ru[0] = 'P' fmt.Printf("%s\n", str) // programming fmt.Printf("%s", string(ru)) // Programmming }
字符串反转
package main
import "fmt"
func main() {
str := "programming"
fmt.Printf("%s", Reversal(str)) // gnimmargorp
}
func Reversal(str string) string {
b := []byte(str)
length := len(b)
for i, j := 0, length-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return string(b)
}
生成随机字符串
-
使用
math/rand
包生成随机字符串package main import ( "fmt" "math/rand" "time" ) func main() { fmt.Printf(generateRandomString(10)) } // 生成指定长度的随机字符串 func generateRandomString(length int) string { str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" bytes := []byte(str) result := []byte{} r := rand.New(rand.NewSource(time.Now().UnixNano())) for i := 0; i < length; i++ { result = append(result, bytes[r.Intn(len(bytes))]) } return string(result) }
-
用
crypto/rand
包生成随机字符串package main import ( "fmt" "math/rand" ) func main() { fmt.Printf("generate random string: %s\n", generateRandomString(10, []byte("0123456789"))) } // 生成指定长度的随机字符串 func generateRandomString(length int, chars []byte) string { if length == 0 { return "" } clen := len(chars) if clen < 2 || clen > 256 { panic("chars out of range") } maxrb := 255 - (256 % clen) b := make([]byte, length) r := make([]byte, length+(length/4)) // 存储随机字节 i := 0 for { if _, err := rand.Read(r); err != nil { panic("rand.Read error" + err.Error()) } for _, rb := range r { c := int(rb) if c > maxrb { continue } b[i] = chars[c%clen] i++ if i == length { return string(b) } } } }
-
用哈希值来表示随机字符串
package main import ( "crypto/md5" "fmt" "io" "time" ) func main() { fmt.Printf("create password string: %s\n", createPassword()) } func createPassword() string { t := time.Now() h := md5.New() io.WriteString(h, "123456") io.WriteString(h, t.String()) password := fmt.Sprintf("%x", h.Sum(nil)) return password }
控制大小写
Go 语言转换所有字符串为大写或小写,strings 包提供了
ToLower()
和ToUpper()
用于将字符串转换成小写和大写
package main
import (
"fmt"
"strings"
)
func main() {
str := "Gopher"
fmt.Printf("to lower: %s\n", strings.ToLower(str)) // to lower: gopher
fmt.Printf("to upper: %s\n", strings.ToUpper(str)) // to upper: GOPHER
}
去除首尾空格
Go 语言
string
包提供了TrimSpace()
和Trim()
来去除字符串的空格
package main
import (
"fmt"
"strings"
)
func main() {
str := " learn go "
fmt.Printf("trim space: %s\n", strings.TrimSpace(str)) // trim space: learn go
fmt.Printf("trim: %s\n", strings.Trim(str, " ")) // trim: learn go
}
合并与分割字符串
-
合并字符串
Go 语言
strings
包提供了一个名为Join()
的函数;第一个参数是一个字符串数组,第二个参数是分隔符package main import ( "fmt" "strings" ) func main() { str := []string{"l", "e", "a", "r", "n", " ", "Go"} fmt.Printf("join: %s\n", strings.Join(str, "")) // join: learn Go }
-
分割字符串
strings
包提供了Split()
、SplitN()
、SplistAfter()
、SplitAfterN()
;4个函数来处理正则分割字符串package main import ( "fmt" "strings" ) func main() { str := "Learn Go Programming" // 第一个参数为被分割字符串,第二个参数为分隔符 res := strings.Split(str, " ") for i := range res { fmt.Printf("Split: %s\n", res[i]) } // 第一个参数为被分割字符串,第二个参数为分隔符,第三个参数为控制分割的片数,如果为-1则不限制片数 res = strings.SplitN(str, " ", 2) for i := range res { fmt.Printf("SplitN: %s\n", res[i]) } // 与 Split 方法一样 res = strings.SplitAfter(str, " ") for i := range res { fmt.Printf("SplitAfter: %s\n", res[i]) } // 与 SplitN 一样 res = strings.SplitAfterN(str, " ", 2) for i := range res { fmt.Printf("SplitAfterN: %s\n", res[i]) } } // 打印结果 // Split: Learn // Split: Go // Split: Programming // SplitN: Learn // SplitN: Go Programming // SplitAfter: Learn // SplitAfter: Go // SplitAfter: Programming // SplitAfterN: Learn // SplitAfterN: Go Programming
转载自:https://juejin.cn/post/7325040809698492416