1. 字符串简介
- 双引号:字符串使用双引号括起来,其中的相关的转义字符将被替换
str := "Hello World! \n Hello Gopher! \n"
输出:
Hello World!
Hello Gopher!
- 反引号:字符串使用反引号括起来,其中的相关的转义字符不会被替换
str := `Hello World! \n Hello Gopher! \n`
输出:
Hello World! \nHello Gopher! \n
双引号中的转义字符被替换,而反引号中原生字符串中的
\n
会被原样输出。
string
类型的零值是为长度为零的字符串,即空字符串 ""
Go 语言中的string
类型是一种值类型,存储的字符串是不可变的,如果要修改string
内容需要将string
转换为[]byte
或[]rune
,并且修改后的string
内容是重新分配的。
可以通过函数len()
来获取字符串所占的字节长度
str := "asd"
len(str)
如果需要获得字符,应该这么做:
package main
import "fmt"
func main() {
str := "我与春风皆过客,你携秋水揽星河。"
for _, char := range str {
fmt.Printf("%c", char)
}
}
输出:
我与春风皆过客,你携秋水揽星河。
获取字符串中某个字节的地址的行为是非法的,例如:
&str[i]
。
2. 字符串的拼接
- 直接使用运算符
str := "Beginning of the string " +
"second part of the string"
由于编译器行尾自动补全分号的缘故,加号
+
必须放在第一行。
拼接的简写形式 +=
也可以用于字符串:
s := "hel" + "lo, "
s += "world!"
fmt.Println(s) // 输出 “hello, world!”
里面的字符串都是不可变的,每次运算都会产生一个新的字符串,所以会产生很多临时的无用的字符串,不仅没有用,还会给 GC 带来额外的负担,所以性能比较差。
fmt.Sprintf()
str := fmt.Sprintf("%d:%s", 2018, "年")
fmt.Println(str) // 2018:年
内部使用
[]byte
实现,不像直接运算符这种会产生很多临时的字符串,但是内部的逻辑比较复杂,有很多额外的判断,还用到了interface
,所以性能一般。
strings.Join()
str = strings.Join([]string{"hello", "world"}, ", ")
fmt.Println(str) // hello, world
Join
会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入,在已有一个数组的情况下,这种效率会很高,但是本来没有,去构造这个数据的代价也不小。
bytes.Buffer
var buffer bytes.Buffer
buffer.WriteString("hello")
buffer.WriteString(", ")
buffer.WriteString("world")
fmt.Print(buffer.String()) // hello, world
这个比较理想,可以当成可变字符使用,对内存的增长也有优化,如果能预估字符串的长度,还可以用
buffer.Grow()
接口来设置capacity
strings.Builder
var b1 strings.Builder
b1.WriteString("ABC")
b1.WriteString("DEF")
fmt.Print(b1.String()) // ABCDEF
strings.Builder
内部通过slice
来保存和管理内容。slice
内部则是通过一个指针指向实际保存内容的数组。strings.Builder
同样也提供了Grow()
来支持预定义容量。当我们可以预定义我们需要使用的容量时,strings.Builder
就能避免扩容而创建新的slice
了。strings.Builder
是非线程安全,性能上和bytes.Buffer
相差无几。
3. 有关 string 的常用处理
标准库中有四个包对字符串处理尤为重要:bytes
、strings
、strconv
和 unicode
包。
-
strings
包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能 -
bytes
包也提供了很多类似功能的函数,但是针对和字符串有着相同结构的[]byte
类型。因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。在这种情况下,使用bytes.Buffer
类型将会更有效 -
strconv
包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换 -
unicode
包提供了IsDigit
、IsLetter
、IsUpper
和IsLower
等类似功能,它们用于给字符分类
3.1 strings 包
3.1.1 判断两个 utf-8 编码字符串是否相同
func EqualFold(s, t string) bool
将
unicode
大写、小写、标题三种格式字符视为相同
func main() {
str1 := "Golang"
str2 := "golang"
fmt.Println(strings.EqualFold(str1, str2)) // true
}
3.1.2 判断字符串 str 是否是以 prefix 开头
strings.HasPrefix(s string,prefix string) bool
func main() {
str := "哈利·波特"
prefix := "哈利"
res := strings.HasPrefix(str, prefix)
fmt.Println(res) // true
}
3.1.3 判断字符串 str 是否是以 suffix 结尾
strings.HasSuffix(str string,suffix string) bool
func main() {
str := "哈利·波特"
prefix := "波特"
res := strings.HasSuffix(str, prefix)
fmt.Println(res) // true
}
3.1.4 判断 s 在 str 中首次出现的位置,如果没有出现返回 -1
strings.Index(str string,s string) int
func main() {
str := "哈利·波特"
s := "波特"
res := strings.Index(str, s)
fmt.Println(res) // 8,这是字节的index,不是Unicode字符数组的index
}
3.1.5 判断 s 在 str 中最后一次出现的位置,如果没有出现返回 -1
strings.LastIndex(str tring, s string) int
func main() {
str := "哈利·波特"
s := "·"
res := strings.LastIndex(str, s)
fmt.Println(res) // 6,这是字节的index,不是Unicode字符数组的index
}
3.1.6 查询非 ASCII 编码的字符在父字符串中的位置
func IndexRune(s string, r rune) int
func main() {
str := "哈利·波特"
s := '波'
res := strings.IndexRune(str, s)
fmt.Println(res) // 8,这是字节的index,不是Unicode字符数组的index
}
虽然搜索的时rune字符,但是返回的还是字节的索引。
3.1.7 字符串替换
strings.Replace(str string, old string, newStr string, n int) string
将
str
中的old
字符串替换为newStr
字符串,返回替换后的结果,n
参数为替换次数,n<0
表示无限次。
func main() {
str := "I love her, her name is red"
old := "her"
newStr := "him"
res := strings.Replace(str, old, newStr,1)
fmt.Println(res) // I love him, her name is red
res = strings.Replace(str, old, newStr,2)
fmt.Println(res) // I love him, him name is red
res = strings.Replace(str, old, newStr,-2)
fmt.Println(res) // I love him, him name is red
}
3.1.8 返回 str 中 substr 出现的次数
strings.Count(str string, substr string) int
func main() {
str := "I love her, her name is red"
substr := "e"
count := strings.Count(str, substr)
fmt.Println(count) // 5
}
3.1.9 重复 count 次的 str
strings.Repeat(str string, count int) string
func main() {
str := "love !"
count := 3
res := strings.Repeat(str, count)
fmt.Println(res) //love!love!love!
}
3.1.10 转换大小写
strings.ToLower(str string) string
strings.ToUpper(str string) string
func main() {
str := "I Love You !"
lower := strings.ToLower(str)
upper := strings.ToUpper(str)
fmt.Println(lower) // i love you !
fmt.Println(upper) // I LOVE YOU !
}
3.1.11 去掉收尾空格符
strings.TrimSpace(str string) string
func main() {
str := "I Love You ! "
res := strings.TrimSpace(str)
fmt.Println(res) // I Love You !
}
3.1.12 去掉 str 两边的 cut 字符串
strings.Trim(str string, cut string) string
func main() {
str := "ooI Love You !oo"
res := strings.Trim(str, "oo")
fmt.Println(res) // I Love You !
}
3.1.13 去掉 str 一边的cut字符串
strings.TrimLeft(str string, cut string) string
strings.TrimRight(str string, cut string) string
3.1.14 以空格作为分隔符,将 str 分隔成切片
strings.Fields(str string) []string
func main() {
str := "liu hai zhang"
res := strings.Fields(str)
fmt.Println(res)
for _, x := range res {
fmt.Println(x)
}
}
输出结果:
[liu hai zhang]
liu
hai
zhang
3.1.15 以 split 作为分隔符,将 str 分隔成切片
strings.Split(str string,split string) []string
func main() {
str := "liu-hai-zhuang"
res := strings.Split(str, "-")
fmt.Println(res) // [liu hai zhuang]
}
3.1.16 用 sep 把 slice 中的所有元素连接成字符串
strings.Join(slice []string,sep string) string
func main() {
slice := []string{"liu", "hai", "zhuang"}
res := strings.Join(slice, "-")
fmt.Println(res) // liu-hai-zhuang
}
3.1.17 判断字符串 s 是否包含子串 substr
func Contains(s, substr string) bool
func main() {
var str = "中国,台湾"
fmt.Println(strings.Contains(str, "台湾")) //true
fmt.Println(strings.Contains(str, "日本")) //false
}
3.1.18 判断字符串 s 是否包含 utf-8 码值 r
func ContainsRune(s string, r rune) bool
func main() {
var r rune = '中'
var str = "中国"
fmt.Println(strings.ContainsRune(str, r)) //true
fmt.Println(strings.ContainsRune(str, '日')) //false
}
3.1.19 判断字符串 s 是否包含字符串 chars 中的任一字符
func ContainsAny(s, chars string) bool
func main() {
var s = "我爱你,中国"
var chars = "我与春风皆过客"
var test = "日"
fmt.Println(strings.ContainsAny(s, chars)) //true
fmt.Println(strings.ContainsAny(s, test)) //false
}
3.2 bytes 包
方法类似strings包,不给过是针对[]byte
类型,这里省略,可以参考博客
3.3 strconv 包
3.3.1 string 转 int
func Atoi(s string) (i int, err error)
func main() {
numStr := "999"
num, err := strconv.Atoi(numStr)
if err != nil {
fmt.Println("can't convert to int")
} else {
fmt.Printf("type:%T value:%#v\n", num, num) // type:int value:999
}
}
另外还可以用
strconv
包下的:
func ParseInt(s string, base int, bitSize int) (i int64, err error)
或
func ParseUint(s string, base int, bitSize int) (n uint64, err error)
base
指定进制(2到36),如果base
为0
,则会从字符串前置判断,”0x”
是16
进制,”0”
是8
进制,否则是10
进制;
bitSize
指定结果必须能无溢出赋值的整数类型,0
、8
、16
、32
、64
分别代表int
、int8
、int16
、int32
、int64
;
3.3.2 int 转 string
func Itoa(i int) string
func main() {
num := 200
numStr := strconv.Itoa(num)
fmt.Printf("type:%T value:%#v\n", numStr, numStr) // type:string value:"200"
}
3.3.3 string 转 bool
func ParseBool(str string) (bool, error)
当
str
为:1
,t
,T
,TRUE
,true
,True
中的一种时为真值
当str
为:0
,f
,F
,FALSE
,false
,False
中的一种时为假值
func main() {
fmt.Println(strconv.ParseBool("t")) // true
fmt.Println(strconv.ParseBool("TRUE")) // true
fmt.Println(strconv.ParseBool("true")) // true
fmt.Println(strconv.ParseBool("True")) // true
fmt.Println(strconv.ParseBool("0")) //false
fmt.Println(strconv.ParseBool("f")) //false
}
3.3.4 string 转 float
func ParseFloat(s string, bitSize int) (f float64, err error)
bitSize
:32
或64
, 对应系统的位数
func main() {
strF := "250.56"
str, err := strconv.ParseFloat(strF, 64)
if err != nil {
fmt.Println(err)
}
fmt.Printf("type:%T value:%#v\n", str, str) // type:float64 value:250.56
}
3.3.5 float 转 string
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
bitSize
表示f的来源类型(32
:float32
、64
:float64
),会据此进行舍入。
fmt
表示格式:'f'
(-ddd.dddd
)、'b'
(-ddddp±ddd
,指数为二进制)、'e'
(-d.dddde±dd
,十进制指数)、'E'
(-d.ddddE±dd
,十进制指数)、'g'
(指数很大时用'e'
格式,否则'f'
格式)、'G'
(指数很大时用'E'
格式,否则'f'
格式)。
prec
控制精度(排除指数部分):对'f'
、'e'
、'E'
,它表示小数点后的数字个数;对'g'
、'G'
,它控制总的数字个数。如果prec
为-1
,则代表使用最少数量的、但又必需的数字来表示f
。
func main() {
num := 250.56
str := strconv.FormatFloat(num, 'f', 4, 64)
fmt.Printf("type:%T value:%#v\n", str, str) // type:string value:"250.5600"
}
当然,以上类型转string的话,可以直接用fmt.Sprintf
实现。
3.4 unicode 包
3.4.1 判断字符大小写
func IsUpper(r rune) bool
func IsLower(r rune) bool
3.4.2 转换字符大小写
func ToUpper(r rune) rune
func ToLower(r rune) rune
3.4.3 判断字符 Title 格式
// 判断字符 r 是否为 Unicode 规定的 Title 字符
// 大部分字符的 Title 格式就是其大写格式
// 只有少数字符的 Title 格式是特殊字符
// 这里判断的就是特殊字符
func IsTitle(r rune) bool
3.4.4 字符转换 Title 格式
func ToTitle(r rune) rune
3.4.5 将字符转换为指定格式
func To(_case int, r rune) rune
_case
取值:UpperCase
、LowerCase
、TitleCase
3.4.6 判断字符是否是汉字
func main() {
for _, r := range "Hello 世界!" {
// 判断字符是否为汉字
if unicode.Is(unicode.Scripts["Han"], r) {
fmt.Printf("%c", r) // 世界
}
}
}
更多
unicode.Scripts
取值请参考:
3.4.7 字符判断
func IsDigit(r rune) bool
IsDigit
判断r
是否为一个十进制的数字字符
func IsNumber(r rune) bool
IsNumber
判断r
是否为一个数字字符 (类别N
)
func IsLetter(r rune) bool
IsLetter
判断r
是否为一个字母字符 (类别L
),汉字也是一个字母字符
func IsSpace(r rune) bool
IsSpace
判断r
是否为一个空白字符,包括\t
,\n
,\v
,\f
,\r
func IsControl(r rune) bool
IsControl
判断r
是否为一个控制字符
func IsGraphic(r rune) bool
IsGraphic
判断字符r
是否为一个“图形字符”,包括字母、标记、数字、标点、符号、空格
func IsPrint(r rune) bool
IsPrint
判断字符r
是否为 Go 所定义的“可打印字符”,包括字母、标记、数字、标点、符号和 ASCII 空格
func IsPunct(r rune) bool
IsPunct
判断r
是否为一个标点字符
func IsSymbol(r rune) bool
IsSymbol
判断r
是否为一个符号字符