上一篇:
Go源码学习:bytes包 - 1.2 - bytes.go -(1)
11、LastIndex:查找字节切片中子切片的最后一个实例索引
// LastIndex 返回字节切片 s 中子切片 sep 的最后一个实例的索引,如果 sep 不在 s 中则返回 -1。
func LastIndex(s, sep []byte) int {
n := len(sep)
switch {
case n == 0:
return len(s)
case n == 1:
return bytealg.LastIndexByte(s, sep[0])
case n == len(s):
if Equal(s, sep) {
return 0
}
return -1
case n > len(s):
return -1
}
return bytealg.LastIndexRabinKarp(s, sep)
}
解释:
LastIndex
是一个函数,用于查找字节切片s
中子切片sep
的最后一个实例的索引。- 函数内部根据不同情况选择不同的查找方式,包括空子切片、单字节子切片、全匹配子切片等。
- 调用了
bytealg
包中的具体实现函数来执行最后索引的查找操作。
作用:
LastIndex
函数的作用是在字节切片中查找指定子切片的最后一个实例,并返回其索引。如果子切片不在字节切片中,则返回 -1。
12、LastIndexByte:查找字节在字节切片中的最后一个实例索引
// LastIndexByte 返回字节切片 s 中字节 c 的最后一个实例的索引,如果 c 不在 s 中则返回 -1。
func LastIndexByte(s []byte, c byte) int {
return bytealg.LastIndexByte(s, c)
}
解释:
LastIndexByte
是一个函数,用于查找字节切片s
中字节c
的最后一个实例的索引。- 函数内部调用了
bytealg
包中的LastIndexByte
函数来执行具体的字节查找操作。
作用:
LastIndexByte
函数的作用是在字节切片中查找指定字节的最后一个实例,并返回其索引。如果指定字节不在字节切片中,则返回 -1。
13、IndexRune:查找字节切片中指定 rune 的首次出现索引
// IndexRune 将字节切片 s 解释为 UTF-8 编码的码点序列。
// 它返回给定 rune 在 s 中首次出现的字节索引。
// 如果 rune 不在 s 中,则返回 -1。
// 如果 r 是 utf8.RuneError,则返回任何无效 UTF-8 字节序列的第一个实例。
func IndexRune(s []byte, r rune) int {
switch {
case 0 <= r && r < utf8.RuneSelf:
return IndexByte(s, byte(r))
case r == utf8.RuneError:
for i := 0; i < len(s); {
r1, n := utf8.DecodeRune(s[i:])
if r1 == utf8.RuneError {
return i
}
i += n
}
return -1
case !utf8.ValidRune(r):
return -1
default:
var b [utf8.UTFMax]byte
n := utf8.EncodeRune(b[:], r)
return Index(s, b[:n])
}
}
解释:
IndexRune
是一个函数,用于将字节切片s
解释为 UTF-8 编码的码点序列,并查找给定rune
在s
中首次出现的字节索引。- 函数内部根据不同情况选择不同的查找方式,包括单字节 rune、无效 UTF-8 字节序列、有效 rune 等。
- 调用了
IndexByte
、utf8.DecodeRune
、utf8.ValidRune
、utf8.EncodeRune
等函数来执行具体的查找和编解码操作。
作用:
IndexRune
函数的作用是在字节切片中查找指定rune
的首次出现索引。- 可以处理单字节 rune、无效 UTF-8 字节序列等情况,提供灵活的查找功能。
14、IndexAny:查找字节切片中任一字符的首次出现索引
// IndexAny 将字节切片 s 解释为 UTF-8 编码的 Unicode 码点序列。
// 它返回在 s 中任一 Unicode 码点在 chars 中首次出现的字节索引。
// 如果 chars 为空或没有共同的码点,则返回 -1。
func IndexAny(s []byte, chars string) int {
if chars == "" {
// 避免扫描整个 s。
return -1
}
if len(s) == 1 {
r := rune(s[0])
if r >= utf8.RuneSelf {
// 搜索 utf8.RuneError。
for _, r = range chars {
if r == utf8.RuneError {
return 0
}
}
return -1
}
if bytealg.IndexByteString(chars, s[0]) >= 0 {
return 0
}
return -1
}
if len(chars) == 1 {
r := rune(chars[0])
if r >= utf8.RuneSelf {
r = utf8.RuneError
}
return IndexRune(s, r)
}
if len(s) > 8 {
if as, isASCII := makeASCIISet(chars); isASCII {
for i, c := range s {
if as.contains(c) {
return i
}
}
return -1
}
}
var width int
for i := 0; i < len(s); i += width {
r := rune(s[i])
if r < utf8.RuneSelf {
if bytealg.IndexByteString(chars, s[i]) >= 0 {
return i
}
width = 1
continue
}
r, width = utf8.DecodeRune(s[i:])
if r != utf8.RuneError {
// r 是 2 到 4 字节
if len(chars) == width {
if chars == string(r) {
return i
}
continue
}
// 如果可用,使用 bytealg.IndexString 提高性能。
if bytealg.MaxLen >= width {
if bytealg.IndexString(chars, string(r)) >= 0 {
return i
}
continue
}
}
for _, ch := range chars {
if r == ch {
return i
}
}
}
return -1
}
解释:
IndexAny
是一个函数,用于将字节切片s
解释为 UTF-8 编码的 Unicode 码点序列,并查找在s
中任一 Unicode 码点在chars
中首次出现的字节索引。- 函数内部根据不同情况选择不同的查找方式,包括单字符、单字节 rune、ASCII 字符集等。
- 调用了
IndexRune
、bytealg.IndexByteString
、utf8.DecodeRune
、bytealg.IndexString
等函数来执行具体的查找和编解码操作。
作用:
IndexAny
函数的作用是在字节切片中查找任一字符在指定字符集chars
中首次出现的索引。- 可以处理单字符、单字节 rune、多字节 rune、ASCII 字符集等情况,提供灵活的查找功能。
15、LastIndexAny:获取最后一个匹配字符的字节索引
这部分代码定义了 LastIndexAny
函数,用于在字节切片 s
中查找字符串 chars
中任一 Unicode 字符的最后一个匹配,并返回该字符的字节索引。如果 chars
为空或在 s
中没有共同的字符,则返回 -1。
// LastIndexAny 将 s 解释为一系列 UTF-8 编码的 Unicode 代码点。
// 它返回 s 中 chars 中任一 Unicode 代码点的最后一次出现的字节索引。
// 如果 chars 为空或在 s 中没有共同的字符,则返回 -1。
func LastIndexAny(s []byte, chars string) int {
// 如果 chars 为空,则避免扫描整个 s。
if chars == "" {
return -1
}
// 如果 s 的长度大于 8,且 chars 是 ASCII 字符集,优化处理。
if len(s) > 8 {
if as, isASCII := makeASCIISet(chars); isASCII {
// 从 s 的末尾开始遍历,查找匹配字符的索引。
for i := len(s) - 1; i >= 0; i-- {
if as.contains(s[i]) {
return i
}
}
return -1
}
}
// 如果 s 的长度为 1,处理单字节情况。
if len(s) == 1 {
r := rune(s[0])
if r >= utf8.RuneSelf {
// 对于多字节字符,检查是否与 chars 中的任一字符匹配。
for _, r = range chars {
if r == utf8.RuneError {
return 0
}
}
return -1
}
// 对于单字节字符,使用 bytealg.IndexByteString 查找匹配字符。
if bytealg.IndexByteString(chars, s[0]) >= 0 {
return 0
}
return -1
}
// 如果 chars 的长度为 1,处理单字符情况。
if len(chars) == 1 {
cr := rune(chars[0])
if cr >= utf8.RuneSelf {
cr = utf8.RuneError
}
// 从 s 的末尾开始解码最后一个 Unicode 字符,并查找匹配字符的索引。
for i := len(s); i > 0; {
r, size := utf8.DecodeLastRune(s[:i])
i -= size
if r == cr {
return i
}
}
return -1
}
// 通用情况:从 s 的末尾开始遍历,查找最后一个匹配字符的索引。
for i := len(s); i > 0; {
r := rune(s[i-1])
if r < utf8.RuneSelf {
// 对于单字节字符,使用 bytealg.IndexByteString 查找匹配字符。
if bytealg.IndexByteString(chars, s[i-1]) >= 0 {
return i - 1
}
i--
continue
}
// 对于多字节字符,解码最后一个 Unicode 字符,并查找匹配字符的索引。
r, size := utf8.DecodeLastRune(s[:i])
i -= size
if r != utf8.RuneError {
// r 是 2 到 4 字节
if len(chars) == size {
if chars == string(r) {
return i
}
continue
}
// 使用 bytealg.IndexString 进行性能优化(如果可用)。
if bytealg.MaxLen >= size {
if bytealg.IndexString(chars, string(r)) >= 0 {
return i
}
continue
}
}
// 遍历 chars,查找匹配字符的索引。
for _, ch := range chars {
if r == ch {
return i
}
}
}
return -1
}
解释:
LastIndexAny
函数用于在字节切片中查找字符串中任一 Unicode 字符的最后一个匹配,并返回该字符的字节索引。- 函数先处理一些特殊情况,如空字符集或单字符集。
- 对于较长的字节切片,且字符集为 ASCII 字符集时,进行优化处理。
- 在循环中从字节切片的末尾开始遍历,解码 Unicode 字符,并查找匹配字符的索引。
- 使用不同的算法处理单字节字符和多字节字符,以及性能优化的考虑。
作用:
LastIndexAny
主要用于在字节切片中查找最后一个匹配字符的位置。- 通过考虑单字节字符和多字节字符的不同情况,以及对较长字节切片和 ASCII 字符集的优化,提高了查找效率。
16、genSplit:通用切割方法
这部分代码定义了 genSplit
方法,用于根据指定的分隔符切割字节切片,并返回切割后的子切片数组。
// genSplit:通用切割方法,根据 sep 分隔字节切片 s,返回切割后的子切片数组。
// sepSave 参数表示是否在子切片中保留 sepSave 字节的分隔符。
// n 参数确定要返回的子切片数量:
// n > 0:最多返回 n 个子切片;最后一个子切片将是未切割的剩余部分。
// n == 0:结果为 nil(零个子切片)。
// n < 0:返回所有子切片。
func genSplit(s, sep []byte, sepSave, n int) [][]byte {
// 如果 n 为零,返回 nil。
if n == 0 {
return nil
}
// 如果分隔符为空,调用 explode 方法进行切割。
if len(sep) == 0 {
return explode(s, n)
}
// 如果 n 为负数,设定为 s 中 sep 的数量加一。
if n < 0 {
n = Count(s, sep) + 1
}
// 如果 n 大于 s 的长度加一,设定为 s 的长度加一。
if n > len(s)+1 {
n = len(s) + 1
}
// 初始化子切片数组 a,长度为 n。
a := make([][]byte, n)
n--
i := 0
// 循环切割过程,直到达到指定的子切片数量。
for i < n {
// 在 s 中找到分隔符 sep 的位置 m。
m := Index(s, sep)
// 如果找不到分隔符,跳出循环。
if m < 0 {
break
}
// 将 s 的起始位置到 m+sepSave 的部分切割,并存储到子切片数组 a 中。
a[i] = s[:m+sepSave : m+sepSave]
// 更新 s 的起始位置,去除已切割的部分和分隔符。
s = s[m+len(sep):]
i++
}
// 将剩余部分存储到最后一个子切片。
a[i] = s
return a[:i+1]
}
解释:
genSplit
方法是一个通用的切割方法,用于根据指定的分隔符切割字节切片。- 方法的参数包括要切割的字节切片
s
、分隔符sep
、是否在子切片中保留分隔符的字节数sepSave
以及要返回的子切片数量n
。 - 首先,处理特殊情况:如果
n
为零,返回 nil;如果分隔符为空,调用explode
方法进行切割;如果n
为负数,设定为s
中分隔符的数量加一;如果n
大于s
的长度加一,设定为s
的长度加一。 - 初始化子切片数组
a
,长度为n
。 - 通过循环进行切割过程,直到达到指定的子切片数量。
- 在循环中,在
s
中找到分隔符sep
的位置,并将切割的部分存储到子切片数组a
中。 - 更新
s
的起始位置,去除已切割的部分和分隔符。 - 将剩余部分存储到最后一个子切片。
- 返回切割后的子切片数组。
作用:
genSplit
方法的主要作用是根据指定的分隔符将字节切片切割成多个子切片,并返回这些子切片组成的数组。- 可以控制返回的子切片数量,并选择是否在子切片中保留分隔符的部分字节。
17、SplitN:切割成指定数量的子切片
这部分代码定义了 SplitN
方法,用于将字节切片 s
切割成由分隔符 sep
分隔的子切片数组,并返回这些子切片。
// SplitN:将字节切片 s 切割成由分隔符 sep 分隔的子切片数组,并返回这些子切片。
// 如果 sep 为空,则在每个 UTF-8 序列后进行切割。
// count 参数确定要返回的子切片数量:
//
// n > 0:最多返回 n 个子切片;最后一个子切片将是未切割的剩余部分。
// n == 0:结果为 nil(零个子切片)。
// n < 0:返回所有子切片。
//
// 要围绕第一个分隔符进行切割,请参见 Cut 方法。
func SplitN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) }
解释:
SplitN
方法是用于切割字节切片的函数,返回由分隔符sep
分隔的子切片数组。- 如果分隔符为空,则在每个 UTF-8 序列后进行切割。
- 参数
n
确定要返回的子切片数量:- 如果
n
大于 0,最多返回n
个子切片,最后一个子切片将是未切割的剩余部分。 - 如果
n
等于 0,结果为 nil(零个子切片)。 - 如果
n
小于 0,返回所有子切片。
- 如果
18、SplitAfterN:切割成指定数量的子切片(保留分隔符)
这部分代码定义了 SplitAfterN
方法,用于将字节切片 s
切割成由分隔符 sep
分隔的子切片数组,并返回这些子切片。与 SplitN
不同的是,SplitAfterN
会在每个分隔符后保留分隔符的部分字节。
// SplitAfterN:将字节切片 s 切割成由分隔符 sep 分隔的子切片数组,并返回这些子切片。
// 与 SplitN 不同,SplitAfterN 会在每个分隔符后保留分隔符的部分字节。
// count 参数确定要返回的子切片数量:
//
// n > 0:最多返回 n 个子切片;最后一个子切片将是未切割的剩余部分。
// n == 0:结果为 nil(零个子切片)。
// n < 0:返回所有子切片。
func SplitAfterN(s, sep []byte, n int) [][]byte {
return genSplit(s, sep, len(sep), n)
}
解释:
SplitAfterN
方法是用于切割字节切片的函数,返回由分隔符sep
分隔的子切片数组。- 与
SplitN
不同,SplitAfterN
会在每个分隔符后保留分隔符的部分字节。 - 参数
n
确定要返回的子切片数量:- 如果
n
大于 0,最多返回n
个子切片,最后一个子切片将是未切割的剩余部分。 - 如果
n
等于 0,结果为 nil(零个子切片)。 - 如果
n
小于 0,返回所有子切片。
- 如果
19、Split:切割成所有子切片
这部分代码定义了 Split
方法,用于将字节切片 s
切割成由分隔符 sep
分隔的子切片数组,并返回这些子切片。相当于调用 genSplit
方法,将 count
参数设定为 -1。
// Split:将字节切片 s 切割成由分隔符 sep 分隔的子切片数组,并返回这些子切片。
// 如果 sep 为空,则在每个 UTF-8 序列后进行切割。
// 等效于使用 count 参数为 -1 的 SplitN 方法。
//
// 要围绕第一个分隔符进行切割,请参见 Cut 方法。
func Split(s, sep []byte) [][]byte { return genSplit(s, sep, 0, -1) }
解释:
Split
方法是用于切割字节切片的函数,返回由分隔符sep
分隔的子切片数组。- 如果分隔符为空,则在每个 UTF-8 序列后进行切割。
- 等效于使用
genSplit
方法,将count
参数设定为 -1。
20、SplitAfter:切割成所有子切片(保留分隔符)
这部分代码定义了 SplitAfter
方法,用于将字节切片 s
切割成由分隔符 sep
分隔的子切片数组,并返回这些子切片。与 Split
不同的是,SplitAfter
会在每个分隔符后保留分隔符的部分字节。
// SplitAfter:将字节切片 s 切割成由分隔符 sep 分隔的子切片数组,并返回这些子切片。
// 与 Split 不同,SplitAfter 会在每个分隔符后保留分隔符的部分字节。
// 等效于使用 count 参数为 -1 的 SplitAfterN 方法。
func SplitAfter(s, sep []byte) [][]byte {
return genSplit(s, sep, len(sep), -1)
}
解释:
SplitAfter
方法是用于切割字节切片的函数,返回由分隔符sep
分隔的子切片数组。- 与
Split
不同,SplitAfter
会在每个分隔符后保留分隔符的部分字节。 - 等效于使用
genSplit
方法,将count
参数设定为 -1。
作用:
SplitAfter
方法的作用与Split
方法类似,不同之处在于保留分隔符的部分字节。