// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // strings 包实现了简单的函数来操做 UTF-8 编码的字符串。 package strings import ( "internal/bytealg" "unicode" "unicode/utf8" ) // explode将 s 拆分为一段 UTF-8 字符串 func explode(s string, n int) []string { l := utf8.RuneCountInString(s) if n < 0 || n > l { n = l } a := make([]string, n) for i := 0; i < n-1; i++ { ch, size := utf8.DecodeRuneInString(s) a[i] = s[:size] s = s[size:] if ch == utf8.RuneError { a[i] = string(utf8.RuneError) } } if n > 0 { a[n-1] = s } return a } // Count 计算 s 中 substr 的非重叠实例的数量 // 若是 substr 是空字符串,Count 返回 1 + s 中的 Unicode 码点数。 func Count(s, substr string) int { // special case if len(substr) == 0 { return utf8.RuneCountInString(s) + 1 } if len(substr) == 1 { return bytealg.CountString(s, substr[0]) } n := 0 for { i := Index(s, substr) if i == -1 { return n } n++ s = s[i+len(substr):] } } // Contains 包含报告 substr 是否在 s 内 func Contains(s, substr string) bool { return Index(s, substr) >= 0 } // ContainsAny 报告字符中的任何 Unicode 代码点是否在 s 内 func ContainsAny(s, chars string) bool { return IndexAny(s, chars) >= 0 } // containsRune 报告 Unicode 代码点 r 是否在 s 内。 func ContainsRune(s string, r rune) bool { return IndexRune(s, r) >= 0 } // LastIndex 返回 s 中 substr 最后一个实例的索引,若是 substr 不存在于 s 中,则返回 -1。 func LastIndex(s, substr string) int { n := len(substr) switch { case n == 0: return len(s) case n == 1: return LastIndexByte(s, substr[0]) case n == len(s): if substr == s { return 0 } return -1 case n > len(s): return -1 } // Rabin-Karp search from the end of the string hashss, pow := bytealg.HashStrRev(substr) last := len(s) - n var h uint32 for i := len(s) - 1; i >= last; i-- { h = h*bytealg.PrimeRK + uint32(s[i]) } if h == hashss && s[last:] == substr { return last } for i := last - 1; i >= 0; i-- { h *= bytealg.PrimeRK h += uint32(s[i]) h -= pow * uint32(s[i+n]) if h == hashss && s[i:i+n] == substr { return i } } return -1 } // IndexByte 返回 s 中 c 的第一个实例的索引,若是 c 不存在于 s 中,则返回 -1。 func IndexByte(s string, c byte) int { return bytealg.IndexByteString(s, c) } // IndexRune 返回 Unicode 代码点 r 的第一个实例的索引,若是 s 中不存在 rune,则返回 -1。 // 若是 r 是 utf8.RuneError,它将返回任何无效 UTF-8 字节序列的第一个实例。 func IndexRune(s string, r rune) int { switch { case 0 <= r && r < utf8.RuneSelf: return IndexByte(s, byte(r)) case r == utf8.RuneError: for i, r := range s { if r == utf8.RuneError { return i } } return -1 case !utf8.ValidRune(r): return -1 default: return Index(s, string(r)) } } // IndexAny 返回来自 s 中 chars 的任何 Unicode 代码点的第一个实例的索引,若是 s 中不存在来自 chars 的 Unicode 代码点,则返回 -1。 func IndexAny(s, chars string) int { if chars == "" { // Avoid scanning all of s. return -1 } if len(chars) == 1 { // Avoid scanning all of s. 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 := 0; i < len(s); i++ { if as.contains(s[i]) { return i } } return -1 } } for i, c := range s { if IndexRune(chars, c) >= 0 { return i } } return -1 } // LastIndexAny 返回 s 中来自 chars 的任何 Unicode 代码点的最后一个实例的索引,若是 s 中不存在来自 chars 的 Unicode 代码点,则返回 -1。 func LastIndexAny(s, chars string) int { if chars == "" { // Avoid scanning all of s. return -1 } if len(s) == 1 { rc := rune(s[0]) if rc >= utf8.RuneSelf { rc = utf8.RuneError } if IndexRune(chars, rc) >= 0 { return 0 } return -1 } if len(s) > 8 { if as, isASCII := makeASCIISet(chars); isASCII { for i := len(s) - 1; i >= 0; i-- { if as.contains(s[i]) { return i } } return -1 } } if len(chars) == 1 { rc := rune(chars[0]) if rc >= utf8.RuneSelf { rc = utf8.RuneError } for i := len(s); i > 0; { r, size := utf8.DecodeLastRuneInString(s[:i]) i -= size if rc == r { return i } } return -1 } for i := len(s); i > 0; { r, size := utf8.DecodeLastRuneInString(s[:i]) i -= size if IndexRune(chars, r) >= 0 { return i } } return -1 } // LastIndexByte 返回 s 中最后一个 c 实例的索引,若是 c 不存在于 s 中,则返回 -1。 func LastIndexByte(s string, c byte) int { for i := len(s) - 1; i >= 0; i-- { if s[i] == c { return i } } return -1 } // 通用拆分:在每一个 sep 实例以后拆分,包括子数组中 sep 的 sepSave 字节 func genSplit(s, sep string, sepSave, n int) []string { if n == 0 { return nil } if sep == "" { return explode(s, n) } if n < 0 { n = Count(s, sep) + 1 } a := make([]string, n) n-- i := 0 for i < n { m := Index(s, sep) if m < 0 { break } a[i] = s[:m+sepSave] s = s[m+len(sep):] i++ } a[i] = s return a[:i+1] } // SplitN 将 s 分割成由 sep 分隔的子字符串,并返回这些分隔符之间的子字符串的一部分。 // // 计数决定了要返回的子串的数量: n > 0:最多 n 个子串; 最后一个子串将是未拆分的余数。 // // s 和 sep 的边缘状况(例如,空字符串)按照 Split 的文档中的描述进行处理。 func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) } // SplitAfterN 在 sep 的每一个实例以后将 s 切片为子字符串,并返回这些子字符串的切片。 // s 和 sep 的边缘状况(例如,空字符串)按照 SplitAfter 的文档中的描述进行处理。 func SplitAfterN(s, sep string, n int) []string { return genSplit(s, sep, len(sep), n) } // Split 将切片 s 拆分为由 sep 分隔的全部子字符串,并返回这些分隔符之间的子字符串的切片。 // // 若是 s 不包含 sep 而且 sep 不为空,则 Split 返回长度为 1 的切片,其惟一元素是 s。 // // 若是 sep 为空,则 Split 在每一个 UTF-8 序列以后拆分。 若是 s 和 sep 都为空,则 Split 返回一个空切片。 // // 它等效于计数为 -1 的 SplitN。 func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) } // SplitAfter 在 sep 的每一个实例以后将 s 切片为全部子字符串,并返回这些子字符串的切片。 // // 若是 s 不包含 sep 而且 sep 不为空,则 SplitAfter 返回长度为 1 的切片,其惟一元素是 s。 // // 若是 sep 为空,则 SplitAfter 在每一个 UTF-8 序列以后拆分。 若是 s 和 sep 都为空,则 SplitAfter 返回一个空切片。 // // 它等效于计数为 -1 的 SplitAfterN。 func SplitAfter(s, sep string) []string { return genSplit(s, sep, len(sep), -1) } var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} // Fields围绕一个或多个连续空白字符的每一个实例拆分字符串 s,如 unicode.IsSpace 定义的那样,若是 s 仅包含空白,则返回 s 的子字符串切片或空切片。 func Fields(s string) []string { // 首先计算字段。 // 若是 s 是 ASCII,这是一个精确的计数,不然它是一个近似值。 n := 0 wasSpace := 1 // setBits 用于跟踪在 s 的字节中设置了哪些位。 setBits := uint8(0) for i := 0; i < len(s); i++ { r := s[i] setBits |= r isSpace := int(asciiSpace[r]) n += wasSpace & ^isSpace wasSpace = isSpace } if setBits >= utf8.RuneSelf { // Some runes in the input string are not ASCII. return FieldsFunc(s, unicode.IsSpace) } // ASCII fast path a := make([]string, n) na := 0 fieldStart := 0 i := 0 // 跳过输入前面的空格。 for i < len(s) && asciiSpace[s[i]] != 0 { i++ } fieldStart = i for i < len(s) { if asciiSpace[s[i]] == 0 { i++ continue } a[na] = s[fieldStart:i] na++ i++ // 跳过字段之间的空格。 for i < len(s) && asciiSpace[s[i]] != 0 { i++ } fieldStart = i } if fieldStart < len(s) { // 最后一个字段可能以 EOF 结束。 a[na] = s[fieldStart:] } return a } // FieldsFunc 在每次运行知足 f(c) 的 Unicode 代码点 c 时拆分字符串 s,并返回 s 切片的数组。 // 若是 s 中的全部代码点都知足 f(c) 或字符串为空,则返回一个空切片。 // // FieldsFunc 不保证它调用 f(c) 的顺序,并假设 f 老是为给定的 c 返回相同的值。 func FieldsFunc(s string, f func(rune) bool) []string { // span 用于记录 s[start:end] 形式的切片。开始索引是包含的,结束索引是不包含的。 type span struct { start int end int } spans := make([]span, 0, 32) // 查找字段开始和结束索引。在单独的通道中执行此操做(而不是对字符串 s 进行切片并当即收集结果子字符串)要高效得多,这多是因为缓存效应。 start := -1 // valid span start if >= 0 for end, rune := range s { if f(rune) { if start >= 0 { spans = append(spans, span{start, end}) // 将开始设置为负值。 // 注意:在此处一致且可重复地使用 -1 会使此代码在 amd64 上减慢几个百分点。 start = ^start } } else { if start < 0 { start = end } } } // 最后一个字段可能以 EOF 结束。 if start >= 0 { spans = append(spans, span{start, len(s)}) } // 从记录的字段索引建立字符串。 a := make([]string, len(spans)) for i, span := range spans { a[i] = s[span.start:span.end] } return a } // Join 链接其第一个参数的元素以建立单个字符串。 分隔符字符串 sep 放置在结果字符串中的元素之间。 func Join(elems []string, sep string) string { switch len(elems) { case 0: return "" case 1: return elems[0] } n := len(sep) * (len(elems) - 1) for i := 0; i < len(elems); i++ { n += len(elems[i]) } var b Builder b.Grow(n) b.WriteString(elems[0]) for _, s := range elems[1:] { b.WriteString(sep) b.WriteString(s) } return b.String() } // HasPrefix 测试字符串 s 是否之前缀开头。 func HasPrefix(s, prefix string) bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix } // HasSuffix 测试字符串 s 是否之后缀结尾。 func HasSuffix(s, suffix string) bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } // Map 返回字符串 s 的副本,其中的全部字符都根据映射函数进行了修改。 若是映射返回负值,则从字符串中删除该字符而不进行替换。 func Map(mapping func(rune) rune, s string) string { // 在最坏的状况下,字符串在映射时会增加,这让事情变得不愉快。 但这种状况太罕见了,咱们强加于假设它没问题。 它也可能会缩小,但会天然而然地脱落。 // 输出缓冲区 b 按需初始化,第一次出现不一样的字符 var b Builder for i, c := range s { r := mapping(c) if r == c && c != utf8.RuneError { continue } var width int if c == utf8.RuneError { c, width = utf8.DecodeRuneInString(s[i:]) if width != 1 && r == c { continue } } else { width = utf8.RuneLen(c) } b.Grow(len(s) + utf8.UTFMax) b.WriteString(s[:i]) if r >= 0 { b.WriteRune(r) } s = s[i+width:] break } // Fast path for unchanged input if b.Cap() == 0 { // didn't call b.Grow above return s } for _, c := range s { r := mapping(c) if r >= 0 { // 常见状况 // 因为内联,肯定是否应该调用 WriteByte 而不是老是调用 WriteRune 的性能更高 if r < utf8.RuneSelf { b.WriteByte(byte(r)) } else { // r 不是 ASCII 符文。 b.WriteRune(r) } } } return b.String() } // 重复返回由字符串 s 的 count 个副本组成的新字符串。 // // 若是 count 为负数或 (len(s) * count) 的结果溢出,它会发生恐慌。 func Repeat(s string, count int) string { if count == 0 { return "" } // 因为咱们不能在溢出时返回错误,若是重复会产生溢出,咱们应该panic。 if count < 0 { panic("strings: negative Repeat count") } else if len(s)*count/count != len(s) { panic("strings: Repeat count causes overflow") } n := len(s) * count var b Builder b.Grow(n) b.WriteString(s) for b.Len() < n { if b.Len() <= n/2 { b.WriteString(b.String()) } else { b.WriteString(b.String()[:n-b.Len()]) break } } return b.String() } // ToUpper 返回全部 Unicode 字母都映射为大写的 s。 func ToUpper(s string) string { isASCII, hasLower := true, false for i := 0; i < len(s); i++ { c := s[i] if c >= utf8.RuneSelf { isASCII = false break } hasLower = hasLower || ('a' <= c && c <= 'z') } if isASCII { // 优化仅 ASCII 字符串。 if !hasLower { return s } var b Builder b.Grow(len(s)) for i := 0; i < len(s); i++ { c := s[i] if 'a' <= c && c <= 'z' { c -= 'a' - 'A' } b.WriteByte(c) } return b.String() } return Map(unicode.ToUpper, s) } // ToLower 返回 s 并将全部 Unicode 字母映射到它们的小写字母。 func ToLower(s string) string { isASCII, hasUpper := true, false for i := 0; i < len(s); i++ { c := s[i] if c >= utf8.RuneSelf { isASCII = false break } hasUpper = hasUpper || ('A' <= c && c <= 'Z') } if isASCII { // 优化仅 ASCII 字符串。 if !hasUpper { return s } var b Builder b.Grow(len(s)) for i := 0; i < len(s); i++ { c := s[i] if 'A' <= c && c <= 'Z' { c += 'a' - 'A' } b.WriteByte(c) } return b.String() } return Map(unicode.ToLower, s) } // ToTitle 返回字符串 s 的副本,其中全部 Unicode 字母都映射到它们的 Unicode 标题大小写。 func ToTitle(s string) string { return Map(unicode.ToTitle, s) } // ToUpperSpecial 返回字符串 s 的副本,其中使用 c 指定的大小写映射将全部 Unicode 字母映射为大写。 func ToUpperSpecial(c unicode.SpecialCase, s string) string { return Map(c.ToUpper, s) } // ToLowerSpecial 返回字符串 s 的副本,其中全部 Unicode 字母都使用 c 指定的大小写映射映射到它们的小写字母。 func ToLowerSpecial(c unicode.SpecialCase, s string) string { return Map(c.ToLower, s) } // ToTitleSpecial 返回字符串 s 的副本,其中全部 Unicode 字母都映射到它们的 Unicode 标题大小写,优先考虑特殊的大小写规则。 func ToTitleSpecial(c unicode.SpecialCase, s string) string { return Map(c.ToTitle, s) } // ToValidUTF8 返回字符串 s 的副本,其中每次运行的无效 UTF-8 字节序列都被替换字符串替换,替换字符串可能为空。 func ToValidUTF8(s, replacement string) string { var b Builder for i, c := range s { if c != utf8.RuneError { continue } _, wid := utf8.DecodeRuneInString(s[i:]) if wid == 1 { b.Grow(len(s) + len(replacement)) b.WriteString(s[:i]) s = s[i:] break } } // 不变的输入的快速路径 if b.Cap() == 0 { return s } invalid := false // 前一个字节来自无效的 UTF-8 序列 for i := 0; i < len(s); { c := s[i] if c < utf8.RuneSelf { i++ invalid = false b.WriteByte(c) continue } _, wid := utf8.DecodeRuneInString(s[i:]) if wid == 1 { i++ if !invalid { invalid = true b.WriteString(replacement) } continue } invalid = false b.WriteString(s[i : i+wid]) i += wid } return b.String() } // isSeparator 报告符文是否能够标记单词边界。 TODO:当包 unicode 捕获更多属性时更新。 func isSeparator(r rune) bool { // ASCII 字母数字和下划线不是分隔符 if r <= 0x7F { switch { case '0' <= r && r <= '9': return false case 'a' <= r && r <= 'z': return false case 'A' <= r && r <= 'Z': return false case r == '_': return false } return true } // 字母和数字不是分隔符 if unicode.IsLetter(r) || unicode.IsDigit(r) { return false } // 不然,咱们如今所能作的就是将空格视为分隔符。 return unicode.IsSpace(r) } // Title 返回字符串 s 的副本,其中全部以单词开头的 Unicode 字母都映射到它们的 Unicode 标题大小写。 func Title(s string) string { // 在这里使用闭包来记住状态。 骇人听闻但有效。 取决于按顺序扫描地图并为每一个符文调用一次闭包。 prev := ' ' return Map( func(r rune) rune { if isSeparator(prev) { prev = r return unicode.ToTitle(r) } prev = r return r }, s) } // TrimLeftFunc 返回字符串 s 的一部分,其中删除了全部知足 f(c) 的前导 Unicode 代码点 c。 func TrimLeftFunc(s string, f func(rune) bool) string { i := indexFunc(s, f, false) if i == -1 { return "" } return s[i:] } // TrimRightFunc 返回字符串 s 的一部分,其中删除了全部知足 f(c) 的尾随 Unicode 代码点 c。 func TrimRightFunc(s string, f func(rune) bool) string { i := lastIndexFunc(s, f, false) if i >= 0 && s[i] >= utf8.RuneSelf { _, wid := utf8.DecodeRuneInString(s[i:]) i += wid } else { i++ } return s[0:i] } // TrimFunc 返回字符串 s 的一部分,其中删除了全部知足 f(c) 的前导和尾随 Unicode 代码点 c。 func TrimFunc(s string, f func(rune) bool) string { return TrimRightFunc(TrimLeftFunc(s, f), f) } // IndexFunc 将索引返回到知足 f(c) 的第一个 Unicode 代码点的 s 中,若是没有,则返回 -1。 func IndexFunc(s string, f func(rune) bool) int { return indexFunc(s, f, true) } // LastIndexFunc 将索引返回到知足 f(c) 的最后一个 Unicode 代码点的 s 中,若是没有,则返回 -1。 func LastIndexFunc(s string, f func(rune) bool) int { return lastIndexFunc(s, f, true) } // indexFunc 与 IndexFunc 相同,除了若是真值==假,谓词函数的意义被反转。 func indexFunc(s string, f func(rune) bool, truth bool) int { for i, r := range s { if f(r) == truth { return i } } return -1 } // lastIndexFunc 与 LastIndexFunc 相同,只是 true==false,谓词函数的意义颠倒了。 func lastIndexFunc(s string, f func(rune) bool, truth bool) int { for i := len(s); i > 0; { r, size := utf8.DecodeLastRuneInString(s[0:i]) i -= size if f(r) == truth { return i } } return -1 } // asciiSet 是一个 32 字节的值,其中每一位表示集合中给定 ASCII 字符的存在。 // 低 16 字节的 128 位,从最低字的最低有效位到最高字的最高有效位,映射到全部 128 个 ASCII 字符的完整范围。 // 高 16 字节的 128 位将被清零,确保任何非 ASCII 字符都将被报告为不在集合中。 type asciiSet [8]uint32 // makeASCIISet 建立一组 ASCII 字符并报告 chars 中的全部字符是否都是 ASCII。 func makeASCIISet(chars string) (as asciiSet, ok bool) { for i := 0; i < len(chars); i++ { c := chars[i] if c >= utf8.RuneSelf { return as, false } as[c>>5] |= 1 << uint(c&31) } return as, true } // contains 报告 c 是否在集合内。 func (as *asciiSet) contains(c byte) bool { return (as[c>>5] & (1 << uint(c&31))) != 0 } func makeCutsetFunc(cutset string) func(rune) bool { if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { return func(r rune) bool { return r == rune(cutset[0]) } } if as, isASCII := makeASCIISet(cutset); isASCII { return func(r rune) bool { return r < utf8.RuneSelf && as.contains(byte(r)) } } return func(r rune) bool { return IndexRune(cutset, r) >= 0 } } // Trim 返回包含在 cutset 中的全部前导和尾随 Unicode 代码点的字符串 s 的切片。 func Trim(s, cutset string) string { if s == "" || cutset == "" { return s } return TrimFunc(s, makeCutsetFunc(cutset)) } // TrimLeft 返回字符串 s 的一个切片,其中包含在 cutset 中的全部前导 Unicode 代码点都已删除。 // // 要删除前缀,请改用 TrimPrefix。 func TrimLeft(s, cutset string) string { if s == "" || cutset == "" { return s } return TrimLeftFunc(s, makeCutsetFunc(cutset)) } // TrimRight 返回字符串 s 的一个片断,其中包含在 cutset 中的全部尾随 Unicode 代码点被删除。 // // 要删除后缀,请改用 TrimSuffix。 func TrimRight(s, cutset string) string { if s == "" || cutset == "" { return s } return TrimRightFunc(s, makeCutsetFunc(cutset)) } // TrimSpace 返回字符串 s 的一部分,删除全部前导和尾随空格,如 Unicode 定义的那样。 func TrimSpace(s string) string { // ASCII 的快捷路径:查找第一个 ASCII 非空格字节 start := 0 for ; start < len(s); start++ { c := s[start] if c >= utf8.RuneSelf { // 若是咱们遇到非 ASCII 字节,则在剩余字节上回退到较慢的 unicode-aware 方法 return TrimFunc(s[start:], unicode.IsSpace) } if asciiSpace[c] == 0 { break } } // 如今从末尾开始寻找第一个 ASCII 非空格字节 stop := len(s) for ; stop > start; stop-- { c := s[stop-1] if c >= utf8.RuneSelf { return TrimFunc(s[start:stop], unicode.IsSpace) } if asciiSpace[c] == 0 { break } } // 此时 s[start:stop] 以一个 ASCII 非空格字节开始和结束,因此咱们完成了。 上面已经处理了非 ASCII 的状况。 return s[start:stop] } // TrimPrefix 返回没有提供的前导前缀字符串的 s。若是 s 不之前缀开头,则 s 原样返回。 func TrimPrefix(s, prefix string) string { if HasPrefix(s, prefix) { return s[len(prefix):] } return s } // TrimSuffix 返回没有提供的尾随后缀字符串的 s。若是 s 不之后缀结尾,则 s 原样返回。 func TrimSuffix(s, suffix string) string { if HasSuffix(s, suffix) { return s[:len(s)-len(suffix)] } return s } // Replace 返回字符串 s 的副本,其中 old 的前 n 个非重叠实例被 new 替换。若是 old 为空, // 则匹配字符串的开头和每一个 UTF-8 序列以后,最多产生 k+1 个替换 对于 k-rune 字符串。 // 若是 n < 0,则替换次数没有限制。 func Replace(s, old, new string, n int) string { if old == new || n == 0 { return s // avoid allocation } // 计算替换次数。 if m := Count(s, old); m == 0 { return s // avoid allocation } else if n < 0 || m < n { n = m } // 将替换应用于缓冲区。 var b Builder b.Grow(len(s) + n*(len(new)-len(old))) start := 0 for i := 0; i < n; i++ { j := start if len(old) == 0 { if i > 0 { _, wid := utf8.DecodeRuneInString(s[start:]) j += wid } } else { j += Index(s[start:], old) } b.WriteString(s[start:j]) b.WriteString(new) start = j + len(old) } b.WriteString(s[start:]) return b.String() } // ReplaceAll 返回字符串 s 的副本,其中 old 的全部非重叠实例都替换为 new。 // 若是 old 为空,则它在字符串的开头和每一个 UTF-8 序列以后匹配,为 k-rune 字符串生成最多 k+1 次替换。 func ReplaceAll(s, old, new string) string { return Replace(s, old, new, -1) } // EqualFold 报告解释为 UTF-8 字符串的 s 和 t 在 Unicode 大小写折叠下是否相等,这是不区分大小写的更通常形式。 func EqualFold(s, t string) bool { for s != "" && t != "" { // 从每一个字符串中提取第一个符文。 var sr, tr rune if s[0] < utf8.RuneSelf { sr, s = rune(s[0]), s[1:] } else { r, size := utf8.DecodeRuneInString(s) sr, s = r, s[size:] } if t[0] < utf8.RuneSelf { tr, t = rune(t[0]), t[1:] } else { r, size := utf8.DecodeRuneInString(t) tr, t = r, t[size:] } // 若是它们匹配,继续前进; 若是不是,则返回 false。 // Easy case. if tr == sr { continue } // 使 sr < tr 简化如下内容。 if tr < sr { tr, sr = sr, tr } // 快速检查 ASCII。 if tr < utf8.RuneSelf { // 仅限 ASCII,sr/tr 必须是大写/小写 if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' { continue } return false } // 通常状况。 SimpleFold(x) 返回下一个等效符文 > x 或环绕到更小的值。 r := unicode.SimpleFold(sr) for r != sr && r < tr { r = unicode.SimpleFold(r) } if r == tr { continue } return false } // One string is empty. Are both? return s == t } // Index 返回 s 中 substr 第一个实例的索引,若是 substr 不存在于 s 中,则返回 -1。 func Index(s, substr string) int { n := len(substr) switch { case n == 0: return 0 case n == 1: return IndexByte(s, substr[0]) case n == len(s): if substr == s { return 0 } return -1 case n > len(s): return -1 case n <= bytealg.MaxLen: // 当 s 和 substr 都很小时使用蛮力 if len(s) <= bytealg.MaxBruteForce { return bytealg.IndexString(s, substr) } c0 := substr[0] c1 := substr[1] i := 0 t := len(s) - n + 1 fails := 0 for i < t { if s[i] != c0 { // IndexByte 比 bytealg.IndexString 快,因此只要咱们没有获得不少误报,就使用它。 o := IndexByte(s[i+1:t], c0) if o < 0 { return -1 } i += o + 1 } if s[i+1] == c1 && s[i:i+n] == substr { return i } fails++ i++ // 当 IndexByte 产生太多误报时切换到 bytealg.IndexString。 if fails > bytealg.Cutover(i) { r := bytealg.IndexString(s[i:], substr) if r >= 0 { return r + i } return -1 } } return -1 } c0 := substr[0] c1 := substr[1] i := 0 t := len(s) - n + 1 fails := 0 for i < t { if s[i] != c0 { o := IndexByte(s[i+1:t], c0) if o < 0 { return -1 } i += o + 1 } if s[i+1] == c1 && s[i:i+n] == substr { return i } i++ fails++ if fails >= 4+i>>4 && i < t { // See comment in ../bytes/bytes.go. j := bytealg.IndexRabinKarp(s[i:], substr) if j < 0 { return -1 } return i + j } } return -1 }