github.com/aaabigfish/gopkg@v1.1.0/stringx/stringx.go (about) 1 // Copyright 2021 ByteDance Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package stringx 16 17 import ( 18 "bytes" 19 "errors" 20 "math" 21 "math/rand" 22 "strconv" 23 "strings" 24 "sync" 25 "unicode/utf8" 26 ) 27 28 // Error pre define 29 var ( 30 ErrDecodeRune = errors.New("error occurred on rune decoding") 31 ) 32 33 // PadLeftChar left pad a string with a specified character in a larger string (specified size). 34 // if the size is less than the param string, the param string is returned. 35 // note: size is unicode size. 36 func PadLeftChar(s string, size int, ch rune) string { 37 return padCharLeftOrRight(s, size, ch, true) 38 } 39 40 // PadLeftSpace left pad a string with space character(' ') in a larger string(specified size). 41 // if the size is less than the param string, the param string is returned. 42 // note: size is unicode size. 43 func PadLeftSpace(s string, size int) string { 44 return PadLeftChar(s, size, ' ') 45 } 46 47 // PadRightChar right pad a string with a specified character in a larger string(specified size). 48 // if the size is less than the param string, the param string is returned. 49 // note: size is unicode size. 50 func PadRightChar(s string, size int, ch rune) string { 51 return padCharLeftOrRight(s, size, ch, false) 52 } 53 54 // PadRightSpace right pad a string with space character(' ') in a large string(specified size). 55 // if the size is less than the param string, the param string is returned. 56 // note: size is unicode size. 57 func PadRightSpace(s string, size int) string { 58 return PadRightChar(s, size, ' ') 59 } 60 61 // PadCenterChar center pad a string with a specified character in a larger string(specified size). 62 // if the size is less than the param string, the param string is returned. 63 // note: size is unicode size. 64 func PadCenterChar(s string, size int, ch rune) string { 65 if size <= 0 { 66 return s 67 } 68 length := utf8.RuneCountInString(s) 69 pads := size - length 70 if pads <= 0 { 71 return s 72 } 73 74 // pad left 75 leftPads := pads / 2 76 if leftPads > 0 { 77 s = padRawLeftChar(s, ch, leftPads) 78 } 79 // pad right 80 rightPads := size - leftPads - length 81 if rightPads > 0 { 82 s = padRawRightChar(s, ch, rightPads) 83 } 84 return s 85 } 86 87 // PadCenterSpace center pad a string with space character(' ') in a larger string(specified size). 88 // if the size is less than the param string, the param string is returned. 89 // note: size is unicode size. 90 func PadCenterSpace(s string, size int) string { 91 return PadCenterChar(s, size, ' ') 92 } 93 94 func padCharLeftOrRight(s string, size int, ch rune, isLeft bool) string { 95 if size <= 0 { 96 return s 97 } 98 pads := size - utf8.RuneCountInString(s) 99 if pads <= 0 { 100 return s 101 } 102 if isLeft { 103 return padRawLeftChar(s, ch, pads) 104 } 105 return padRawRightChar(s, ch, pads) 106 } 107 108 func padRawLeftChar(s string, ch rune, padSize int) string { 109 return RepeatChar(ch, padSize) + s 110 } 111 112 func padRawRightChar(s string, ch rune, padSize int) string { 113 return s + RepeatChar(ch, padSize) 114 } 115 116 // RepeatChar returns padding using the specified delimiter repeated to a given length. 117 func RepeatChar(ch rune, repeat int) string { 118 if repeat <= 0 { 119 return "" 120 } 121 sb := strings.Builder{} 122 sb.Grow(repeat) 123 for i := 0; i < repeat; i++ { 124 sb.WriteRune(ch) 125 } 126 return sb.String() 127 } 128 129 // RemoveChar removes all occurrences of a specified character from the string. 130 func RemoveChar(s string, rmVal rune) string { 131 if s == "" { 132 return s 133 } 134 sb := strings.Builder{} 135 sb.Grow(len(s) / 2) 136 137 for _, v := range s { 138 if v != rmVal { 139 sb.WriteRune(v) 140 } 141 } 142 return sb.String() 143 } 144 145 // RemoveString removes all occurrences of a substring from the string. 146 func RemoveString(s, rmStr string) string { 147 if s == "" || rmStr == "" { 148 return s 149 } 150 return strings.ReplaceAll(s, rmStr, "") 151 } 152 153 // Rotate rotates(circular shift) a string of shift characters. 154 func Rotate(s string, shift int) string { 155 if shift == 0 { 156 return s 157 } 158 sLen := len(s) 159 if sLen == 0 { 160 return s 161 } 162 163 shiftMod := shift % sLen 164 if shiftMod == 0 { 165 return s 166 } 167 168 offset := -(shiftMod) 169 sb := strings.Builder{} 170 sb.Grow(sLen) 171 _, _ = sb.WriteString(SubStart(s, offset)) 172 _, _ = sb.WriteString(Sub(s, 0, offset)) 173 return sb.String() 174 } 175 176 // Sub returns substring from specified string avoiding panics with index start and end. 177 // start, end are based on unicode(utf8) count. 178 func Sub(s string, start, end int) string { 179 return sub(s, start, end) 180 } 181 182 // SubStart returns substring from specified string avoiding panics with start. 183 // start, end are based on unicode(utf8) count. 184 func SubStart(s string, start int) string { 185 return sub(s, start, math.MaxInt64) 186 } 187 188 func sub(s string, start, end int) string { 189 if s == "" { 190 return "" 191 } 192 193 unicodeLen := utf8.RuneCountInString(s) 194 // end 195 if end < 0 { 196 end += unicodeLen 197 } 198 if end > unicodeLen { 199 end = unicodeLen 200 } 201 // start 202 if start < 0 { 203 start += unicodeLen 204 } 205 if start > end { 206 return "" 207 } 208 209 // start <= end 210 if start < 0 { 211 start = 0 212 } 213 if end < 0 { 214 end = 0 215 } 216 if start == 0 && end == unicodeLen { 217 return s 218 } 219 220 sb := strings.Builder{} 221 sb.Grow(end - start) 222 runeIndex := 0 223 for _, v := range s { 224 if runeIndex >= end { 225 break 226 } 227 if runeIndex >= start { 228 sb.WriteRune(v) 229 } 230 runeIndex++ 231 } 232 return sb.String() 233 } 234 235 // MustReverse reverses a string, panics when error happens. 236 func MustReverse(s string) string { 237 result, err := Reverse(s) 238 if err != nil { 239 panic(err) 240 } 241 return result 242 } 243 244 // Reverse reverses a string with error status returned. 245 func Reverse(s string) (string, error) { 246 if s == "" { 247 return s, nil 248 } 249 src := S2b(s) 250 dst := make([]byte, len(s)) 251 srcIndex := len(s) 252 dstIndex := 0 253 for srcIndex > 0 { 254 r, n := utf8.DecodeLastRune(src[:srcIndex]) 255 if r == utf8.RuneError { 256 return B2s(dst), ErrDecodeRune 257 } 258 utf8.EncodeRune(dst[dstIndex:], r) 259 srcIndex -= n 260 dstIndex += n 261 } 262 return B2s(dst), nil 263 } 264 265 // Shuffle shuffles runes in a string and returns. 266 func Shuffle(s string) string { 267 if s == "" { 268 return s 269 } 270 runes := []rune(s) 271 index := 0 272 for i := len(runes) - 1; i > 0; i-- { 273 index = rand.Intn(i + 1) 274 if i != index { 275 runes[i], runes[index] = runes[index], runes[i] 276 } 277 } 278 return string(runes) 279 } 280 281 // ContainsAnySubstrings returns whether s contains any of substring in slice. 282 func ContainsAnySubstrings(s string, subs []string) bool { 283 for _, v := range subs { 284 if strings.Contains(s, v) { 285 return true 286 } 287 } 288 return false 289 } 290 291 var ( 292 bfPool = sync.Pool{ 293 New: func() interface{} { 294 return bytes.NewBuffer([]byte{}) 295 }, 296 } 297 ) 298 299 // JoinInts format int64 slice like:n1,n2,n3. 300 func JoinInts(is []int64) string { 301 if len(is) == 0 { 302 return "" 303 } 304 if len(is) == 1 { 305 return strconv.FormatInt(is[0], 10) 306 } 307 buf := bfPool.Get().(*bytes.Buffer) 308 for _, i := range is { 309 buf.WriteString(strconv.FormatInt(i, 10)) 310 buf.WriteByte(',') 311 } 312 if buf.Len() > 0 { 313 buf.Truncate(buf.Len() - 1) 314 } 315 s := buf.String() 316 buf.Reset() 317 bfPool.Put(buf) 318 return s 319 } 320 321 // SplitInts split string into int64 slice. 322 func SplitInts(s string) ([]int64, error) { 323 if s == "" { 324 return nil, nil 325 } 326 sArr := strings.Split(s, ",") 327 res := make([]int64, 0, len(sArr)) 328 for _, sc := range sArr { 329 i, err := strconv.ParseInt(sc, 10, 64) 330 if err != nil { 331 return nil, err 332 } 333 res = append(res, i) 334 } 335 return res, nil 336 }