bytes包常用函数
- bytes.go中提供了很多字节切片的工具类
Compare
- Compare方法比较两个字节切片的大小,-1,0, 1
- nil和空切片是相等的
|
|
Contains
- Contains方法返回b中是否存在子切片subslice
|
|
- Contains函数主题为函数Index
- Index函数返回sep在s中找到的第一个实例的索引值,比如s为{1,2,3,1,2,3}, sep为{2,3},则Index函数返回1,如果没有匹配到,则返回-1
- Index方法中,根据sep的长度做了一些优化,为0直接返回0,意思是index为0
- sep长度为1时,利用IndexByte函数找一个字节是否在切片中出现过
- sep的长度和s的长度相等时,利用Equal方法判断两个切片是否相等
- sep长度小于MaxLen时,这个值根据操作系统在32或者64,
- s的长度小于64时,直接用bytealg.Index函数进行暴力查找即可
- 是的长度大于64时,先找到匹配sep第一个字符的索引值,然后判断第二个字符是否相等,相等则直接用Equal方法判断两个截取的切片是否相等。如果第二个字符不相等,则失败次数+1,同时比较失败次数和Cutover函数的返回值,如果错误次数过多,此时会直接进行暴力查找
- sep的长度大于MaxLen时,前半部分和之前阐述的类似,不通的在于对失败错误的容忍度。
- Cutover函数允许每8个字节出错一次,实验证明其切换间隔为16个字节时效率更高,此时采用RabinKarp算法进行查找
|
|
- RabinKarp算法实现,假定sep为{a,b},s为{d,c,a,b}
- RabinKarp算法首先选取一个基准值,primeRK=16777619,这个
- 然后用函数hashStr对sep进行hash运算,返回的结果包括两个
- 第一个hash,表示的是对子串的计算值,其值可以理解为primeRK进制下该字节数组的值,则hash=97*primeRK+b
- 第二个pow,返回的是和sep长度相关的值,由于pow的值为uint32,则实际上是个取余之后的值,RK^(len(sep-1) % 2^32,其目的是为了在s上找sep滑动时,去除掉最高位的hash值
- 在s的最左边开始计算和sep长度相等的hash值为d*RK+c,如果匹配则直接返回索引0
- 如果不匹配,则在s上进行长度为len(sep)的滑动,此时hash为
d*Rk*Rk + c * RK + a - d * pow
- 而实际上
pow = RK*RK
的,则hash值为c*RK + a
,此时不符合继续滑动 - 滑动之后hash值为
a*RK + b
,此时返回索引2
|
|
- ContainsAny方法只要chars中任意一个utf-8编码的字符存在于字节切片中,返回true
- IndexAny实际上为ContainsAny的主要函数
- 如果s字节数组大于8并且是ASCII编码的,则用函数makeASCIISet构建ASCII集合
- makeASCIISet返回的数据asciiSet为一个32字节的数据,从前向后每一个bit位的索引代表对应的ASCII的值,实际上32字节的数据是分为8个uint32的数字来表示的,这样如字符a的位置为第四个元素的倒数第二个bit位
- 从而对s进行迭代,看其中的byte是否存asciiSet中,asciiSet提供了contain方法来校验其对应的bit位上是否存在该字符
- bitmap可以最大限度的节省内存空间,但是代码会复杂一些,实际工作中自己做取舍
- 否则对切片s进行rune转码,逐个字符迭代比较并返回结果,属于暴力方法
|
|
- ContainsRune方法返回切片b中是否含有字符r,返回找到的匹配字节的索引值
- 通过方法IndexRune来实现
- 如果r是ASCII字符,调用函数IndexBytes来完成查找
- 如果要查找的是utf8.RuneError,则返回第一个不合法的序列
- 否则将rune转换成字节切片,调用Index方法完成查找
|
|
Count
- Count函数返回字节数组中s存在sep的个数
- sep长度为0,则返回s中字符数+1
- sep长度为1,调用bytealg.Count函数返回结果
- 否则,利用Index方法来进行个数查找
|
|
Equal
- Equal的实现是调用更底层的Equal方法
- Equal实际上只是看两个字节切片的长度和元素值是否相等,不看是否来自同一个底层数组的引用
- nil和空切片是相等的
|
|
EqualFold
- EqualFold方法将判断s,t在Unicode大小写折叠下是否相等
- 如果是ASCII编码则简单比较大小写即可,否则需要进行unicode.SimpleFold进行转换并比较
- 判断也是值是否相等
|
|
Fields
- Fields函数将字节切片s,以空白字符切割,并返回[][]byte类型结果,如
[]byte("1 2 3 4")
返回[]string{"1", "2", "3", "4"}
- 用setBits来记录s中出现过的字节
- 如果
setBits>utf8.RuneSelf
表示存在不是ASCII编码的数据,此时调用FieldsFunc方法来完成分割操作- FieldFunc传入的第二个参数为unicode.IsSpace,来判断一个字符是否为空白字符
- span用于记录各个分割出的Fied的起点和终点,从而组织输出数据
- 如果
setBits<utf8.RuneSelf
则表示都是ASCII编码,而ASCII编码的空白字符为[256]uint{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}
,可以快速进行分割
|
|
Has函数
- HasPrefix,HasSuffix函数本质上是用Equals方法判断截取后的字节切片是否相等
|
|
Join
- Join函数,Fields的逆向操作,将二维字切片用sep切片拼接为一维字节切片
- 对于二维切片数组不同的长度做了相应的拼接处理
- 拼接过程中用内建函数copy来完成
|
|
Last类函数
- LastIndex函数返回s中sep最后一次出现的索引值
- LastIndexByte返回字节c在s中最后一次出现的索引值
- LastIndexAny返回s中最后一个出现的chars中的任意一个字符的索引值
|
|
- LastIndexFunc返回满足函数f的最后一个字符的索引值
|
|
Map
- Map函数根据函数mapping完成s中字符的映射变换
|
|
Repeat
- Repeat函数完成对s的多份复制,如{a,b}变为{a,b,a,b}
- 程序中进行相乘的时候需要考虑溢出问题
|
|
Replace
- Replace函数完成字节切片替换,n为替换的个数,n<0则替换全部
- Count函数用于计算old切片出现的次数
- Index用于查找要替换的旧切片old
- ReplaceAll将n设置为-1即可
|
|
Rune
- Rune函数将字节数组转换成Rune切片,主要调用utf8编码的函数来实现
|
|
Split函数
- SplitN函数表示将s按照分隔符sep分割成n份,不带分隔符,最后一份不再进行分割
- SplitAfterN表示将s按照分隔符sep分割成n份,最后一份不再进行分割,带分隔符
- Split函数将s按照分隔符sep进行分割,不带分隔符
- SplitAfter函数将s按照分隔符sep进行分割,带分隔符
|
|
- Split类方法实际上都是对genSplit方法的封装
- sep为空的时候,用explode完成对字节切片的按照字符的分割
- 索引定位的时候也是通过Index方法来实现的
|
|
Title
- Title方法利用Map完成对切片s首字母的大写转换
- isSeparator标识一个字符是否为边界字符
|
|
To类函数
- To类函数实际上都是对Map的一层封装,具体对字符的操作可以参照unicode包的方法
|
|
Trim类函数
- Trim函数完成字节切片前后去除指定的字符数据
- Trim函数的实现是通过TrimFunc函数来实现的
- TrimFunc函数是通过组合TrimRightFunc和TrimLeftFunc来实现的
- TrimRightFunc完成右侧的字符截取,通过lastIndexFunc函数完成
- TrimLeftFunc完成左侧的字符截取,通过indexFunc函数完成
- Trim函数的筛选函数f是makeCutsetFunc函数的返回值
- makeCutsetFunc会分情况返回当前的字节是否属于该剔除的字符
|
|
- TrimPrefix完成指定前缀的截取
- TrimSuffix完成指定后缀的截取
- TrimSpace完成对前后空白字符的截取
|
|