文本处理
1 strings包
1.1 字符串比较
package main
import (
"fmt"
"strings")
func main() {
a := "a"
b := "A"
/*
Compare 函数,用于比较两个字符串的大小,如果两个字符串相等,返回为 0。如果 a 小于 b ,返回 -1 ,反之返回 1 。不推荐使用这个函数,直接使用 == != > < >= <= 等一系列运算符更加直观。
*/
fmt.Println(strings.Compare(a, b)) // 区分大小
fmt.Println(strings.EqualFold(a, b)) // 不区分大小
}
/*
1
true
*/1.2 是否存在某个字符
有好几个函数可以进行判断。
看下运行这些函数返回的结果。
其实查看前面三个函数的源代码就会发现其实调用的都是相对应类型Index函数。
1.3 子串出现的次数
go中计算子串在字符串中出现次数的函数是Count。
特别要注意的是Count返回的是在字符串中无重叠的次数,所以这里返回的是2.当substr为空的时候返回的是utf8.RuneCountInString(s) + 1
1.4 字符串分割
字符串分割最常用的就是Split系列函数。
可以看到这四个函数都调用了genSplit函数。
这四个函数都是通过 sep 进行分割,返回[]string。如果 sep 为空,相当于分成一个个的 UTF-8 字符,如 Split("abc",""),得到的是[a b c]。
Split(s, sep) 和 SplitN(s, sep, -1) 等价;SplitAfter(s, sep) 和 SplitAfterN(s, sep, -1) 等价。带After的函数会把分隔符保留下来。
带 N 的方法可以通过最后一个参数 n 控制返回的结果中的 slice 中的元素个数,当 n < 0 时,返回所有的子字符串;当 n == 0 时,返回的结果是 nil;当 n > 0 时,表示返回的 slice 中最多只有 n 个元素,其中,最后一个元素不会分割,比如:
1.5 判断字符串前缀或后缀
如果 prefix 或 suffix 为 "" , 返回值总是 true。
1.6 Join操作
可以将字符串切片跳过sep分隔符连接起来。
1.7 构建重复字符
函数签名如下:
将 s 重复 count 次,如果 count 为负数或返回值长度 len(s)*count 超出 string 上限会导致 panic,这个函数使用很简单:
输出结果:
1.8 字符替换
可以使用Map或Replace对字符进行替换,但这两个方法粒度不同。
Map方法可以设置一个函数按照字面量粒度对字符进行映射替换。如果 mapping 返回值 <0 ,则舍弃该字符。该方法只能对每一个字符做处理,但处理方式很灵活,可以方便的过滤,筛选汉字等。
Replace函数可以直接替换字符串中的子串。
Replce方法只能对单个子串进行替换,如果想进行多子串替换,可以定义一个Replacer对象。
1.9 大小写转换
在strings包中提供了四种大小写转换的函数。
这四种函数都用来把字符串全部转换成大小或小写。但ToLowerSpecial和ToUpperSpecial可以转换特殊字符的大小写。
1.10 修剪字符串
示例:
1.11 Builder 类型
该类型实现了 io 包下的 Writer, ByteWriter, StringWriter 等接口,可以向该对象内写入数据,Builder 没有实现 Reader 等接口,所以该类型不可读,但提供了 String 方法可以获取对象内的数据。
Builder 的 cap 会自动增长,一般不需要手动调用 Grow 方法。
String 方法可以方便的获取 Builder 的内容。可以用来拼接字符串。
1.12 字符串切片
在Go中字符串的底层其实就是一个[]byte,所以也可以使用切片的方法获取子串。
2 bytes包
该包定义了一些操作 byte slice 的便利操作。因为字符串可以表示为 []byte,因此,bytes 包定义的函数、方法等和 strings 包很类似。
2.1 Rune类型转换
该函数将 []byte 转换为 []rune ,适用于汉字等多字节字符,示例:
运行结果:
2.2 Buffer类型
bytes.Buffer 类型,该类型实现了 io 包下的 ByteScanner, ByteWriter, ReadWriter, Reader, ReaderFrom, RuneReader, RuneScanner, StringWriter, Writer, WriterTo 等接口,可以方便的进行读写操作。
Buffer 可以通过 3 中方法初始化对象:
3 strconv:字符串与基本类型转换
由于将字符串转为其他数据类型可能会出错,strconv 包定义了两个 error 类型的变量:ErrRange 和 ErrSyntax。其中,ErrRange 表示值超过了类型能表示的最大范围,比如将 "128" 转为 int8 就会返回这个错误;ErrSyntax 表示语法错误,比如将 "" 转为 int 类型会返回这个错误。
然而,在返回错误的时候,不是直接将上面的变量值返回,而是通过构造一个 NumError 类型的 error 对象返回。NumError 结构的定义如下:
可见,该结构记录了转换过程中发生的错误信息。该结构不仅包含了一个 error 类型的成员,记录具体的错误信息,而且它自己也实现了 error 接口:
包的实现中,定义了两个便捷函数,用于构造 NumError 对象:
3.1 字符串和整型之间的转换
最常用的有三个函数。
其中,Atoi 是 ParseInt 的便捷版,内部通过调用 ParseInt(s, 10, 0) 来实现的;ParseInt 转为有符号整型;ParseUint 转为无符号整型。
参数 base 代表字符串按照给定的进制进行解释。一般的,base 的取值为 2~36,如果 base 的值为 0,则会根据字符串的前缀来确定 base 的值:"0x" 表示 16 进制; "0" 表示 8 进制;否则就是 10 进制。
参数 bitSize 表示的是整数取值范围,或者说整数的具体类型。取值 0、8、16、32 和 64 分别代表 int、int8、int16、int32 和 int64。
这里有必要说一下,当 bitSize==0 时的情况。
Go 中,int/uint 类型,不同系统能表示的范围是不一样的,目前的实现是,32 位系统占 4 个字节;64 位系统占 8 个字节。当 bitSize==0 时,应该表示 32 位还是 64 位呢?这里没有利用 runtime.GOARCH 之类的方式,而是巧妙的通过如下表达式确定 intSize:
3.2 整型转换为字符串
实际应用中,我们经常会遇到需要将字符串和整型连接起来,在 Java 中,可以通过操作符 "+" 做到。不过,在 Go 语言中,你需要将整型转为字符串类型,然后才能进行连接。这个时候,strconv 包中的整型转字符串的相关函数就派上用场了。这些函数签名如下:
其中,Itoa 内部直接调用 FormatInt(i, 10) 实现的。base 参数可以取 2~36(0-9,a-z)。
标准库还提供了另外两个函数:AppendInt 和 AppendUint,这两个函数不是将整数转为字符串,而是将整数转为字符数组 append 到目标字符数组中。(最终,我们也可以通过返回的 []byte 得到字符串)
除了使用上述方法将整数转为字符串外,经常见到有人使用 fmt 包来做这件事。如:
那么,这两种方式我们该怎么选择呢?我们主要来考察一下性能。
我们分别循环转换了 10000 次。Sprintf 的时间是 3.549761ms,而 Itoa 的时间是 848.208us,相差 4 倍多。
Sprintf 性能差些可以预见,因为它接收的是 interface,需要进行反射等操作。个人建议使用 strconv 包中的方法进行转换。
3.3 字符串与布尔值之间的转换
Go 中字符串和布尔值之间的转换比较简单,主要有三个函数:
3.4 字符串与浮点数之间的转换
类似的,包含三个函数:
函数的命名和作用跟上面讲解的其他类型一致。
关于 FormatFloat 的 fmt 参数, 在第一章第三节格式化 IO 中有详细介绍。而 prec 表示有效数字(对 fmt='b' 无效),对于 'e', 'E' 和 'f',有效数字用于小数点之后的位数;对于 'g' 和 'G',则是所有的有效数字。例如:
由于浮点数有精度的问题,精度不一样,ParseFloat 和 FormatFloat 可能达不到互逆的效果。如:
另外,fmt='b' 时,得到的字符串是无法通过 ParseFloat 还原的。
3.5 Quote
除了这两种方法,strconv 包还提供了函数这做件事(Quote 函数)。我们称 "studygolang.com" 这种用双引号引起来的字符串为 Go 语言字面值字符串(Go string literal)。
4 Regexp包
Go中使用Regexp包来使用正则表达式,简单的正则可以使用以下两个函数。
示例:
4.1 Regexp类型
前面两种方法适合于简单的正则匹配,如果想进行复杂的匹配,需要使用Regexp类型。创建Regexp类型的方法有四种。
4.1.1 匹配操作
会检查是否包含对应正则匹配模式。
4.2 查找操作
4.2.1 替换操作
4.2.2 切割操作
示例:
4.2.3 其他操作
5 unicode包:Unicode码点
世界中的字符有许许多多,有英文,中文,韩文等。我们强烈需要一个大大的映射表把世界上的字符映射成计算机可以阅读的二进制数字(字节)。 这样,每个字符都给予一个独一无二的编码,就不会出现写文字的人和阅读文字的人编码不同而出现无法读取的乱码现象了。
于是 Unicode 就出现了,它是一种所有符号的编码映射。最开始的时候,unicode 认为使用两个字节,也就是 16 位就能包含所有的字符了。 但是非常可惜,两个字节最多只能覆盖 65536 个字符,这显然是不够的,于是附加了一套字符编码,即 unicode4.0,附加的字符用 4 个字节表示。 现在为止,大概 Unicode 可以覆盖 100 多万个字符了。
Unicode 只是定义了一个字符和一个编码的映射,但是呢,对应的存储却没有制定。 比如一个编码 0x0041 代表大写字母 A,那么可能有一种存储至少有 4 个字节,那可能 0x00000041 来存储代表 A。 这个就是 unicode 的具体实现。unicode 的具体实现有很多种,UTF-8 和 UTF-16 就是其中两种。
UTF-8 表示最少用一个字节就能表示一个字符的编码实现。它采取的方式是对不同的语言使用不同的方法,将 unicode 编码按照这个方法进行转换。 我们只要记住最后的结果是英文占一个字节,中文占三个字节。这种编码实现方式也是现在应用最为广泛的方式了。
UTF-16 表示最少用两个字节能表示一个字符的编码实现。同样是对 unicode 编码进行转换,它的结果是英文占用两个字节,中文占用两个或者四个字节。 实际上,UTF-16 就是最严格实现了 unicode4.0。但由于英文是最通用的语言,所以推广程度没有 UTF-8 那么普及。
go 对 unicode 的支持包含三个包 :
unicode
unicode/utf8
unicode/utf16
unicode 包包含基本的字符判断函数。utf8 包主要负责 rune 和 byte 之间的转换。utf16 包负责 rune 和 uint16 数组之间的转换。
由于字符的概念有的时候比较模糊,比如字符(小写 a)普通显示为 a,在重音字符中(grave-accented)中显示为à。 这时候字符(character)的概念就有点不准确了,因为 a 和à显然是两个不同的 unicode 编码,但是却代表同一个字符,所以引入了 rune。 一个 rune 就代表一个 unicode 编码,所以上面的 a 和à是两个不同的 rune。
这里有个要注意的事情,go 语言的所有代码都是 UTF8 的,所以如果我们在程序中的字符串都是 utf8 编码的,但是我们的单个字符(单引号扩起来的)却是 unicode 的。
5.1 unicode包
常用函数:
5.2 utf8 包
utf8 里面的函数就有一些字节和字符的转换。
判断是否符合 utf8 编码的函数:
func Valid(p []byte) bool
func ValidRune(r rune) bool
func ValidString(s string) bool
判断 rune 所占字节数:
func RuneLen(r rune) int
判断字节串或者字符串的 rune 数:
func RuneCount(p []byte) int
func RuneCountInString(s string) (n int)
编码和解码到 rune:
func EncodeRune(p []byte, r rune) int
func DecodeRune(p []byte) (r rune, size int)
func DecodeRuneInString(s string) (r rune, size int)
func DecodeLastRune(p []byte) (r rune, size int)
func DecodeLastRuneInString(s string) (r rune, size int)
是否为完整 rune:
func FullRune(p []byte) bool
func FullRuneInString(s string) bool
是否为 rune 第一个字节:
func RuneStart(b byte) bool
示例:
运行结果:
5.3 utf16 包
utf16 的包的函数就比较少了。
将 uint16 和 rune 进行转换
unicode 有个基本字符平面和增补平面的概念,基本字符平面只有 65535 个字符,增补平面(有 16 个)加上去就能表示 1114112 个字符。 utf16 就是严格实现了 unicode 的这种编码规范。
而基本字符和增补平面字符就是一个代理对(Surrogate Pair)。一个代理对可以和一个 rune 进行转换。
示例:
输出结果:
6 字符串拼接方法汇总
最后更新于