- 1. 字符串简介
- 2. 字符串的拼接
-
3. 有关 string 的常用处理
-
3.1 strings 包
- 3.1.1 判断两个 utf-8 编码字符串是否相同
- 3.1.2 判断字符串 str 是否是以 prefix 开头
- 3.1.3 判断字符串 str 是否是以 suffix 结尾
- 3.1.4 判断 s 在 str 中首次出现的位置,如果没有出现返回 -1
- 3.1.5 判断 s 在 str 中最后一次出现的位置,如果没有出现返回 -1
- 3.1.6 查询非 ASCII 编码的字符在父字符串中的位置
- 3.1.7 字符串替换
- 3.1.8 返回 str 中 substr 出现的次数
- 3.1.9 重复 count 次的 str
- 3.1.10 转换大小写
- 3.1.11 去掉收尾空格符
- 3.1.12 去掉 str 两边的 cut 字符串
- 3.1.13 去掉 str 一边的cut字符串
- 3.1.14 以空格作为分隔符,将 str 分隔成切片
- 3.1.15 以 split 作为分隔符,将 str 分隔成切片
- 3.1.16 用 sep 把 slice 中的所有元素连接成字符串
- 3.1.17 判断字符串 s 是否包含子串 substr
- 3.1.18 判断字符串 s 是否包含 utf-8 码值 r
- 3.1.19 判断字符串 s 是否包含字符串 chars 中的任一字符
- 3.2 bytes 包
- 3.3 strconv 包
- 3.4 unicode 包
-
3.1 strings 包
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, worldJoin
会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入,在已有一个数组的情况下,这种效率会很高,但是本来没有,去构造这个数据的代价也不小。 -
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()) // ABCDEFstrings.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
类型,这里省略,可以参考博客https://www.cnblogs.com/golove/p/3287729.html
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
取值请参考:http://www.cnblogs.com/golove/p/3269099.html
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
是否为一个符号字符