github.com/songzhibin97/gkit@v1.2.13/sys/stringx/stringx.go (about) 1 package stringx 2 3 import ( 4 "errors" 5 "github.com/songzhibin97/gkit/internal/hack" 6 "github.com/songzhibin97/gkit/sys/fastrand" 7 "math" 8 "strings" 9 "unicode/utf8" 10 ) 11 12 // Error pre define 13 var ( 14 ErrDecodeRune = errors.New("error occurred on rune decoding") 15 ) 16 17 // PadLeftChar left pad a string with a specified character in a larger string (specified size). 18 // if the size is less than the param string, the param string is returned. 19 // note: size is unicode size. 20 func PadLeftChar(s string, size int, ch rune) string { 21 return padCharLeftOrRight(s, size, ch, true) 22 } 23 24 // PadLeftSpace left pad a string with space character(' ') in a larger string(specified size). 25 // if the size is less than the param string, the param string is returned. 26 // note: size is unicode size. 27 func PadLeftSpace(s string, size int) string { 28 return PadLeftChar(s, size, ' ') 29 } 30 31 // PadRightChar right pad a string with a specified character in a larger string(specified size). 32 // if the size is less than the param string, the param string is returned. 33 // note: size is unicode size. 34 func PadRightChar(s string, size int, ch rune) string { 35 return padCharLeftOrRight(s, size, ch, false) 36 } 37 38 // PadRightSpace right pad a string with space character(' ') in a large string(specified size). 39 // if the size is less than the param string, the param string is returned. 40 // note: size is unicode size. 41 func PadRightSpace(s string, size int) string { 42 return PadRightChar(s, size, ' ') 43 } 44 45 // PadCenterChar center pad a string with a specified character in a larger string(specified size). 46 // if the size is less than the param string, the param string is returned. 47 // note: size is unicode size. 48 func PadCenterChar(s string, size int, ch rune) string { 49 if size <= 0 { 50 return s 51 } 52 length := utf8.RuneCountInString(s) 53 pads := size - length 54 if pads <= 0 { 55 return s 56 } 57 58 // pad left 59 leftPads := pads / 2 60 if leftPads > 0 { 61 s = padRawLeftChar(s, ch, leftPads) 62 } 63 // pad right 64 rightPads := size - leftPads - length 65 if rightPads > 0 { 66 s = padRawRightChar(s, ch, rightPads) 67 } 68 return s 69 } 70 71 // PadCenterSpace center pad a string with space character(' ') in a larger string(specified size). 72 // if the size is less than the param string, the param string is returned. 73 // note: size is unicode size. 74 func PadCenterSpace(s string, size int) string { 75 return PadCenterChar(s, size, ' ') 76 } 77 78 func padCharLeftOrRight(s string, size int, ch rune, isLeft bool) string { 79 if size <= 0 { 80 return s 81 } 82 pads := size - utf8.RuneCountInString(s) 83 if pads <= 0 { 84 return s 85 } 86 if isLeft { 87 return padRawLeftChar(s, ch, pads) 88 } 89 return padRawRightChar(s, ch, pads) 90 } 91 92 func padRawLeftChar(s string, ch rune, padSize int) string { 93 return RepeatChar(ch, padSize) + s 94 } 95 96 func padRawRightChar(s string, ch rune, padSize int) string { 97 return s + RepeatChar(ch, padSize) 98 } 99 100 // RepeatChar returns padding using the specified delimiter repeated to a given length. 101 func RepeatChar(ch rune, repeat int) string { 102 if repeat <= 0 { 103 return "" 104 } 105 sb := strings.Builder{} 106 sb.Grow(repeat) 107 for i := 0; i < repeat; i++ { 108 sb.WriteRune(ch) 109 } 110 return sb.String() 111 } 112 113 // RemoveChar removes all occurrences of a specified character from the string. 114 func RemoveChar(s string, rmVal rune) string { 115 if s == "" { 116 return s 117 } 118 sb := strings.Builder{} 119 sb.Grow(len(s) / 2) 120 121 for _, v := range s { 122 if v != rmVal { 123 sb.WriteRune(v) 124 } 125 } 126 return sb.String() 127 } 128 129 // RemoveString removes all occurrences of a substring from the string. 130 func RemoveString(s, rmStr string) string { 131 if s == "" || rmStr == "" { 132 return s 133 } 134 return strings.ReplaceAll(s, rmStr, "") 135 } 136 137 // Rotate rotates(circular shift) a string of shift characters. 138 func Rotate(s string, shift int) string { 139 if shift == 0 { 140 return s 141 } 142 sLen := len(s) 143 if sLen == 0 { 144 return s 145 } 146 147 shiftMod := shift % sLen 148 if shiftMod == 0 { 149 return s 150 } 151 152 offset := -(shiftMod) 153 sb := strings.Builder{} 154 sb.Grow(sLen) 155 _, _ = sb.WriteString(SubStart(s, offset)) 156 _, _ = sb.WriteString(Sub(s, 0, offset)) 157 return sb.String() 158 } 159 160 // Sub returns substring from specified string avoiding panics with index start and end. 161 // start, end are based on unicode(utf8) count. 162 func Sub(s string, start, end int) string { 163 return sub(s, start, end) 164 } 165 166 // SubStart returns substring from specified string avoiding panics with start. 167 // start, end are based on unicode(utf8) count. 168 func SubStart(s string, start int) string { 169 return sub(s, start, math.MaxInt64) 170 } 171 172 func sub(s string, start, end int) string { 173 if s == "" { 174 return "" 175 } 176 177 unicodeLen := utf8.RuneCountInString(s) 178 // end 179 if end < 0 { 180 end += unicodeLen 181 } 182 if end > unicodeLen { 183 end = unicodeLen 184 } 185 // start 186 if start < 0 { 187 start += unicodeLen 188 } 189 if start > end { 190 return "" 191 } 192 193 // start <= end 194 if start < 0 { 195 start = 0 196 } 197 if end < 0 { 198 end = 0 199 } 200 if start == 0 && end == unicodeLen { 201 return s 202 } 203 204 sb := strings.Builder{} 205 sb.Grow(end - start) 206 runeIndex := 0 207 for _, v := range s { 208 if runeIndex >= end { 209 break 210 } 211 if runeIndex >= start { 212 sb.WriteRune(v) 213 } 214 runeIndex++ 215 } 216 return sb.String() 217 } 218 219 // MustReverse reverses a string, panics when error happens. 220 func MustReverse(s string) string { 221 result, err := Reverse(s) 222 if err != nil { 223 panic(err) 224 } 225 return result 226 } 227 228 // Reverse reverses a string with error status returned. 229 func Reverse(s string) (string, error) { 230 if s == "" { 231 return s, nil 232 } 233 src := hack.StringToBytes(s) 234 dst := make([]byte, len(s)) 235 srcIndex := len(s) 236 dstIndex := 0 237 for srcIndex > 0 { 238 r, n := utf8.DecodeLastRune(src[:srcIndex]) 239 if r == utf8.RuneError { 240 return hack.BytesToString(dst), ErrDecodeRune 241 } 242 utf8.EncodeRune(dst[dstIndex:], r) 243 srcIndex -= n 244 dstIndex += n 245 } 246 return hack.BytesToString(dst), nil 247 } 248 249 // Shuffle shuffles runes in a string and returns. 250 func Shuffle(s string) string { 251 if s == "" { 252 return s 253 } 254 runes := []rune(s) 255 index := 0 256 for i := len(runes) - 1; i > 0; i-- { 257 index = fastrand.Intn(i + 1) 258 if i != index { 259 runes[i], runes[index] = runes[index], runes[i] 260 } 261 } 262 return string(runes) 263 } 264 265 // ContainsAnySubstrings returns whether s contains any of substring in slice. 266 func ContainsAnySubstrings(s string, subs []string) bool { 267 for _, v := range subs { 268 if strings.Contains(s, v) { 269 return true 270 } 271 } 272 return false 273 }