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