github.com/searKing/golang/go@v1.2.117/strings/strings.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package strings 6 7 import ( 8 "strings" 9 "unicode" 10 "unicode/utf8" 11 12 bytes_ "github.com/searKing/golang/go/bytes" 13 unicode_ "github.com/searKing/golang/go/unicode" 14 ) 15 16 // ContainsRuneAnyFunc reports whether any of the Unicode code point r satisfying f(r) is within s. 17 func ContainsRuneAnyFunc(s string, f func(rune) bool) bool { 18 if f == nil { 19 return false 20 } 21 for _, r := range s { 22 if f(r) { 23 return true 24 } 25 } 26 return false 27 } 28 29 // ContainsRuneOnlyFunc reports whether all of the Unicode code point r satisfying f(r) is within s. 30 func ContainsRuneOnlyFunc(s string, f func(rune) bool) bool { 31 if f == nil { 32 return true 33 } 34 for _, r := range s { 35 if !f(r) { 36 return false 37 } 38 } 39 return true 40 } 41 42 // ContainsAnyRangeTable reports whether the string contains any rune in any of the specified table of ranges. 43 func ContainsAnyRangeTable(s string, rangeTabs ...*unicode.RangeTable) bool { 44 if len(rangeTabs) == 0 { 45 return ContainsRuneAnyFunc(s, nil) 46 } 47 return ContainsRuneAnyFunc(s, func(r rune) bool { 48 for _, t := range rangeTabs { 49 if t == nil { 50 continue 51 } 52 if unicode.Is(t, r) { 53 return true 54 } 55 } 56 return false 57 }) 58 } 59 60 // ContainsOnlyRangeTable reports whether the string contains only rune in all of the specified table of ranges. 61 func ContainsOnlyRangeTable(s string, rangeTabs ...*unicode.RangeTable) bool { 62 if len(rangeTabs) == 0 { 63 return ContainsRuneOnlyFunc(s, nil) 64 } 65 return ContainsRuneOnlyFunc(s, func(r rune) bool { 66 for _, t := range rangeTabs { 67 if t == nil { 68 continue 69 } 70 if !unicode.Is(t, r) { 71 return false 72 } 73 } 74 return true 75 }) 76 } 77 78 // ContainsAsciiVisual reports whether the string contains any rune in visual ascii code, that is [0x21, 0x7E]. 79 func ContainsAsciiVisual(s string) bool { 80 return ContainsAnyRangeTable(s, unicode_.AsciiVisual) 81 } 82 83 // ContainsAsciiVisual reports whether the string contains only rune in visual ascii code, that is [0x21, 0x7E]. 84 func ContainsOnlyAsciiVisual(s string) bool { 85 return ContainsOnlyRangeTable(s, unicode_.AsciiVisual) 86 } 87 88 // JoinRepeat behaves like strings.Join([]string{s,...,s}, sep) 89 func JoinRepeat(s string, sep string, n int) string { 90 var b strings.Builder 91 for i := 0; i < n-1; i++ { 92 b.WriteString(s) 93 b.WriteString(sep) 94 } 95 if n > 0 { 96 b.WriteString(s) 97 } 98 return b.String() 99 } 100 101 // MapLeading returns a copy of the string s with its first characters modified 102 // according to the mapping function. If mapping returns a negative value, the character is 103 // dropped from the string with no replacement. 104 func MapLeading(mapping func(rune) rune, s string) string { 105 if s == "" { 106 return s 107 } 108 rLeading, sRight := ExtractFirstRune(s) 109 srMapped := mapping(rLeading) 110 if srMapped < 0 { 111 return sRight 112 } 113 114 // Fast path for unchanged input 115 if rLeading == srMapped { 116 return s 117 } 118 return string(srMapped) + sRight 119 } 120 121 // ToLowerLeading returns s with it's first Unicode letter mapped to their lower case. 122 func ToLowerLeading(s string) string { 123 return MapLeading(unicode.ToLower, s) 124 } 125 126 // ToUpperLeading returns s with it's first Unicode letter mapped to their upper case. 127 func ToUpperLeading(s string) string { 128 return MapLeading(unicode.ToUpper, s) 129 } 130 131 // Truncate shrinks s's len to n at most 132 func Truncate(s string, n int) string { 133 if n < 0 { 134 n = 0 135 } 136 if len(s) <= n { 137 return s 138 } 139 return s[:n] 140 } 141 142 // PadLeft returns s padded to length n, padded left with repeated pad 143 // return s directly if pad is empty 144 // padding s with {{pad}} and spaces(less than len(pad)) as a prefix, as [pad]...[pad][space]...[space][s] 145 func PadLeft(s string, pad string, n int) string { 146 if len(pad) == 0 { 147 return s 148 } 149 150 pc, sc := ComputePad(s, pad, n) 151 152 return strings.Repeat(pad, pc) + strings.Repeat(" ", sc) + s 153 } 154 155 // PadRight returns s padded to length n, padded right with repeated pad 156 // return s directly if pad is empty 157 // padding s with {{pad}} and spaces(less than len(pad)) as a suffix, as [s][space]...[space][pad]...[pad] 158 func PadRight(s string, pad string, n int) string { 159 if len(pad) == 0 { 160 return s 161 } 162 pc, sc := ComputePad(s, pad, n) 163 164 return s + strings.Repeat(" ", sc) + strings.Repeat(pad, pc) 165 } 166 167 // ComputePad returns pad's count and space's count(less than len(pad)) will be need to pad s to len n 168 // padCount = (n-len(s))/len(pad) 169 // spaceCount = (n-len(s))%len(pad) 170 func ComputePad(s string, pad string, n int) (padCount, spaceCount int) { 171 return bytes_.ComputePad([]byte(s), []byte(pad), n) 172 } 173 174 // ReverseByByte returns a string with the bytes of s in reverse order. 175 func ReverseByByte(s string) string { 176 var b strings.Builder 177 b.Grow(len(s)) 178 for i := len(s) - 1; i >= 0; i-- { 179 b.WriteByte(s[i]) 180 } 181 return b.String() 182 } 183 184 // ReverseByRune returns a string with the runes of s in reverse order. 185 // Invalid UTF-8 sequences, if any, will be reversed byte by byte. 186 func ReverseByRune(s string) string { 187 res := make([]byte, len(s)) 188 prevPos, resPos := 0, len(s) 189 for pos := range s { 190 resPos -= pos - prevPos 191 copy(res[resPos:], s[prevPos:pos]) 192 prevPos = pos 193 } 194 copy(res[0:], s[prevPos:]) 195 return string(res) 196 } 197 198 // CountPrefix counts the number of non-overlapping instances of continuous substr prefix in s. 199 // If substr is an empty string, CountPrefix returns 1 + the number of Unicode code points in s. 200 func CountPrefix(s, substr string) int { 201 // special case 202 if len(substr) == 0 { 203 return utf8.RuneCountInString(s) + 1 204 } 205 n := 0 206 gap := len(substr) 207 for i := 0; i+gap <= len(s); i += gap { 208 if s[i:i+gap] == substr { 209 n++ 210 continue 211 } 212 break 213 } 214 return n 215 } 216 217 // CountSuffix counts the number of non-overlapping instances of continuous substr suffix in s. 218 // If substr is an empty string, CountSuffix returns 1 + the number of Unicode code points in s. 219 func CountSuffix(s, substr string) int { 220 // special case 221 if len(substr) == 0 { 222 return utf8.RuneCountInString(s) + 1 223 } 224 n := 0 225 gap := len(substr) 226 for i := len(s); i-gap >= 0; i -= gap { 227 if s[i-gap:i] == substr { 228 n++ 229 continue 230 } 231 break 232 } 233 return n 234 }