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