| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | package strings |
| |
|
| | import ( |
| | "internal/bytealg" |
| | "internal/stringslite" |
| | "math/bits" |
| | "unicode" |
| | "unicode/utf8" |
| | ) |
| |
|
| | const maxInt = int(^uint(0) >> 1) |
| |
|
| | |
| | |
| | |
| | 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++ { |
| | _, size := utf8.DecodeRuneInString(s) |
| | a[i] = s[:size] |
| | s = s[size:] |
| | } |
| | if n > 0 { |
| | a[n-1] = s |
| | } |
| | return a |
| | } |
| |
|
| | |
| | |
| | func Count(s, substr string) int { |
| | |
| | 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):] |
| | } |
| | } |
| |
|
| | |
| | func Contains(s, substr string) bool { |
| | return Index(s, substr) >= 0 |
| | } |
| |
|
| | |
| | func ContainsAny(s, chars string) bool { |
| | return IndexAny(s, chars) >= 0 |
| | } |
| |
|
| | |
| | func ContainsRune(s string, r rune) bool { |
| | return IndexRune(s, r) >= 0 |
| | } |
| |
|
| | |
| | func ContainsFunc(s string, f func(rune) bool) bool { |
| | return IndexFunc(s, f) >= 0 |
| | } |
| |
|
| | |
| | func LastIndex(s, substr string) int { |
| | n := len(substr) |
| | switch { |
| | case n == 0: |
| | return len(s) |
| | case n == 1: |
| | return bytealg.LastIndexByteString(s, substr[0]) |
| | case n == len(s): |
| | if substr == s { |
| | return 0 |
| | } |
| | return -1 |
| | case n > len(s): |
| | return -1 |
| | } |
| | |
| | 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 |
| | } |
| |
|
| | |
| | func IndexByte(s string, c byte) int { |
| | return stringslite.IndexByte(s, c) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func IndexRune(s string, r rune) int { |
| | const haveFastIndex = bytealg.MaxBruteForce > 0 |
| | 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: |
| | |
| | |
| | |
| | rs := string(r) |
| | last := len(rs) - 1 |
| | i := last |
| | fails := 0 |
| | for i < len(s) { |
| | if s[i] != rs[last] { |
| | o := IndexByte(s[i+1:], rs[last]) |
| | if o < 0 { |
| | return -1 |
| | } |
| | i += o + 1 |
| | } |
| | |
| | for j := 1; j < len(rs); j++ { |
| | if s[i-j] != rs[last-j] { |
| | goto next |
| | } |
| | } |
| | return i - last |
| | next: |
| | fails++ |
| | i++ |
| | if (haveFastIndex && fails > bytealg.Cutover(i)) && i < len(s) || |
| | (!haveFastIndex && fails >= 4+i>>4 && i < len(s)) { |
| | goto fallback |
| | } |
| | } |
| | return -1 |
| |
|
| | fallback: |
| | |
| | if haveFastIndex { |
| | if j := bytealg.IndexString(s[i-last:], string(r)); j >= 0 { |
| | return i + j - last |
| | } |
| | } else { |
| | c0 := rs[last] |
| | c1 := rs[last-1] |
| | loop: |
| | for ; i < len(s); i++ { |
| | if s[i] == c0 && s[i-1] == c1 { |
| | for k := 2; k < len(rs); k++ { |
| | if s[i-k] != rs[last-k] { |
| | continue loop |
| | } |
| | } |
| | return i - last |
| | } |
| | } |
| | } |
| | return -1 |
| | } |
| | } |
| |
|
| | |
| | |
| | func IndexAny(s, chars string) int { |
| | if chars == "" { |
| | |
| | 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 := 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 |
| | } |
| |
|
| | |
| | |
| | |
| | func LastIndexAny(s, chars string) int { |
| | if chars == "" { |
| | |
| | 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 |
| | } |
| |
|
| | |
| | func LastIndexByte(s string, c byte) int { |
| | return bytealg.LastIndexByteString(s, c) |
| | } |
| |
|
| | |
| | |
| | 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 |
| | } |
| |
|
| | if n > len(s)+1 { |
| | n = len(s) + 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] |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func SplitAfterN(s, sep string, n int) []string { |
| | return genSplit(s, sep, len(sep), n) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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} |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func Fields(s string) []string { |
| | |
| | |
| | n := 0 |
| | wasSpace := 1 |
| | |
| | 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 { |
| | |
| | return FieldsFunc(s, unicode.IsSpace) |
| | } |
| | |
| | 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) { |
| | a[na] = s[fieldStart:] |
| | } |
| | return a |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func FieldsFunc(s string, f func(rune) bool) []string { |
| | |
| | |
| | type span struct { |
| | start int |
| | end int |
| | } |
| | spans := make([]span, 0, 32) |
| |
|
| | |
| | |
| | |
| | |
| | start := -1 |
| | for end, rune := range s { |
| | if f(rune) { |
| | if start >= 0 { |
| | spans = append(spans, span{start, end}) |
| | |
| | |
| | |
| | start = ^start |
| | } |
| | } else { |
| | if start < 0 { |
| | start = end |
| | } |
| | } |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | func Join(elems []string, sep string) string { |
| | switch len(elems) { |
| | case 0: |
| | return "" |
| | case 1: |
| | return elems[0] |
| | } |
| |
|
| | var n int |
| | if len(sep) > 0 { |
| | if len(sep) >= maxInt/(len(elems)-1) { |
| | panic("strings: Join output length overflow") |
| | } |
| | n += len(sep) * (len(elems) - 1) |
| | } |
| | for _, elem := range elems { |
| | if len(elem) > maxInt-n { |
| | panic("strings: Join output length overflow") |
| | } |
| | n += len(elem) |
| | } |
| |
|
| | var b Builder |
| | b.Grow(n) |
| | b.WriteString(elems[0]) |
| | for _, s := range elems[1:] { |
| | b.WriteString(sep) |
| | b.WriteString(s) |
| | } |
| | return b.String() |
| | } |
| |
|
| | |
| | func HasPrefix(s, prefix string) bool { |
| | return stringslite.HasPrefix(s, prefix) |
| | } |
| |
|
| | |
| | func HasSuffix(s, suffix string) bool { |
| | return stringslite.HasSuffix(s, suffix) |
| | } |
| |
|
| | |
| | |
| | |
| | func Map(mapping func(rune) rune, s string) string { |
| | |
| | |
| | |
| |
|
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | if b.Cap() == 0 { |
| | return s |
| | } |
| |
|
| | for _, c := range s { |
| | r := mapping(c) |
| |
|
| | if r >= 0 { |
| | |
| | |
| | |
| | if r < utf8.RuneSelf { |
| | b.WriteByte(byte(r)) |
| | } else { |
| | |
| | b.WriteRune(r) |
| | } |
| | } |
| | } |
| |
|
| | return b.String() |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | const ( |
| | repeatedSpaces = "" + |
| | " " + |
| | " " |
| | repeatedDashes = "" + |
| | "----------------------------------------------------------------" + |
| | "----------------------------------------------------------------" |
| | repeatedZeroes = "" + |
| | "0000000000000000000000000000000000000000000000000000000000000000" |
| | repeatedEquals = "" + |
| | "================================================================" + |
| | "================================================================" |
| | repeatedTabs = "" + |
| | "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" + |
| | "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | func Repeat(s string, count int) string { |
| | switch count { |
| | case 0: |
| | return "" |
| | case 1: |
| | return s |
| | } |
| |
|
| | |
| | |
| | |
| | if count < 0 { |
| | panic("strings: negative Repeat count") |
| | } |
| | hi, lo := bits.Mul(uint(len(s)), uint(count)) |
| | if hi > 0 || lo > uint(maxInt) { |
| | panic("strings: Repeat output length overflow") |
| | } |
| | n := int(lo) |
| |
|
| | if len(s) == 0 { |
| | return "" |
| | } |
| |
|
| | |
| | switch s[0] { |
| | case ' ', '-', '0', '=', '\t': |
| | switch { |
| | case n <= len(repeatedSpaces) && HasPrefix(repeatedSpaces, s): |
| | return repeatedSpaces[:n] |
| | case n <= len(repeatedDashes) && HasPrefix(repeatedDashes, s): |
| | return repeatedDashes[:n] |
| | case n <= len(repeatedZeroes) && HasPrefix(repeatedZeroes, s): |
| | return repeatedZeroes[:n] |
| | case n <= len(repeatedEquals) && HasPrefix(repeatedEquals, s): |
| | return repeatedEquals[:n] |
| | case n <= len(repeatedTabs) && HasPrefix(repeatedTabs, s): |
| | return repeatedTabs[:n] |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const chunkLimit = 8 * 1024 |
| | chunkMax := n |
| | if n > chunkLimit { |
| | chunkMax = chunkLimit / len(s) * len(s) |
| | if chunkMax == 0 { |
| | chunkMax = len(s) |
| | } |
| | } |
| |
|
| | var b Builder |
| | b.Grow(n) |
| | b.WriteString(s) |
| | for b.Len() < n { |
| | chunk := min(n-b.Len(), b.Len(), chunkMax) |
| | b.WriteString(b.String()[:chunk]) |
| | } |
| | return b.String() |
| | } |
| |
|
| | |
| | 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 { |
| | if !hasLower { |
| | return s |
| | } |
| | var ( |
| | b Builder |
| | pos int |
| | ) |
| | b.Grow(len(s)) |
| | for i := 0; i < len(s); i++ { |
| | c := s[i] |
| | if 'a' <= c && c <= 'z' { |
| | c -= 'a' - 'A' |
| | if pos < i { |
| | b.WriteString(s[pos:i]) |
| | } |
| | b.WriteByte(c) |
| | pos = i + 1 |
| | } |
| | } |
| | if pos < len(s) { |
| | b.WriteString(s[pos:]) |
| | } |
| | return b.String() |
| | } |
| | return Map(unicode.ToUpper, s) |
| | } |
| |
|
| | |
| | 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 { |
| | if !hasUpper { |
| | return s |
| | } |
| | var ( |
| | b Builder |
| | pos int |
| | ) |
| | b.Grow(len(s)) |
| | for i := 0; i < len(s); i++ { |
| | c := s[i] |
| | if 'A' <= c && c <= 'Z' { |
| | c += 'a' - 'A' |
| | if pos < i { |
| | b.WriteString(s[pos:i]) |
| | } |
| | b.WriteByte(c) |
| | pos = i + 1 |
| | } |
| | } |
| | if pos < len(s) { |
| | b.WriteString(s[pos:]) |
| | } |
| | return b.String() |
| | } |
| | return Map(unicode.ToLower, s) |
| | } |
| |
|
| | |
| | |
| | func ToTitle(s string) string { return Map(unicode.ToTitle, s) } |
| |
|
| | |
| | |
| | func ToUpperSpecial(c unicode.SpecialCase, s string) string { |
| | return Map(c.ToUpper, s) |
| | } |
| |
|
| | |
| | |
| | func ToLowerSpecial(c unicode.SpecialCase, s string) string { |
| | return Map(c.ToLower, s) |
| | } |
| |
|
| | |
| | |
| | func ToTitleSpecial(c unicode.SpecialCase, s string) string { |
| | return Map(c.ToTitle, s) |
| | } |
| |
|
| | |
| | |
| | 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 |
| | 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() |
| | } |
| |
|
| | |
| | |
| | func isSeparator(r rune) bool { |
| | |
| | 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) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | 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) |
| | } |
| |
|
| | |
| | |
| | func TrimLeftFunc(s string, f func(rune) bool) string { |
| | i := indexFunc(s, f, false) |
| | if i == -1 { |
| | return "" |
| | } |
| | return s[i:] |
| | } |
| |
|
| | |
| | |
| | func TrimRightFunc(s string, f func(rune) bool) string { |
| | i := lastIndexFunc(s, f, false) |
| | if i >= 0 { |
| | _, wid := utf8.DecodeRuneInString(s[i:]) |
| | i += wid |
| | } else { |
| | i++ |
| | } |
| | return s[0:i] |
| | } |
| |
|
| | |
| | |
| | func TrimFunc(s string, f func(rune) bool) string { |
| | return TrimRightFunc(TrimLeftFunc(s, f), f) |
| | } |
| |
|
| | |
| | |
| | func IndexFunc(s string, f func(rune) bool) int { |
| | return indexFunc(s, f, true) |
| | } |
| |
|
| | |
| | |
| | func LastIndexFunc(s string, f func(rune) bool) int { |
| | return lastIndexFunc(s, f, true) |
| | } |
| |
|
| | |
| | |
| | |
| | func indexFunc(s string, f func(rune) bool, truth bool) int { |
| | for i, r := range s { |
| | if f(r) == truth { |
| | return i |
| | } |
| | } |
| | return -1 |
| | } |
| |
|
| | |
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | type asciiSet [8]uint32 |
| |
|
| | |
| | |
| | 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/32] |= 1 << (c % 32) |
| | } |
| | return as, true |
| | } |
| |
|
| | |
| | func (as *asciiSet) contains(c byte) bool { |
| | return (as[c/32] & (1 << (c % 32))) != 0 |
| | } |
| |
|
| | |
| | |
| | func Trim(s, cutset string) string { |
| | if s == "" || cutset == "" { |
| | return s |
| | } |
| | if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { |
| | return trimLeftByte(trimRightByte(s, cutset[0]), cutset[0]) |
| | } |
| | if as, ok := makeASCIISet(cutset); ok { |
| | return trimLeftASCII(trimRightASCII(s, &as), &as) |
| | } |
| | return trimLeftUnicode(trimRightUnicode(s, cutset), cutset) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func TrimLeft(s, cutset string) string { |
| | if s == "" || cutset == "" { |
| | return s |
| | } |
| | if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { |
| | return trimLeftByte(s, cutset[0]) |
| | } |
| | if as, ok := makeASCIISet(cutset); ok { |
| | return trimLeftASCII(s, &as) |
| | } |
| | return trimLeftUnicode(s, cutset) |
| | } |
| |
|
| | func trimLeftByte(s string, c byte) string { |
| | for len(s) > 0 && s[0] == c { |
| | s = s[1:] |
| | } |
| | return s |
| | } |
| |
|
| | func trimLeftASCII(s string, as *asciiSet) string { |
| | for len(s) > 0 { |
| | if !as.contains(s[0]) { |
| | break |
| | } |
| | s = s[1:] |
| | } |
| | return s |
| | } |
| |
|
| | func trimLeftUnicode(s, cutset string) string { |
| | for len(s) > 0 { |
| | r, n := utf8.DecodeRuneInString(s) |
| | if !ContainsRune(cutset, r) { |
| | break |
| | } |
| | s = s[n:] |
| | } |
| | return s |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func TrimRight(s, cutset string) string { |
| | if s == "" || cutset == "" { |
| | return s |
| | } |
| | if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { |
| | return trimRightByte(s, cutset[0]) |
| | } |
| | if as, ok := makeASCIISet(cutset); ok { |
| | return trimRightASCII(s, &as) |
| | } |
| | return trimRightUnicode(s, cutset) |
| | } |
| |
|
| | func trimRightByte(s string, c byte) string { |
| | for len(s) > 0 && s[len(s)-1] == c { |
| | s = s[:len(s)-1] |
| | } |
| | return s |
| | } |
| |
|
| | func trimRightASCII(s string, as *asciiSet) string { |
| | for len(s) > 0 { |
| | if !as.contains(s[len(s)-1]) { |
| | break |
| | } |
| | s = s[:len(s)-1] |
| | } |
| | return s |
| | } |
| |
|
| | func trimRightUnicode(s, cutset string) string { |
| | for len(s) > 0 { |
| | r, n := rune(s[len(s)-1]), 1 |
| | if r >= utf8.RuneSelf { |
| | r, n = utf8.DecodeLastRuneInString(s) |
| | } |
| | if !ContainsRune(cutset, r) { |
| | break |
| | } |
| | s = s[:len(s)-n] |
| | } |
| | return s |
| | } |
| |
|
| | |
| | |
| | |
| | func TrimSpace(s string) string { |
| | |
| | for lo, c := range []byte(s) { |
| | if c >= utf8.RuneSelf { |
| | |
| | |
| | return TrimFunc(s[lo:], unicode.IsSpace) |
| | } |
| | if asciiSpace[c] != 0 { |
| | continue |
| | } |
| | s = s[lo:] |
| | |
| | for hi := len(s) - 1; hi >= 0; hi-- { |
| | c := s[hi] |
| | if c >= utf8.RuneSelf { |
| | return TrimRightFunc(s[:hi+1], unicode.IsSpace) |
| | } |
| | if asciiSpace[c] == 0 { |
| | |
| | |
| | |
| | return s[:hi+1] |
| | } |
| | } |
| | } |
| | return "" |
| | } |
| |
|
| | |
| | |
| | func TrimPrefix(s, prefix string) string { |
| | return stringslite.TrimPrefix(s, prefix) |
| | } |
| |
|
| | |
| | |
| | func TrimSuffix(s, suffix string) string { |
| | return stringslite.TrimSuffix(s, suffix) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func Replace(s, old, new string, n int) string { |
| | if old == new || n == 0 { |
| | return s |
| | } |
| |
|
| | |
| | if m := Count(s, old); m == 0 { |
| | return s |
| | } else if n < 0 || m < n { |
| | n = m |
| | } |
| |
|
| | |
| | var b Builder |
| | b.Grow(len(s) + n*(len(new)-len(old))) |
| | start := 0 |
| | if len(old) > 0 { |
| | for range n { |
| | j := start + Index(s[start:], old) |
| | b.WriteString(s[start:j]) |
| | b.WriteString(new) |
| | start = j + len(old) |
| | } |
| | } else { |
| | b.WriteString(new) |
| | for range n - 1 { |
| | _, wid := utf8.DecodeRuneInString(s[start:]) |
| | j := start + wid |
| | b.WriteString(s[start:j]) |
| | b.WriteString(new) |
| | start = j |
| | } |
| | } |
| | b.WriteString(s[start:]) |
| | return b.String() |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func ReplaceAll(s, old, new string) string { |
| | return Replace(s, old, new, -1) |
| | } |
| |
|
| | |
| | |
| | |
| | func EqualFold(s, t string) bool { |
| | |
| | i := 0 |
| | for n := min(len(s), len(t)); i < n; i++ { |
| | sr := s[i] |
| | tr := t[i] |
| | if sr|tr >= utf8.RuneSelf { |
| | goto hasUnicode |
| | } |
| |
|
| | |
| | if tr == sr { |
| | continue |
| | } |
| |
|
| | |
| | if tr < sr { |
| | tr, sr = sr, tr |
| | } |
| | |
| | if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' { |
| | continue |
| | } |
| | return false |
| | } |
| | |
| | return len(s) == len(t) |
| |
|
| | hasUnicode: |
| | s = s[i:] |
| | t = t[i:] |
| | for _, sr := range s { |
| | |
| | if len(t) == 0 { |
| | return false |
| | } |
| |
|
| | |
| | tr, size := utf8.DecodeRuneInString(t) |
| | t = t[size:] |
| |
|
| | |
| |
|
| | |
| | if tr == sr { |
| | continue |
| | } |
| |
|
| | |
| | if tr < sr { |
| | tr, sr = sr, tr |
| | } |
| | |
| | if tr < utf8.RuneSelf { |
| | |
| | if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' { |
| | continue |
| | } |
| | return false |
| | } |
| |
|
| | |
| | |
| | r := unicode.SimpleFold(sr) |
| | for r != sr && r < tr { |
| | r = unicode.SimpleFold(r) |
| | } |
| | if r == tr { |
| | continue |
| | } |
| | return false |
| | } |
| |
|
| | |
| | return len(t) == 0 |
| | } |
| |
|
| | |
| | func Index(s, substr string) int { |
| | return stringslite.Index(s, substr) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func Cut(s, sep string) (before, after string, found bool) { |
| | return stringslite.Cut(s, sep) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func CutPrefix(s, prefix string) (after string, found bool) { |
| | return stringslite.CutPrefix(s, prefix) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func CutSuffix(s, suffix string) (before string, found bool) { |
| | return stringslite.CutSuffix(s, suffix) |
| | } |
| |
|