// 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 }