github.com/webx-top/com@v1.2.12/string.go (about) 1 // Copyright 2013 com authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // 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, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 package com 16 17 import ( 18 "bytes" 19 "crypto/hmac" 20 "crypto/md5" 21 "crypto/rand" 22 "crypto/sha1" 23 "crypto/sha256" 24 "encoding/base64" 25 "encoding/gob" 26 "encoding/hex" 27 "encoding/json" 28 "fmt" 29 "hash" 30 "io" 31 r "math/rand" 32 "os" 33 "regexp" 34 "strconv" 35 "strings" 36 "time" 37 "unicode" 38 "unsafe" 39 40 "golang.org/x/text/cases" 41 "golang.org/x/text/language" 42 ) 43 44 func Str2bytes(s string) []byte { 45 x := (*[2]uintptr)(unsafe.Pointer(&s)) 46 h := [3]uintptr{x[0], x[1], x[1]} 47 return *(*[]byte)(unsafe.Pointer(&h)) 48 } 49 50 func Bytes2str(b []byte) string { 51 return *(*string)(unsafe.Pointer(&b)) 52 } 53 54 // Md5 md5 hash string 55 func Md5(str string) string { 56 m := md5.New() 57 io.WriteString(m, str) 58 return hex.EncodeToString(m.Sum(nil)) 59 } 60 61 func ByteMd5(b []byte) string { 62 m := md5.New() 63 m.Write(b) 64 return hex.EncodeToString(m.Sum(nil)) 65 } 66 67 func Md5file(file string) string { 68 barray, _ := os.ReadFile(file) 69 return ByteMd5(barray) 70 } 71 72 func Token(key string, val []byte, args ...string) string { 73 hm := hmac.New(sha1.New, []byte(key)) 74 hm.Write(val) 75 for _, v := range args { 76 hm.Write([]byte(v)) 77 } 78 return base64.URLEncoding.EncodeToString(hm.Sum(nil)) 79 } 80 81 func Token256(key string, val []byte, args ...string) string { 82 hm := hmac.New(sha256.New, []byte(key)) 83 hm.Write(val) 84 for _, v := range args { 85 hm.Write([]byte(v)) 86 } 87 return base64.URLEncoding.EncodeToString(hm.Sum(nil)) 88 } 89 90 func Encode(data interface{}, args ...string) ([]byte, error) { 91 if len(args) > 0 && args[0] == `JSON` { 92 return JSONEncode(data) 93 } 94 return GobEncode(data) 95 } 96 97 func Decode(data []byte, to interface{}, args ...string) error { 98 if len(args) > 0 && args[0] == `JSON` { 99 return JSONDecode(data, to) 100 } 101 return GobDecode(data, to) 102 } 103 104 func GobEncode(data interface{}) ([]byte, error) { 105 var buf bytes.Buffer 106 enc := gob.NewEncoder(&buf) 107 err := enc.Encode(&data) 108 if err != nil { 109 return nil, err 110 } 111 return buf.Bytes(), nil 112 } 113 114 func GobDecode(data []byte, to interface{}) error { 115 buf := bytes.NewBuffer(data) 116 dec := gob.NewDecoder(buf) 117 return dec.Decode(to) 118 } 119 120 func JSONEncode(data interface{}, indents ...string) ([]byte, error) { 121 if len(indents) > 0 && len(indents[0]) > 0 { 122 return json.MarshalIndent(data, ``, indents[0]) 123 } 124 return json.Marshal(data) 125 } 126 127 func JSONDecode(data []byte, to interface{}) error { 128 return json.Unmarshal(data, to) 129 } 130 131 func sha(m hash.Hash, str string) string { 132 io.WriteString(m, str) 133 return hex.EncodeToString(m.Sum(nil)) 134 } 135 136 // Sha1 sha1 hash string 137 func Sha1(str string) string { 138 return sha(sha1.New(), str) 139 } 140 141 // Sha256 sha256 hash string 142 func Sha256(str string) string { 143 return sha(sha256.New(), str) 144 } 145 146 // Ltrim trim space on left 147 func Ltrim(str string) string { 148 return strings.TrimLeftFunc(str, unicode.IsSpace) 149 } 150 151 // Rtrim trim space on right 152 func Rtrim(str string) string { 153 return strings.TrimRightFunc(str, unicode.IsSpace) 154 } 155 156 // Trim trim space in all string length 157 func Trim(str string) string { 158 return strings.TrimSpace(str) 159 } 160 161 // StrRepeat repeat string times 162 func StrRepeat(str string, times int) string { 163 return strings.Repeat(str, times) 164 } 165 166 // StrReplace replace find all occurs to string 167 func StrReplace(str string, find string, to string) string { 168 return strings.Replace(str, find, to, -1) 169 } 170 171 // IsLetter returns true if the 'l' is an English letter. 172 func IsLetter(l uint8) bool { 173 n := (l | 0x20) - 'a' 174 if n >= 0 && n < 26 { 175 return true 176 } 177 return false 178 } 179 180 // Expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match. 181 func Expand(template string, match map[string]string, subs ...string) string { 182 var p []byte 183 var i int 184 for { 185 i = strings.Index(template, "{") 186 if i < 0 { 187 break 188 } 189 p = append(p, template[:i]...) 190 template = template[i+1:] 191 i = strings.Index(template, "}") 192 if s, ok := match[template[:i]]; ok { 193 p = append(p, s...) 194 } else { 195 j, _ := strconv.Atoi(template[:i]) 196 if j >= len(subs) { 197 p = append(p, []byte("Missing")...) 198 } else { 199 p = append(p, subs[j]...) 200 } 201 } 202 template = template[i+1:] 203 } 204 p = append(p, template...) 205 return string(p) 206 } 207 208 // Reverse s string, support unicode 209 func Reverse(s string) string { 210 n := len(s) 211 runes := make([]rune, n) 212 for _, rune := range s { 213 n-- 214 runes[n] = rune 215 } 216 return string(runes[n:]) 217 } 218 219 // RandomCreateBytes generate random []byte by specify chars. 220 func RandomCreateBytes(n int, alphabets ...byte) []byte { 221 const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 222 var bytes = make([]byte, n) 223 var randby bool 224 if num, err := rand.Read(bytes); num != n || err != nil { 225 r.Seed(time.Now().UnixNano()) 226 randby = true 227 } 228 for i, b := range bytes { 229 if len(alphabets) == 0 { 230 if randby { 231 bytes[i] = alphanum[r.Intn(len(alphanum))] 232 } else { 233 bytes[i] = alphanum[b%byte(len(alphanum))] 234 } 235 } else { 236 if randby { 237 bytes[i] = alphabets[r.Intn(len(alphabets))] 238 } else { 239 bytes[i] = alphabets[b%byte(len(alphabets))] 240 } 241 } 242 } 243 return bytes 244 } 245 246 // Substr returns the substr from start to length. 247 func Substr(s string, dot string, lengthAndStart ...int) string { 248 var start, length, argsLen, ln int 249 argsLen = len(lengthAndStart) 250 if argsLen > 0 { 251 length = lengthAndStart[0] 252 } 253 if argsLen > 1 { 254 start = lengthAndStart[1] 255 } 256 bt := []rune(s) 257 if start < 0 { 258 start = 0 259 } 260 ln = len(bt) 261 if start > ln { 262 start = start % ln 263 } 264 end := start + length 265 if end > (ln - 1) { 266 end = ln 267 } 268 if dot == "" || end == ln { 269 return string(bt[start:end]) 270 } 271 return string(bt[start:end]) + dot 272 } 273 274 func IsASCIIUpper(r rune) bool { 275 return 'A' <= r && r <= 'Z' 276 } 277 278 func IsUpperLetter(r rune) bool { 279 return IsASCIIUpper(r) 280 } 281 282 func ToASCIIUpper(r rune) rune { 283 if 'a' <= r && r <= 'z' { 284 r -= ('a' - 'A') 285 } 286 return r 287 } 288 289 func ToUpperLetter(r rune) rune { 290 return ToASCIIUpper(r) 291 } 292 293 func StrIsASCIIUpper(s string) bool { 294 if len(s) == 0 { 295 return false 296 } 297 for _, r := range s { 298 if !IsASCIIUpper(r) { 299 return false 300 } 301 } 302 return true 303 } 304 305 func StrIsUpperLetter(s string) bool { 306 return StrIsASCIIUpper(s) 307 } 308 309 func IsAlpha(r rune) bool { 310 if ('Z' < r || r < 'A') && ('z' < r || r < 'a') { 311 return false 312 } 313 return true 314 } 315 316 func StrIsLetter(s string) bool { 317 return StrIsAlpha(s) 318 } 319 320 func StrIsAlpha(s string) bool { 321 if len(s) == 0 { 322 return false 323 } 324 for _, r := range s { 325 if !IsAlpha(r) { 326 return false 327 } 328 } 329 return true 330 } 331 332 func IsAlphaNumeric(r rune) bool { 333 if ('Z' < r || r < 'A') && ('z' < r || r < 'a') && ('9' < r || r < '0') { 334 return false 335 } 336 return true 337 } 338 339 func StrIsAlphaNumeric(s string) bool { 340 if len(s) == 0 { 341 return false 342 } 343 for _, r := range s { 344 if !IsAlphaNumeric(r) { 345 return false 346 } 347 } 348 return true 349 } 350 351 func IsNumeric(r rune) bool { 352 if '9' < r || r < '0' { 353 return false 354 } 355 return true 356 } 357 358 func StrIsNumeric(s string) bool { 359 if len(s) == 0 { 360 return false 361 } 362 for _, r := range s { 363 if !IsNumeric(r) { 364 return false 365 } 366 } 367 return true 368 } 369 370 var titleCaser = cases.Title(language.Und, cases.NoLower) 371 372 func Title(v string) string { 373 return titleCaser.String(v) 374 } 375 376 // GonicCase : webxTop => webx_top 377 func GonicCase(name string) string { 378 s := make([]rune, 0, len(name)+3) 379 for idx, chr := range name { 380 if IsASCIIUpper(chr) && idx > 0 { 381 if !IsASCIIUpper(s[len(s)-1]) { 382 s = append(s, '_') 383 } 384 } 385 if !IsASCIIUpper(chr) && idx > 1 { 386 l := len(s) 387 if IsASCIIUpper(s[l-1]) && IsASCIIUpper(s[l-2]) { 388 s = append(s, s[l-1]) 389 s[l-1] = '_' 390 } 391 } 392 s = append(s, chr) 393 } 394 return strings.ToLower(string(s)) 395 } 396 397 // TitleCase : webx_top => Webx_Top 398 func TitleCase(name string) string { 399 var s []rune 400 upNextChar := true 401 name = strings.ToLower(name) 402 for _, chr := range name { 403 switch { 404 case upNextChar: 405 upNextChar = false 406 chr = ToASCIIUpper(chr) 407 case chr == '_', chr == ' ': 408 upNextChar = true 409 } 410 s = append(s, chr) 411 } 412 return string(s) 413 } 414 415 // SnakeCase : WebxTop => webx_top 416 func SnakeCase(name string) string { 417 var s []rune 418 for idx, chr := range name { 419 if isUpper := IsASCIIUpper(chr); isUpper { 420 if idx > 0 { 421 s = append(s, '_') 422 } 423 chr -= ('A' - 'a') 424 } 425 s = append(s, chr) 426 } 427 return string(s) 428 } 429 430 // CamelCase : webx_top => webxTop 431 func CamelCase(s string) string { 432 var n string 433 var capNext bool 434 for _, v := range s { 435 if v >= 'a' && v <= 'z' { 436 if capNext { 437 n += strings.ToUpper(string(v)) 438 capNext = false 439 } else { 440 n += string(v) 441 } 442 continue 443 } 444 if v == '_' || v == ' ' { 445 capNext = true 446 } else { 447 capNext = false 448 n += string(v) 449 } 450 } 451 return n 452 } 453 454 // PascalCase : webx_top => WebxTop 455 func PascalCase(s string) string { 456 var n string 457 capNext := true 458 for _, v := range s { 459 if v >= 'a' && v <= 'z' { 460 if capNext { 461 n += strings.ToUpper(string(v)) 462 capNext = false 463 } else { 464 n += string(v) 465 } 466 continue 467 } 468 if v == '_' || v == ' ' { 469 capNext = true 470 } else { 471 capNext = false 472 n += string(v) 473 } 474 } 475 return n 476 } 477 478 // UpperCaseFirst : webx => Webx 479 func UpperCaseFirst(name string) string { 480 s := []rune(name) 481 if len(s) > 0 { 482 s[0] = unicode.ToUpper(s[0]) 483 name = string(s) 484 } 485 return name 486 } 487 488 // LowerCaseFirst : WEBX => wEBX 489 func LowerCaseFirst(name string) string { 490 s := []rune(name) 491 if len(s) > 0 { 492 s[0] = unicode.ToLower(s[0]) 493 name = string(s) 494 } 495 return name 496 } 497 498 func AddSlashes(s string, args ...rune) string { 499 b := []rune{'\''} 500 if len(args) > 0 { 501 b = append(b, args...) 502 } 503 return AddCSlashes(s, b...) 504 } 505 506 func AddCSlashes(s string, b ...rune) string { 507 var builder strings.Builder 508 for _, v := range s { 509 if v == '\\' { 510 builder.WriteRune(v) 511 } else { 512 for _, f := range b { 513 if v == f { 514 builder.WriteRune('\\') 515 break 516 } 517 } 518 } 519 builder.WriteRune(v) 520 } 521 return builder.String() 522 } 523 524 func AddRSlashes(s string) string { 525 var builder strings.Builder 526 for _, c := range s { 527 switch c { 528 case '\n': 529 builder.WriteRune('\\') 530 builder.WriteRune('n') 531 case '\r': 532 builder.WriteRune('\\') 533 builder.WriteRune('r') 534 case '\t': 535 builder.WriteRune('\\') 536 builder.WriteRune('t') 537 default: 538 builder.WriteRune(c) 539 } 540 } 541 return builder.String() 542 } 543 544 func StripSlashes(s string) string { 545 var builder strings.Builder 546 size := len(s) 547 var skip bool 548 for i, ch := range s { 549 if skip { 550 builder.WriteRune(ch) 551 skip = false 552 continue 553 } 554 if ch == '\\' { 555 if i+1 < size && s[i+1] == '\\' { 556 skip = true 557 } 558 continue 559 } 560 builder.WriteRune(ch) 561 } 562 return builder.String() 563 } 564 565 // MaskString 0123456789 => 012****789 566 func MaskString(v string, width ...float64) string { 567 size := len(v) 568 if size < 1 { 569 return `` 570 } 571 if size == 1 { 572 return `*` 573 } 574 show := 0.3 575 if len(width) > 0 { 576 show = width[0] 577 } 578 showSize := int(float64(size) * show) 579 if showSize < 1 { 580 showSize = 1 581 } 582 hideSize := size - showSize*2 583 rights := showSize + hideSize 584 if rights > 0 && hideSize > 0 && rights < size && showSize < size { 585 return v[0:showSize] + strings.Repeat(`*`, hideSize) + v[rights:] 586 } 587 if show < 0.5 { 588 showSize = int(float64(size) * 0.5) 589 if showSize < 1 { 590 showSize = 1 591 } 592 hideSize = size - showSize 593 if hideSize > 0 && showSize < size { 594 return v[0:showSize] + strings.Repeat(`*`, hideSize) 595 } 596 } 597 return v[0:1] + strings.Repeat(`*`, size-1) 598 } 599 600 // LeftPadZero 字符串指定长度,长度不足的时候左边补零 601 func LeftPadZero(input string, padLength int) string { 602 return fmt.Sprintf(`%0*s`, padLength, input) 603 } 604 605 var ( 606 reSpaceLine = regexp.MustCompile("([\\t\\s\r]*\n){2,}") 607 BreakLine = []byte("\n") 608 BreakLineString = StrLF 609 ) 610 611 func CleanSpaceLine(b []byte) []byte { 612 return reSpaceLine.ReplaceAll(b, BreakLine) 613 } 614 615 func CleanSpaceLineString(b string) string { 616 return reSpaceLine.ReplaceAllString(b, BreakLineString) 617 }