github.com/PandaGoAdmin/utils@v0.0.0-20211208134815-d5461603a00f/string.go (about) 1 package kgo 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 crand "crypto/rand" 7 "crypto/rsa" 8 "crypto/x509" 9 "encoding/base64" 10 "encoding/gob" 11 "encoding/hex" 12 "encoding/json" 13 "encoding/pem" 14 "errors" 15 "fmt" 16 "github.com/json-iterator/go" 17 xhtml "golang.org/x/net/html" 18 "golang.org/x/text/encoding/simplifiedchinese" 19 "golang.org/x/text/encoding/traditionalchinese" 20 "golang.org/x/text/transform" 21 "golang.org/x/text/width" 22 "golang.org/x/text/cases" 23 "golang.org/x/text/language" 24 "hash/crc32" 25 "html" 26 "io" 27 "io/ioutil" 28 "math" 29 "math/rand" 30 "net" 31 "net/http" 32 "net/url" 33 "regexp" 34 "strconv" 35 "strings" 36 "time" 37 "unicode" 38 "unicode/utf8" 39 ) 40 41 // Md5 获取字节切片md5值. 42 // length指定结果长度,默认32. 43 func (ks *LkkString) Md5Byte(str []byte, length ...uint8) []byte { 44 var l uint8 = 32 45 if len(length) > 0 { 46 l = length[0] 47 } 48 49 return md5Byte(str, l) 50 } 51 52 // Md5 获取字符串md5值. 53 // length指定结果长度,默认32. 54 func (ks *LkkString) Md5(str string, length ...uint8) string { 55 var l uint8 = 32 56 if len(length) > 0 { 57 l = length[0] 58 } 59 60 return string(md5Byte([]byte(str), l)) 61 } 62 63 // IsMd5 是否md5值. 64 func (ks *LkkString) IsMd5(str string) bool { 65 return str != "" && RegMd5.MatchString(str) 66 } 67 68 // ShaXByte 计算字节切片的 shaX 散列值,x为1/256/512 . 69 func (ks *LkkString) ShaXByte(str []byte, x uint16) []byte { 70 return shaXByte(str, x) 71 } 72 73 // ShaX 计算字符串的 shaX 散列值,x为1/256/512 . 74 func (ks *LkkString) ShaX(str string, x uint16) string { 75 return string(shaXByte([]byte(str), x)) 76 } 77 78 // IsSha1 是否Sha1值. 79 func (ks *LkkString) IsSha1(str string) bool { 80 return str != "" && RegSha1.MatchString(str) 81 } 82 83 // IsSha256 是否Sha256值. 84 func (ks *LkkString) IsSha256(str string) bool { 85 return str != "" && RegSha256.MatchString(str) 86 } 87 88 // IsSha512 是否Sha512值. 89 func (ks *LkkString) IsSha512(str string) bool { 90 return str != "" && RegSha512.MatchString(str) 91 } 92 93 // Index 查找子串sub在字符串str中第一次出现的位置,不存在则返回-1; 94 // ignoreCase为是否忽略大小写. 95 func (ks *LkkString) Index(str, sub string, ignoreCase bool) int { 96 if str == "" || sub == "" { 97 return -1 98 } 99 100 if ignoreCase { 101 str = strings.ToLower(str) 102 sub = strings.ToLower(sub) 103 } 104 105 return strings.Index(str, sub) 106 } 107 108 // LastIndex 查找子串sub在字符串str中最后一次出现的位置,不存在则返回-1; 109 // ignoreCase为是否忽略大小写. 110 func (ks *LkkString) LastIndex(str, sub string, ignoreCase bool) int { 111 if str == "" || sub == "" { 112 return -1 113 } 114 115 if ignoreCase { 116 str = strings.ToLower(str) 117 sub = strings.ToLower(sub) 118 } 119 120 return strings.LastIndex(str, sub) 121 } 122 123 // Addslashes 使用反斜线引用字符串. 124 func (ks *LkkString) Addslashes(str string) string { 125 var buf bytes.Buffer 126 for _, char := range str { 127 switch char { 128 case '\'', '"', '\\': 129 buf.WriteRune('\\') 130 } 131 buf.WriteRune(char) 132 } 133 return buf.String() 134 } 135 136 // Stripslashes 反引用一个引用字符串. 137 func (ks *LkkString) Stripslashes(str string) string { 138 var buf bytes.Buffer 139 l, skip := len(str), false 140 for i, char := range str { 141 if skip { 142 skip = false 143 } else if char == '\\' { 144 if i+1 < l && str[i+1] == '\\' { 145 skip = true 146 } 147 continue 148 } 149 buf.WriteRune(char) 150 } 151 return buf.String() 152 } 153 154 // JsonEncode 对val变量进行 JSON 编码. 155 // 依赖库github.com/json-iterator/go. 156 func (ks *LkkString) JsonEncode(val interface{}) ([]byte, error) { 157 var jsons = jsoniter.ConfigCompatibleWithStandardLibrary 158 return jsons.Marshal(val) 159 } 160 161 // JsonDecode 对 JSON 格式的str字符串进行解码,注意res使用指针. 162 // 依赖库github.com/json-iterator/go. 163 func (ks *LkkString) JsonDecode(str []byte, res interface{}) error { 164 var jsons = jsoniter.ConfigCompatibleWithStandardLibrary 165 return jsons.Unmarshal(str, res) 166 } 167 168 // Utf8ToGbk UTF-8转GBK编码. 169 func (ks *LkkString) Utf8ToGbk(s []byte) ([]byte, error) { 170 reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder()) 171 d, e := io.ReadAll(reader) 172 return d, e 173 } 174 175 // GbkToUtf8 GBK转UTF-8编码. 176 func (ks *LkkString) GbkToUtf8(s []byte) ([]byte, error) { 177 reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder()) 178 d, e := io.ReadAll(reader) 179 return d, e 180 } 181 182 // IsUtf8 字符串是否UTF-8编码. 183 func (ks *LkkString) IsUtf8(s []byte) bool { 184 return utf8.Valid(s) 185 } 186 187 // IsGbk 字符串是否GBK编码. 188 func (ks *LkkString) IsGbk(s []byte) (res bool) { 189 length := len(s) 190 var i, j int 191 for i < length { 192 j = i + 1 193 //大于127的使用双字节编码,且落在gbk编码范围内的字符 194 //GBK中每个汉字包含两个字节,第一个字节(首字节)的范围是0x81-0xFE(即129-254),第二个字节(尾字节)的范围是0x40-0xFE(即64-254) 195 if s[i] > 0x7f && j < length { 196 if s[i] >= 0x81 && 197 s[i] <= 0xfe && 198 s[j] >= 0x40 && 199 s[j] <= 0xfe { 200 i += 2 201 res = true 202 } else { 203 res = false 204 break 205 } 206 } else { 207 i++ 208 } 209 } 210 211 return 212 } 213 214 // Img2Base64 将图片字节转换为base64字符串.ext为图片扩展名,默认jpg. 215 func (ks *LkkString) Img2Base64(content []byte, ext ...string) string { 216 var imgType string = "jpg" 217 if len(ext) > 0 { 218 imgType = strings.ToLower(ext[0]) 219 } 220 221 return img2Base64(content, imgType) 222 } 223 224 // StartsWith 字符串str是否以sub开头. 225 func (ks *LkkString) StartsWith(str, sub string, ignoreCase bool) bool { 226 if str != "" && sub != "" { 227 i := ks.Index(str, sub, ignoreCase) 228 return i == 0 229 } 230 231 return false 232 } 233 234 // StartsWiths 字符串str是否以subs其中之一为开头. 235 func (ks *LkkString) StartsWiths(str string, subs []string, ignoreCase bool) (res bool) { 236 for _, sub := range subs { 237 res = ks.StartsWith(str, sub, ignoreCase) 238 if res { 239 break 240 } 241 } 242 return 243 } 244 245 // EndsWith 字符串str是否以sub结尾. 246 func (ks *LkkString) EndsWith(str, sub string, ignoreCase bool) bool { 247 if str != "" && sub != "" { 248 i := ks.LastIndex(str, sub, ignoreCase) 249 return i != -1 && (len(str)-len(sub)) == i 250 } 251 252 return false 253 } 254 255 // EndsWiths 字符串str是否以subs其中之一为结尾. 256 func (ks *LkkString) EndsWiths(str string, subs []string, ignoreCase bool) (res bool) { 257 for _, sub := range subs { 258 res = ks.EndsWith(str, sub, ignoreCase) 259 if res { 260 break 261 } 262 } 263 return 264 } 265 266 // Trim 去除字符串首尾处的空白字符(或者其他字符). 267 // characterMask为要修剪的字符. 268 func (ks *LkkString) Trim(str string, characterMask ...string) string { 269 mask := getTrimMask(characterMask) 270 return strings.Trim(str, mask) 271 } 272 273 // Ltrim 删除字符串开头的空白字符(或其他字符). 274 // characterMask为要修剪的字符. 275 func (ks *LkkString) Ltrim(str string, characterMask ...string) string { 276 mask := getTrimMask(characterMask) 277 return strings.TrimLeft(str, mask) 278 } 279 280 // Rtrim 删除字符串末端的空白字符(或者其他字符). 281 // characterMask为要修剪的字符. 282 func (ks *LkkString) Rtrim(str string, characterMask ...string) string { 283 mask := getTrimMask(characterMask) 284 return strings.TrimRight(str, mask) 285 } 286 287 // TrimBOM 移除字符串中的BOM 288 func (ks *LkkString) TrimBOM(str []byte) []byte { 289 return bytes.Trim(str, bomChars) 290 } 291 292 // IsEmpty 字符串是否为空(包括空格). 293 func (ks *LkkString) IsEmpty(str string) bool { 294 if str == "" || len(ks.Trim(str)) == 0 { 295 return true 296 } 297 298 return false 299 } 300 301 // IsLetters 字符串是否全(英文)字母组成. 302 func (ks *LkkString) IsLetters(str string) bool { 303 for _, r := range str { 304 if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') { 305 return false 306 } 307 } 308 return str != "" 309 } 310 311 // IsUpper 字符串是否全部大写. 312 func (ks *LkkString) IsUpper(str string) bool { 313 for _, r := range str { 314 if !unicode.IsUpper(r) && unicode.IsLetter(r) { 315 return false 316 } 317 } 318 return str != "" 319 } 320 321 // IsLower 字符串是否全部小写. 322 func (ks *LkkString) IsLower(str string) bool { 323 for _, r := range str { 324 if !unicode.IsLower(r) && unicode.IsLetter(r) { 325 return false 326 } 327 } 328 return str != "" 329 } 330 331 // HasLetter 字符串是否含有(英文)字母. 332 func (ks *LkkString) HasLetter(str string) bool { 333 for _, r := range str { 334 if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') { 335 return true 336 } 337 } 338 return false 339 } 340 341 // IsASCII 是否ASCII字符串. 342 func (ks *LkkString) IsASCII(str string) bool { 343 //return str != "" && RegAscii.MatchString(str) 344 n := len(str) 345 for i := 0; i < n; i++ { 346 if str[i] > 127 { 347 return false 348 } 349 } 350 351 return str != "" 352 } 353 354 // IsMultibyte 字符串是否含有多字节字符. 355 func (ks *LkkString) IsMultibyte(str string) bool { 356 return str != "" && RegMultiByte.MatchString(str) 357 } 358 359 // HasFullWidth 是否含有全角字符. 360 func (ks *LkkString) HasFullWidth(str string) bool { 361 return str != "" && RegFullWidth.MatchString(str) 362 } 363 364 // HasHalfWidth 是否含有半角字符. 365 func (ks *LkkString) HasHalfWidth(str string) bool { 366 return str != "" && RegHalfWidth.MatchString(str) 367 } 368 369 // IsEnglish 字符串是否纯英文.letterCase是否检查大小写,枚举值(CASE_NONE,CASE_LOWER,CASE_UPPER). 370 func (ks *LkkString) IsEnglish(str string, letterCase LkkCaseSwitch) bool { 371 switch letterCase { 372 case CASE_LOWER: 373 return str != "" && RegAlphaLower.MatchString(str) 374 case CASE_UPPER: 375 return str != "" && RegAlphaUpper.MatchString(str) 376 default: //CASE_NONE 377 return ks.IsLetters(str) 378 } 379 } 380 381 // HasEnglish 是否含有英文字符,HasLetter的别名. 382 func (ks *LkkString) HasEnglish(str string) bool { 383 return ks.HasLetter(str) 384 } 385 386 // HasChinese 字符串是否含有中文. 387 func (ks *LkkString) HasChinese(str string) bool { 388 for _, r := range str { 389 if unicode.Is(unicode.Scripts["Han"], r) { 390 return true 391 } 392 } 393 394 return false 395 } 396 397 // IsChinese 字符串是否全部中文. 398 func (ks *LkkString) IsChinese(str string) bool { 399 return str != "" && RegChineseAll.MatchString(str) 400 } 401 402 // IsChineseName 字符串是否中文名称. 403 func (ks *LkkString) IsChineseName(str string) bool { 404 return str != "" && RegChineseName.MatchString(str) 405 } 406 407 // IsWord 是否词语(不以下划线开头的中文、英文、数字、下划线). 408 func (ks *LkkString) IsWord(str string) bool { 409 return str != "" && RegWord.MatchString(str) 410 } 411 412 // IsNumeric 字符串是否数值(不包含复数和科学计数法). 413 func (ks *LkkString) IsNumeric(str string) bool { 414 if str == "" { 415 return false 416 } 417 _, err := strconv.ParseFloat(str, 64) 418 return err == nil 419 } 420 421 // IsAlphaNumeric 是否字母或数字. 422 func (ks *LkkString) IsAlphaNumeric(str string) bool { 423 return str != "" && RegAlphaNumeric.MatchString(str) 424 } 425 426 // HasSpecialChar 字符串是否含有特殊字符. 427 func (ks *LkkString) HasSpecialChar(str string) bool { 428 for _, r := range str { 429 // IsPunct 判断 r 是否为一个标点字符 (类别 P) 430 // IsSymbol 判断 r 是否为一个符号字符 431 // IsMark 判断 r 是否为一个 mark 字符 (类别 M) 432 if unicode.IsPunct(r) || unicode.IsSymbol(r) || unicode.IsMark(r) { 433 return true 434 } 435 } 436 437 return false 438 } 439 440 // IsJSON 字符串是否合法的json格式. 441 func (ks *LkkString) IsJSON(str string) bool { 442 length := len(str) 443 if length == 0 { 444 return false 445 } else if (str[0] != '{' || str[length-1] != '}') && (str[0] != '[' || str[length-1] != ']') { 446 return false 447 } 448 449 var js json.RawMessage 450 return json.Unmarshal([]byte(str), &js) == nil 451 } 452 453 // IsIP 检查字符串是否IP地址. 454 func (ks *LkkString) IsIP(str string) bool { 455 return str != "" && net.ParseIP(str) != nil 456 } 457 458 // IsIPv4 检查字符串是否IPv4地址. 459 func (ks *LkkString) IsIPv4(str string) bool { 460 ipAddr := net.ParseIP(str) 461 // 不是合法的IP地址 462 if ipAddr == nil { 463 return false 464 } 465 466 return ipAddr.To4() != nil && strings.ContainsRune(str, '.') 467 } 468 469 // IsIPv6 检查字符串是否IPv6地址. 470 func (ks *LkkString) IsIPv6(str string) bool { 471 ipAddr := net.ParseIP(str) 472 return ipAddr != nil && strings.ContainsRune(str, ':') 473 } 474 475 // IsDNSName 是否DNS名称. 476 func (ks *LkkString) IsDNSName(str string) bool { 477 if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 { 478 // constraints already violated 479 return false 480 } 481 return !ks.IsIP(str) && RegDNSname.MatchString(str) 482 } 483 484 // IsHost 字符串是否主机名(IP或DNS名称). 485 func (ks *LkkString) IsHost(str string) bool { 486 return ks.IsIP(str) || ks.IsDNSName(str) 487 } 488 489 // IsDialAddr 是否网络拨号地址(形如127.0.0.1:80),用于net.Dial()检查. 490 func (ks *LkkString) IsDialAddr(str string) bool { 491 h, p, err := net.SplitHostPort(str) 492 if err == nil && h != "" && p != "" && (ks.IsDNSName(h) || ks.IsIP(h)) && isPort(p) { 493 return true 494 } 495 496 return false 497 } 498 499 // IsMACAddr 是否MAC物理网卡地址. 500 func (ks *LkkString) IsMACAddr(str string) bool { 501 _, err := net.ParseMAC(str) 502 return err == nil 503 } 504 505 // IsEmail 检查字符串是否邮箱.参数validateTrue,是否验证邮箱主机的真实性. 506 func (ks *LkkString) IsEmail(email string, validateHost bool) (bool, error) { 507 //长度检查 508 length := len(email) 509 at := strings.LastIndexByte(email, '@') 510 if (length < 6 || length > 254) || (at <= 0 || at > length-3) { 511 return false, fmt.Errorf("[IsEmail] invalid email length") 512 } 513 514 //验证邮箱格式 515 chkFormat := RegEmail.MatchString(email) 516 if !chkFormat { 517 return false, fmt.Errorf("[IsEmail] invalid email format") 518 } 519 520 //验证主机 521 if validateHost { 522 host := email[at+1:] 523 if _, err := net.LookupMX(host); err != nil { 524 //因无法确定mx主机的smtp端口,所以去掉Hello/Mail/Rcpt检查邮箱是否存在 525 //仅检查主机是否有效 526 if _, err := net.LookupIP(host); err != nil { 527 return false, err 528 } 529 } 530 } 531 532 return true, nil 533 } 534 535 // IsMobilecn 检查字符串是否中国大陆手机号. 536 func (ks *LkkString) IsMobilecn(str string) bool { 537 return str != "" && RegMobilecn.MatchString(str) 538 } 539 540 // IsTel 是否固定电话或400/800电话. 541 func (ks *LkkString) IsTel(str string) bool { 542 return str != "" && RegTelephone.MatchString(str) 543 } 544 545 // IsPhone 是否电话号码(手机或固话). 546 func (ks *LkkString) IsPhone(str string) bool { 547 return str != "" && RegPhone.MatchString(str) 548 } 549 550 // IsCreditNo 检查是否(15或18位)身份证号码,并返回经校验的号码. 551 func (ks *LkkString) IsCreditNo(str string) (bool, string) { 552 chk := str != "" && RegCreditno.MatchString(str) 553 if !chk { 554 return false, "" 555 } 556 557 // 检查省份代码 558 if _, chk = CreditArea[str[0:2]]; !chk { 559 return false, "" 560 } 561 562 // 将15位身份证升级到18位 563 leng := len(str) 564 if leng == 15 { 565 // 先转为17位,如果身份证顺序码是996 997 998 999,这些是为百岁以上老人的特殊编码 566 if chk, _ = ks.Dstrpos(str[12:], []string{"996", "997", "998", "999"}, false); chk { 567 str = str[0:6] + "18" + str[6:] 568 } else { 569 str = str[0:6] + "19" + str[6:] 570 } 571 572 // 再加上校验码 573 code := append([]byte{}, creditChecksum(str)) 574 str += string(code) 575 } 576 577 // 检查生日 578 birthday := str[6:10] + "-" + str[10:12] + "-" + str[12:14] 579 chk, tim := KTime.IsDate2time(birthday) 580 now := KTime.UnixTime() 581 if !chk { 582 return false, "" 583 } else if tim >= now { 584 return false, "" 585 } 586 587 // 18位身份证需要验证最后一位校验位 588 if leng == 18 { 589 str = strings.ToUpper(str) 590 if str[17] != creditChecksum(str) { 591 return false, "" 592 } 593 } 594 595 return true, str 596 } 597 598 // IsHexcolor 检查是否十六进制颜色,并返回带"#"的修正值. 599 func (ks *LkkString) IsHexcolor(str string) (bool, string) { 600 chk := str != "" && RegHexcolor.MatchString(str) 601 if chk && !strings.ContainsRune(str, '#') { 602 str = "#" + strings.ToUpper(str) 603 } 604 return chk, str 605 } 606 607 // IsRGBcolor 检查字符串是否RGB颜色格式. 608 func (ks *LkkString) IsRGBcolor(str string) bool { 609 return str != "" && RegRgbcolor.MatchString(str) 610 } 611 612 // IsBlank 是否空(空白)字符串. 613 func (ks *LkkString) IsBlank(str string) bool { 614 // Check length 615 if len(str) > 0 { 616 // Iterate string 617 for i := range str { 618 // Check about char different from whitespace 619 // 227为全角空格 620 if str[i] > 32 && str[i] != 227 { 621 return false 622 } 623 } 624 } 625 return true 626 } 627 628 // IsWhitespaces 是否全部空白字符,不包括空字符串. 629 func (ks *LkkString) IsWhitespaces(str string) bool { 630 return str != "" && RegWhitespaceAll.MatchString(str) 631 } 632 633 // HasWhitespace 是否带有空白字符. 634 func (ks *LkkString) HasWhitespace(str string) bool { 635 return str != "" && RegWhitespaceHas.MatchString(str) 636 } 637 638 // IsBase64 是否base64字符串. 639 func (ks *LkkString) IsBase64(str string) bool { 640 return str != "" && RegBase64.MatchString(str) 641 } 642 643 // IsBase64Image 是否base64编码的图片. 644 func (ks *LkkString) IsBase64Image(str string) bool { 645 if str == "" || !strings.ContainsRune(str, ',') { 646 return false 647 } 648 649 dataURI := strings.Split(str, ",") 650 return RegBase64Image.MatchString(dataURI[0]) && RegBase64.MatchString(dataURI[1]) 651 } 652 653 // IsRsaPublicKey 检查字符串是否RSA的公钥,keylen为密钥长度. 654 func (ks *LkkString) IsRsaPublicKey(str string, keylen uint16) bool { 655 bb := bytes.NewBufferString(str) 656 pemBytes, _ := ioutil.ReadAll(bb) 657 658 // 获取公钥 659 block, _ := pem.Decode(pemBytes) 660 if block != nil && block.Type != "PUBLIC KEY" { 661 return false 662 } 663 var der []byte 664 var err error 665 666 if block != nil { 667 der = block.Bytes 668 } else { 669 der, err = base64.StdEncoding.DecodeString(str) 670 if err != nil { 671 return false 672 } 673 } 674 675 key, err := x509.ParsePKIXPublicKey(der) 676 if err != nil { 677 return false 678 } 679 pubkey, ok := key.(*rsa.PublicKey) 680 if !ok { 681 return false 682 } 683 bitlen := len(pubkey.N.Bytes()) * 8 684 return bitlen == int(keylen) 685 } 686 687 // IsUrl 检查字符串是否URL. 688 func (ks *LkkString) IsUrl(str string) bool { 689 if str == "" || len(str) <= 3 || utf8.RuneCountInString(str) >= 2083 || strings.HasPrefix(str, ".") { 690 return false 691 } 692 693 res, err := url.ParseRequestURI(str) 694 if err != nil { 695 return false //Couldn't even parse the url 696 } 697 if len(res.Scheme) == 0 { 698 return false //No Scheme found 699 } 700 701 return true 702 } 703 704 // IsUrlExists 检查URL是否存在. 705 func (ks *LkkString) IsUrlExists(str string) bool { 706 if !ks.IsUrl(str) { 707 return false 708 } 709 710 client := &http.Client{} 711 resp, err := client.Head(str) 712 if err != nil { 713 return false 714 } else if resp.StatusCode == 404 { 715 return false 716 } 717 718 return true 719 } 720 721 // Jsonp2Json 将jsonp转为json串. 722 // Example: forbar({a:"1",b:2}) to {"a":"1","b":2} 723 func (ks *LkkString) Jsonp2Json(str string) (string, error) { 724 start := strings.Index(str, "(") 725 end := strings.LastIndex(str, ")") 726 727 if start == -1 || end == -1 { 728 return "", errors.New("[Jsonp2Json] invalid jsonp.") 729 } 730 731 start += 1 732 if start >= end { 733 return "", errors.New("[Jsonp2Json] invalid jsonp.") 734 } 735 736 res := str[start:end] 737 738 return res, nil 739 } 740 741 // Strpos 查找字符串首次出现的位置,找不到时返回-1. 742 // haystack在该字符串中进行查找,needle要查找的字符串; 743 // offset起始位置,为负数时时,搜索会从字符串结尾指定字符数开始. 744 func (ks *LkkString) Strpos(haystack, needle string, offset int) int { 745 length := len(haystack) 746 if length == 0 || offset > length || -offset > length { 747 return -1 748 } 749 750 if offset < 0 { 751 offset += length 752 } 753 pos := strings.Index(haystack[offset:], needle) 754 if pos == -1 { 755 return -1 756 } 757 return pos + offset 758 } 759 760 // Stripos 查找字符串首次出现的位置(不区分大小写),找不到时返回-1. 761 // haystack在该字符串中进行查找,needle要查找的字符串; 762 // offset起始位置,为负数时时,搜索会从字符串结尾指定字符数开始. 763 func (ks *LkkString) Stripos(haystack, needle string, offset int) int { 764 length := len(haystack) 765 if length == 0 || offset > length || -offset > length { 766 return -1 767 } 768 769 if offset < 0 { 770 offset += length 771 } 772 pos := ks.Index(haystack[offset:], needle, true) 773 if pos == -1 { 774 return -1 775 } 776 return pos + offset 777 } 778 779 // Strrpos 查找指定字符串在目标字符串中最后一次出现的位置. 780 func (ks *LkkString) Strrpos(haystack, needle string, offset int) int { 781 pos, length := 0, len(haystack) 782 if length == 0 || offset > length || -offset > length { 783 return -1 784 } 785 786 if offset < 0 { 787 haystack = haystack[:offset+length+1] 788 } else { 789 haystack = haystack[offset:] 790 } 791 pos = strings.LastIndex(haystack, needle) 792 if offset > 0 && pos != -1 { 793 pos += offset 794 } 795 return pos 796 } 797 798 // Strripos 查找指定字符串在目标字符串中最后一次出现的位置(不区分大小写). 799 func (ks *LkkString) Strripos(haystack, needle string, offset int) int { 800 pos, length := 0, len(haystack) 801 if length == 0 || offset > length || -offset > length { 802 return -1 803 } 804 805 if offset < 0 { 806 haystack = haystack[:offset+length+1] 807 } else { 808 haystack = haystack[offset:] 809 } 810 pos = ks.LastIndex(haystack, needle, true) 811 if offset > 0 && pos != -1 { 812 pos += offset 813 } 814 return pos 815 } 816 817 // Dstrpos 检查字符串str是否包含数组arr的元素之一,返回检查结果和匹配的字符串. 818 // chkCase为是否检查大小写. 819 func (ks *LkkString) Dstrpos(str string, arr []string, chkCase bool) (bool, string) { 820 if len(str) == 0 || len(arr) == 0 { 821 return false, "" 822 } 823 824 for _, v := range arr { 825 if (chkCase && ks.Strpos(str, v, 0) != -1) || (!chkCase && ks.Stripos(str, v, 0) != -1) { 826 return true, v 827 } 828 } 829 830 return false, "" 831 } 832 833 // Nl2br 将换行符转换为br标签. 834 func (ks *LkkString) Nl2br(str string) string { 835 return strings.Replace(str, "\n", "<br />", -1) 836 } 837 838 // Br2nl 将br标签转换为换行符. 839 func (ks *LkkString) Br2nl(str string) string { 840 // <br> , <br /> , <br/> 841 // <BR> , <BR /> , <BR/> 842 843 l := len(str) 844 buf := make([]byte, 0, l) //prealloca 845 846 for i := 0; i < l; i++ { 847 switch str[i] { 848 case 60: //< 849 if l >= i+3 { 850 /* 851 b = 98 852 B = 66 853 r = 82 854 R = 114 855 SPACE = 32 856 / = 47 857 > = 62 858 */ 859 860 if l >= i+3 && ((str[i+1] == 98 || str[i+1] == 66) && (str[i+2] == 82 || str[i+2] == 114) && str[i+3] == 62) { // <br> || <BR> 861 buf = append(buf, bytLinefeed...) 862 i += 3 863 continue 864 } 865 866 if l >= i+4 && ((str[i+1] == 98 || str[i+1] == 66) && (str[i+2] == 82 || str[i+2] == 114) && str[i+3] == 47 && str[i+4] == 62) { // <br/> || <BR/> 867 buf = append(buf, bytLinefeed...) 868 i += 4 869 continue 870 } 871 872 if l >= i+5 && ((str[i+1] == 98 || str[i+1] == 66) && (str[i+2] == 82 || str[i+2] == 114) && str[i+3] == 32 && str[i+4] == 47 && str[i+5] == 62) { // <br /> || <BR /> 873 buf = append(buf, bytLinefeed...) 874 i += 5 875 continue 876 } 877 } 878 default: 879 buf = append(buf, str[i]) 880 } 881 } 882 883 return string(buf) 884 } 885 886 // RemoveSpace 移除字符串中的空白字符. 887 // all为true时移除全部空白,为false时只替换连续的空白字符为一个空格. 888 func (ks *LkkString) RemoveSpace(str string, all bool) string { 889 if all && str != "" { 890 return strings.Join(strings.Fields(str), "") 891 } else if str != "" { 892 //先将2个以上的连续空白符转为空格 893 str = RegWhitespaceDuplicate.ReplaceAllString(str, " ") 894 //再将[\t\n\f\r]等转为空格 895 str = RegWhitespace.ReplaceAllString(str, " ") 896 } 897 898 return strings.TrimSpace(str) 899 } 900 901 // StripTags 过滤html标签. 902 func (ks *LkkString) StripTags(str string) string { 903 return RegHtmlTag.ReplaceAllString(str, "") 904 } 905 906 // Html2Text 将html转换为纯文本. 907 func (ks *LkkString) Html2Text(str string) string { 908 domDoc := xhtml.NewTokenizer(strings.NewReader(str)) 909 previousStartToken := domDoc.Token() 910 var text string 911 loopDom: 912 for { 913 nx := domDoc.Next() 914 switch { 915 case nx == xhtml.ErrorToken: 916 break loopDom // End of the document 917 case nx == xhtml.StartTagToken: 918 previousStartToken = domDoc.Token() 919 case nx == xhtml.TextToken: 920 if chk, _ := ks.Dstrpos(previousStartToken.Data, TextHtmlExcludeTags, false); chk { 921 continue 922 } 923 924 text += " " + strings.TrimSpace(xhtml.UnescapeString(string(domDoc.Text()))) 925 } 926 } 927 928 return ks.RemoveSpace(text, false) 929 } 930 931 // ParseStr 将URI查询字符串转换为字典. 932 func (ks *LkkString) ParseStr(encodedString string, result map[string]interface{}) error { 933 // split encodedString. 934 if encodedString[0] == '?' { 935 encodedString = strings.TrimLeft(encodedString, "?") 936 } 937 938 parts := strings.Split(encodedString, "&") 939 for _, part := range parts { 940 pos := strings.Index(part, "=") 941 if pos <= 0 { 942 continue 943 } 944 key, err := url.QueryUnescape(part[:pos]) 945 if err != nil { 946 return err 947 } 948 for key[0] == ' ' && key[1:] != "" { 949 key = key[1:] 950 } 951 if key == "" || key[0] == '[' { 952 continue 953 } 954 value, err := url.QueryUnescape(part[pos+1:]) 955 if err != nil { 956 return err 957 } 958 959 // split into multiple keys 960 var keys []string 961 left := 0 962 for i, k := range key { 963 if k == '[' && left == 0 { 964 left = i 965 } else if k == ']' { 966 if left > 0 { 967 if len(keys) == 0 { 968 keys = append(keys, key[:left]) 969 } 970 keys = append(keys, key[left+1:i]) 971 left = 0 972 if i+1 < len(key) && key[i+1] != '[' { 973 break 974 } 975 } 976 } 977 } 978 if len(keys) == 0 { 979 keys = append(keys, key) 980 } 981 // first key 982 first := "" 983 for i, chr := range keys[0] { 984 if chr == ' ' || chr == '.' || chr == '[' { 985 first += "_" 986 } else { 987 first += string(chr) 988 } 989 if chr == '[' { 990 first += keys[0][i+1:] 991 break 992 } 993 } 994 keys[0] = first 995 996 // build nested map 997 if err := buildQueryMap(result, keys, value); err != nil { 998 return err 999 } 1000 } 1001 1002 return nil 1003 } 1004 1005 // ParseUrl 解析URL,返回其组成部分. 1006 // component为需要返回的组成; 1007 // -1: all; 1: scheme; 2: host; 4: port; 8: user; 16: pass; 32: path; 64: query; 128: fragment . 1008 func (ks *LkkString) ParseUrl(str string, component int16) (map[string]string, error) { 1009 u, err := url.Parse(str) 1010 if err != nil { 1011 return nil, err 1012 } 1013 if component == -1 { 1014 component = 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 1015 } 1016 var res = make(map[string]string) 1017 if (component & 1) == 1 { 1018 res["scheme"] = u.Scheme 1019 } 1020 if (component & 2) == 2 { 1021 res["host"] = u.Hostname() 1022 } 1023 if (component & 4) == 4 { 1024 res["port"] = u.Port() 1025 } 1026 if (component & 8) == 8 { 1027 res["user"] = u.User.Username() 1028 } 1029 if (component & 16) == 16 { 1030 res["pass"], _ = u.User.Password() 1031 } 1032 if (component & 32) == 32 { 1033 res["path"] = u.Path 1034 } 1035 if (component & 64) == 64 { 1036 res["query"] = u.RawQuery 1037 } 1038 if (component & 128) == 128 { 1039 res["fragment"] = u.Fragment 1040 } 1041 return res, nil 1042 } 1043 1044 // UrlEncode 编码 URL 字符串. 1045 func (ks *LkkString) UrlEncode(str string) string { 1046 return url.QueryEscape(str) 1047 } 1048 1049 // UrlDecode 解码已编码的 URL 字符串. 1050 func (ks *LkkString) UrlDecode(str string) (string, error) { 1051 return url.QueryUnescape(str) 1052 } 1053 1054 // RawUrlEncode 按照 RFC 3986 对 URL 进行编码. 1055 func (ks *LkkString) RawUrlEncode(str string) string { 1056 return strings.Replace(url.QueryEscape(str), "+", "%20", -1) 1057 } 1058 1059 // RawUrlDecode 对已编码的 URL 字符串进行解码. 1060 func (ks *LkkString) RawUrlDecode(str string) (string, error) { 1061 return url.QueryUnescape(strings.Replace(str, "%20", "+", -1)) 1062 } 1063 1064 // HttpBuildQuery 根据参数生成 URL-encode 之后的请求字符串. 1065 func (ks *LkkString) HttpBuildQuery(queryData url.Values) string { 1066 return queryData.Encode() 1067 } 1068 1069 // FormatUrl 格式化URL. 1070 func (ks *LkkString) FormatUrl(str string) string { 1071 if str != "" { 1072 if ks.Strpos(str, "://", 0) == -1 { 1073 str = "http://" + str 1074 } 1075 1076 // 将"\"替换为"/" 1077 str = strings.ReplaceAll(str, "\\", "/") 1078 1079 // 将连续的"//"或"\\"或"\/",替换为"/" 1080 str = RegUrlBackslashDuplicate.ReplaceAllString(str, "$1/") 1081 } 1082 1083 return str 1084 } 1085 1086 // GetDomain 从URL字符串中获取域名. 1087 // 可选参数isMain,默认为false,取完整域名;为true时,取主域名(如abc.test.com取test.com). 1088 func (ks *LkkString) GetDomain(str string, isMain ...bool) string { 1089 str = ks.FormatUrl(str) 1090 u, err := url.Parse(str) 1091 main := false 1092 if len(isMain) > 0 { 1093 main = isMain[0] 1094 } 1095 1096 if err != nil || !strings.Contains(str, ".") { 1097 return "" 1098 } else if !main { 1099 return u.Hostname() 1100 } 1101 1102 parts := strings.Split(u.Hostname(), ".") 1103 domain := parts[len(parts)-2] + "." + parts[len(parts)-1] 1104 1105 return domain 1106 } 1107 1108 // ClearUrlPrefix 清除URL的前缀; 1109 // str为URL字符串,prefix为前缀,默认"/". 1110 func (ks *LkkString) ClearUrlPrefix(str string, prefix ...string) string { 1111 var p string = "/" 1112 if len(prefix) > 0 { 1113 p = prefix[0] 1114 } 1115 1116 for p != "" && strings.HasPrefix(str, p) { 1117 str = str[len(p):] 1118 } 1119 1120 return str 1121 } 1122 1123 // ClearUrlSuffix 清除URL的后缀; 1124 // str为URL字符串,suffix为后缀,默认"/". 1125 func (ks *LkkString) ClearUrlSuffix(str string, suffix ...string) string { 1126 var s string = "/" 1127 if len(suffix) > 0 { 1128 s = suffix[0] 1129 } 1130 1131 for s != "" && strings.HasSuffix(str, s) { 1132 str = str[0 : len(str)-len(s)] 1133 } 1134 1135 return str 1136 } 1137 1138 // Random 生成随机字符串. 1139 // length为长度,rtype为枚举: 1140 // RAND_STRING_ALPHA 字母; 1141 // RAND_STRING_NUMERIC 数值; 1142 // RAND_STRING_ALPHANUM 字母+数值; 1143 // RAND_STRING_SPECIAL 字母+数值+特殊字符; 1144 // RAND_STRING_CHINESE 仅中文. 1145 func (ks *LkkString) Random(length uint8, rtype LkkRandString) string { 1146 if length == 0 { 1147 return "" 1148 } 1149 1150 var letter []rune 1151 alphas := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 1152 numbers := "0123456789" 1153 specials := "~!@#$%^&*()_+{}:|<>?`-=;,." 1154 1155 rand.Seed(time.Now().UTC().UnixNano()) 1156 1157 switch rtype { 1158 case RAND_STRING_ALPHA: 1159 letter = []rune(alphas) 1160 case RAND_STRING_NUMERIC: 1161 letter = []rune(numbers) 1162 case RAND_STRING_ALPHANUM: 1163 letter = []rune(alphas + numbers) 1164 case RAND_STRING_SPECIAL: 1165 letter = []rune(alphas + numbers + specials) 1166 case RAND_STRING_CHINESE: 1167 letter = CommonChinese 1168 default: 1169 letter = []rune(alphas) 1170 } 1171 1172 res := make([]rune, length) 1173 for i := range res { 1174 res[i] = letter[rand.Intn(len(letter))] 1175 } 1176 1177 return string(res) 1178 } 1179 1180 // DetectEncoding 匹配字符编码,TODO. 1181 func (ks *LkkString) DetectEncoding(str string) (res string) { 1182 //TODO 检查字符编码 1183 return 1184 } 1185 1186 // Ucfirst 将字符串的第一个字符转换为大写. 1187 func (ks *LkkString) Ucfirst(str string) string { 1188 for _, v := range str { 1189 u := string(unicode.ToUpper(v)) 1190 return u + str[len(u):] 1191 } 1192 return "" 1193 } 1194 1195 // Lcfirst 将字符串的第一个字符转换为小写. 1196 func (ks *LkkString) Lcfirst(str string) string { 1197 for _, v := range str { 1198 u := string(unicode.ToLower(v)) 1199 return u + str[len(u):] 1200 } 1201 return "" 1202 } 1203 1204 // Ucwords 将字符串中每个词的首字母转换为大写. 1205 func (ks *LkkString) Ucwords(str string) string { 1206 caser := cases.Title(language.English) 1207 return caser.String(str) 1208 } 1209 1210 // Lcwords 将字符串中每个词的首字母转换为小写. 1211 func (ks *LkkString) Lcwords(str string) string { 1212 buf := &bytes.Buffer{} 1213 lastIsSpace := true 1214 for _, r := range str { 1215 if unicode.IsLetter(r) { 1216 if lastIsSpace { 1217 r = unicode.ToLower(r) 1218 } 1219 1220 lastIsSpace = false 1221 } else { 1222 lastIsSpace = false 1223 if unicode.IsSpace(r) || unicode.IsPunct(r) || unicode.IsSymbol(r) || unicode.IsMark(r) { 1224 lastIsSpace = true 1225 } 1226 } 1227 1228 buf.WriteRune(r) 1229 } 1230 1231 return buf.String() 1232 } 1233 1234 // Substr 截取字符串str的子串. 1235 // start 为起始位置.若值是负数,返回的结果将从 str 结尾处向前数第 abs(start) 个字符开始. 1236 // length 为截取的长度.若值时负数, str 末尾处的 abs(length) 个字符将会被省略. 1237 // start/length的绝对值必须<=原字符串长度. 1238 func (ks *LkkString) Substr(str string, start int, length ...int) string { 1239 total := len(str) 1240 if total == 0 { 1241 return "" 1242 } 1243 1244 var sublen, end int 1245 max := total //最大的结束位置 1246 1247 if len(length) == 0 { 1248 sublen = total 1249 } else { 1250 sublen = length[0] 1251 } 1252 1253 if start < 0 { 1254 start = total + start 1255 } 1256 1257 if sublen < 0 { 1258 sublen = total + sublen 1259 if sublen > 0 { 1260 max = sublen 1261 } 1262 } 1263 1264 if start < 0 || sublen <= 0 || start >= max { 1265 return "" 1266 } 1267 1268 end = start + sublen 1269 if end > max { 1270 end = max 1271 } 1272 1273 return str[start:end] 1274 } 1275 1276 // MbSubstr 返回(宽字符)字符串str的子串. 1277 // start 为起始位置.若值是负数,返回的结果将从 str 结尾处向前数第 abs(start) 个字符开始. 1278 // length 为截取的长度.若值时负数, str 末尾处的 abs(length) 个字符将会被省略. 1279 // start/length的绝对值必须<=原字符串长度. 1280 func (ks *LkkString) MbSubstr(str string, start int, length ...int) string { 1281 if len(str) == 0 { 1282 return "" 1283 } 1284 1285 runes := []rune(str) 1286 total := len(runes) 1287 1288 var sublen, end int 1289 max := total //最大的结束位置 1290 1291 if len(length) == 0 { 1292 sublen = total 1293 } else { 1294 sublen = length[0] 1295 } 1296 1297 if start < 0 { 1298 start = total + start 1299 } 1300 1301 if sublen < 0 { 1302 sublen = total + sublen 1303 if sublen > 0 { 1304 max = sublen 1305 } 1306 } 1307 1308 if start < 0 || sublen <= 0 || start >= max { 1309 return "" 1310 } 1311 1312 end = start + sublen 1313 if end > max { 1314 end = max 1315 } 1316 1317 return string(runes[start:end]) 1318 } 1319 1320 // SubstrCount 计算子串substr在字符串str中出现的次数,区分大小写. 1321 func (ks *LkkString) SubstrCount(str, substr string) int { 1322 return strings.Count(str, substr) 1323 } 1324 1325 // SubstriCount 计算子串substr在字符串str中出现的次数,忽略大小写. 1326 func (ks *LkkString) SubstriCount(str, substr string) int { 1327 return strings.Count(strings.ToLower(str), strings.ToLower(substr)) 1328 } 1329 1330 // Reverse 反转字符串. 1331 func (ks *LkkString) Reverse(str string) string { 1332 n := len(str) 1333 runes := make([]rune, n) 1334 for _, r := range str { 1335 n-- 1336 runes[n] = r 1337 } 1338 return string(runes[n:]) 1339 } 1340 1341 // ChunkSplit 将字符串分割成小块.str为要分割的字符,chunklen为分割的尺寸,end为行尾序列符号. 1342 func (ks *LkkString) ChunkSplit(str string, chunklen uint, end string) string { 1343 if end == "" { 1344 return str 1345 } 1346 1347 runes, erunes := []rune(str), []rune(end) 1348 length := uint(len(runes)) 1349 if length <= 1 || length < chunklen { 1350 return str + end 1351 } 1352 ns := make([]rune, 0, len(runes)+len(erunes)) 1353 var i uint 1354 for i = 0; i < length; i += chunklen { 1355 if i+chunklen > length { 1356 ns = append(ns, runes[i:]...) 1357 } else { 1358 ns = append(ns, runes[i:i+chunklen]...) 1359 } 1360 ns = append(ns, erunes...) 1361 } 1362 return string(ns) 1363 } 1364 1365 // Strlen 获取字符串长度. 1366 func (ks *LkkString) Strlen(str string) int { 1367 return len(str) 1368 } 1369 1370 // MbStrlen 获取宽字符串的长度,多字节的字符被计为 1. 1371 func (ks *LkkString) MbStrlen(str string) int { 1372 return utf8.RuneCountInString(str) 1373 } 1374 1375 // Shuffle 随机打乱字符串. 1376 func (ks *LkkString) Shuffle(str string) string { 1377 if str == "" { 1378 return str 1379 } 1380 1381 runes := []rune(str) 1382 index := 0 1383 1384 for i := len(runes) - 1; i > 0; i-- { 1385 index = rand.Intn(i + 1) 1386 1387 if i != index { 1388 runes[i], runes[index] = runes[index], runes[i] 1389 } 1390 } 1391 1392 return string(runes) 1393 } 1394 1395 // Ord 将首字符转换为rune(ASCII值). 1396 // 注意:当字符串为空时返回65533. 1397 func (ks *LkkString) Ord(char string) rune { 1398 r, _ := utf8.DecodeRune([]byte(char)) 1399 return r 1400 } 1401 1402 // Chr 返回相对应于 ASCII 所指定的单个字符. 1403 func (ks *LkkString) Chr(chr uint) string { 1404 return string(rune(chr)) 1405 } 1406 1407 // Serialize 对变量进行序列化. 1408 func (ks *LkkString) Serialize(val interface{}) ([]byte, error) { 1409 buf := bytes.Buffer{} 1410 enc := gob.NewEncoder(&buf) 1411 gob.Register(val) 1412 1413 err := enc.Encode(&val) 1414 if err != nil { 1415 return nil, err 1416 } 1417 1418 return buf.Bytes(), nil 1419 } 1420 1421 // UnSerialize 对字符串进行反序列化. 1422 // 其中register注册对象,其类型必须和Serialize的一致. 1423 func (ks *LkkString) UnSerialize(data []byte, register ...interface{}) (val interface{}, err error) { 1424 for _, v := range register { 1425 gob.Register(v) 1426 break 1427 } 1428 1429 buf := bytes.NewBuffer(data) 1430 dec := gob.NewDecoder(buf) 1431 err = dec.Decode(&val) 1432 return 1433 } 1434 1435 // Quotemeta 转义元字符集,包括 . \ + * ? [ ^ ] ( $ )等. 1436 func (ks *LkkString) Quotemeta(str string) string { 1437 var buf bytes.Buffer 1438 for _, char := range str { 1439 switch char { 1440 case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?': 1441 buf.WriteRune('\\') 1442 } 1443 buf.WriteRune(char) 1444 } 1445 return buf.String() 1446 } 1447 1448 // Htmlentities 将字符转换为 HTML 转义字符. 1449 func (ks *LkkString) Htmlentities(str string) string { 1450 return html.EscapeString(str) 1451 } 1452 1453 // HtmlentityDecode 将HTML实体转换为它们对应的字符. 1454 func (ks *LkkString) HtmlentityDecode(str string) string { 1455 return html.UnescapeString(str) 1456 } 1457 1458 // Crc32 计算一个字符串的 crc32 多项式. 1459 func (ks *LkkString) Crc32(str string) uint32 { 1460 return crc32.ChecksumIEEE([]byte(str)) 1461 } 1462 1463 // SimilarText 计算两个字符串的相似度;返回在两个字符串中匹配字符的数目,以及相似程度百分数. 1464 func (ks *LkkString) SimilarText(first, second string) (res int, percent float64) { 1465 l1, l2 := len(first), len(second) 1466 if l1+l2 == 0 { 1467 return 0, 0.0 1468 } 1469 1470 res = similarText(first, second, l1, l2) 1471 percent = float64(res*200) / float64(l1+l2) 1472 1473 return res, percent 1474 } 1475 1476 // Explode 字符串分割.delimiters为分隔符,可选,支持多个. 1477 func (ks *LkkString) Explode(str string, delimiters ...string) (res []string) { 1478 if str == "" { 1479 return 1480 } 1481 1482 dLen := len(delimiters) 1483 if dLen == 0 { 1484 res = append(res, str) 1485 } else if dLen > 1 { 1486 var sl []string 1487 for _, v := range delimiters { 1488 if v != "" { 1489 sl = append(sl, v, KDelimiter) 1490 } 1491 } 1492 str = strings.NewReplacer(sl...).Replace(str) 1493 res = strings.Split(str, KDelimiter) 1494 } else { 1495 res = strings.Split(str, delimiters[0]) 1496 } 1497 1498 return 1499 } 1500 1501 // Uniqid 获取一个带前缀的唯一ID(24位). 1502 // prefix 为前缀字符串. 1503 func (ks *LkkString) Uniqid(prefix string) string { 1504 buf := make([]byte, 12) 1505 _, _ = crand.Read(buf) 1506 1507 return fmt.Sprintf("%s%08x%16x", 1508 prefix, 1509 buf[0:4], 1510 buf[4:12]) 1511 } 1512 1513 // UuidV4 获取36位UUID(Version4). 1514 func (ks *LkkString) UuidV4() (string, error) { 1515 buf := make([]byte, 16) 1516 _, err := crand.Read(buf) 1517 1518 return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", 1519 buf[0:4], 1520 buf[4:6], 1521 buf[6:8], 1522 buf[8:10], 1523 buf[10:16]), err 1524 } 1525 1526 // VersionCompare 对比两个版本号字符串. 1527 // operator允许的操作符有: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne . 1528 // 特定的版本字符串,将会用以下顺序处理: 1529 // 无 < dev < alpha = a < beta = b < RC = rc < # < pl = p < ga < release = r 1530 // 用法: 1531 // VersionCompare("1.2.3-alpha", "1.2.3RC7", '>=') ; 1532 // VersionCompare("1.2.3-beta", "1.2.3pl", 'lt') ; 1533 // VersionCompare("1.1_dev", "1.2any", 'eq') . 1534 func (ks *LkkString) VersionCompare(version1, version2, operator string) (bool, error) { 1535 var canonicalize func(string) string 1536 var vcompare func(string, string) int 1537 var special func(string, string) int 1538 1539 // canonicalize 规范化转换 1540 canonicalize = func(version string) string { 1541 ver := []byte(version) 1542 l := len(ver) 1543 var buf = make([]byte, l*2) 1544 j := 0 1545 for i, v := range ver { 1546 next := uint8(0) 1547 if i+1 < l { // Have the next one 1548 next = ver[i+1] 1549 } 1550 if v == '-' || v == '_' || v == '+' { // replace '-', '_', '+' to '.' 1551 if j > 0 && buf[j-1] != '.' { 1552 buf[j] = '.' 1553 j++ 1554 } 1555 } else if (next > 0) && 1556 (!(next >= '0' && next <= '9') && (v >= '0' && v <= '9')) || 1557 (!(v >= '0' && v <= '9') && (next >= '0' && next <= '9')) { // Insert '.' before and after a non-digit 1558 buf[j] = v 1559 j++ 1560 if v != '.' && next != '.' { 1561 buf[j] = '.' 1562 j++ 1563 } 1564 continue 1565 } else if !((v >= '0' && v <= '9') || 1566 (v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z')) { // Non-letters and numbers 1567 if j > 0 && buf[j-1] != '.' { 1568 buf[j] = '.' 1569 j++ 1570 } 1571 } else { 1572 buf[j] = v 1573 j++ 1574 } 1575 } 1576 1577 return string(buf[:j]) 1578 } 1579 1580 // version compare 1581 // 在第一个版本低于第二个时,vcompare() 返回 -1;如果两者相等,返回 0;第二个版本更低时则返回 1. 1582 vcompare = func(origV1, origV2 string) int { 1583 if origV1 == "" || origV2 == "" { 1584 if origV1 == "" && origV2 == "" { 1585 return 0 1586 } 1587 if origV1 == "" { 1588 return -1 1589 } 1590 return 1 1591 } 1592 1593 ver1, ver2, compare := canonicalize(origV1), canonicalize(origV2), 0 1594 n1, n2 := 0, 0 1595 for { 1596 p1, p2 := "", "" 1597 n1 = strings.IndexByte(ver1, '.') 1598 if n1 == -1 { 1599 p1, ver1 = ver1[:], "" 1600 } else { 1601 p1, ver1 = ver1[:n1], ver1[n1+1:] 1602 } 1603 n2 = strings.IndexByte(ver2, '.') 1604 if n2 == -1 { 1605 p2, ver2 = ver2, "" 1606 } else { 1607 p2, ver2 = ver2[:n2], ver2[n2+1:] 1608 } 1609 1610 if p1 == "" || p2 == "" { 1611 break 1612 } 1613 1614 if (p1[0] >= '0' && p1[0] <= '9') && (p2[0] >= '0' && p2[0] <= '9') { // all is digit 1615 l1, _ := strconv.Atoi(p1) 1616 l2, _ := strconv.Atoi(p2) 1617 if l1 > l2 { 1618 compare = 1 1619 } else if l1 == l2 { 1620 compare = 0 1621 } else { 1622 compare = -1 1623 } 1624 } else { 1625 compare = special(p1, p2) 1626 } 1627 if compare != 0 || n1 == -1 || n2 == -1 { 1628 break 1629 } 1630 } 1631 1632 return compare 1633 } 1634 1635 // compare special version forms 特殊版本号 1636 special = func(form1, form2 string) int { 1637 found1, found2 := -1, -1 1638 // (Any string not found) < dev < alpha = a < beta = b < RC = rc < # < pl = p < ga < release = r 1639 forms := map[string]int{ 1640 "dev": 0, 1641 "alpha": 1, 1642 "a": 1, 1643 "beta": 2, 1644 "b": 2, 1645 "RC": 3, 1646 "rc": 3, 1647 "#": 4, 1648 "pl": 5, 1649 "p": 5, 1650 "ga": 6, 1651 "release": 7, 1652 "r": 7, 1653 } 1654 1655 for name, order := range forms { 1656 if form1 == name { 1657 found1 = order 1658 break 1659 } 1660 } 1661 for name, order := range forms { 1662 if form2 == name { 1663 found2 = order 1664 break 1665 } 1666 } 1667 1668 if found1 == found2 { 1669 if found1 == -1 { 1670 return strings.Compare(form1, form2) 1671 } 1672 return 0 1673 } else if found1 > found2 { 1674 return 1 1675 } else { 1676 return -1 1677 } 1678 } 1679 1680 compare := vcompare(version1, version2) 1681 // 在第一个版本低于第二个时,vcompare() 返回 -1;如果两者相等,返回 0;第二个版本更低时则返回 1. 1682 switch operator { 1683 case "<", "lt": 1684 return compare == -1, nil 1685 case "<=", "le": 1686 return compare != 1, nil 1687 case ">", "gt": 1688 return compare == 1, nil 1689 case ">=", "ge": 1690 return compare != -1, nil 1691 case "==", "=", "eq": 1692 return compare == 0, nil 1693 case "!=", "<>", "ne": 1694 return compare != 0, nil 1695 default: 1696 return false, errors.New("[VersionCompare]`operator: invalid") 1697 } 1698 } 1699 1700 // ToCamelCase 转为驼峰写法. 1701 // 去掉包括下划线"_"和横杠"-". 1702 func (ks *LkkString) ToCamelCase(str string) string { 1703 if len(str) == 0 { 1704 return "" 1705 } 1706 1707 buf := &bytes.Buffer{} 1708 var r0, r1 rune 1709 var size int 1710 1711 // leading connector will appear in output. 1712 for len(str) > 0 { 1713 r0, size = utf8.DecodeRuneInString(str) 1714 str = str[size:] 1715 1716 if !isCaseConnector(r0) { 1717 r0 = unicode.ToUpper(r0) 1718 break 1719 } 1720 1721 buf.WriteRune(r0) 1722 } 1723 1724 for len(str) > 0 { 1725 r1 = r0 1726 r0, size = utf8.DecodeRuneInString(str) 1727 str = str[size:] 1728 1729 if isCaseConnector(r0) && isCaseConnector(r1) { 1730 buf.WriteRune(r1) 1731 continue 1732 } 1733 1734 if isCaseConnector(r1) { 1735 r0 = unicode.ToUpper(r0) 1736 } else if unicode.IsLower(r1) && unicode.IsUpper(r0) { 1737 buf.WriteRune(r1) 1738 } else if unicode.IsUpper(r1) && unicode.IsLower(r0) { 1739 buf.WriteRune(r1) 1740 } else { 1741 r0 = unicode.ToLower(r0) 1742 buf.WriteRune(r1) 1743 } 1744 } 1745 1746 buf.WriteRune(r0) 1747 return buf.String() 1748 } 1749 1750 // ToSnakeCase 转为蛇形写法. 1751 // 使用下划线"_"连接. 1752 func (ks *LkkString) ToSnakeCase(str string) string { 1753 return camelCaseToLowerCase(str, '_') 1754 } 1755 1756 // ToSnakeCase 转为串形写法. 1757 // 使用横杠"-"连接. 1758 func (ks *LkkString) ToKebabCase(str string) string { 1759 return camelCaseToLowerCase(str, '-') 1760 } 1761 1762 // RemoveBefore 移除before之前的字符串; 1763 // include为是否移除包括before本身; 1764 // ignoreCase为是否忽略大小写. 1765 func (ks *LkkString) RemoveBefore(str, before string, include, ignoreCase bool) string { 1766 i := ks.Index(str, before, ignoreCase) 1767 if i > 0 { 1768 if include { 1769 str = str[i+len(before):] 1770 } else { 1771 str = str[i:] 1772 } 1773 } 1774 return str 1775 } 1776 1777 // RemoveAfter 移除after之后的字符串; 1778 // include为是否移除包括after本身; 1779 // ignoreCase为是否忽略大小写. 1780 func (ks *LkkString) RemoveAfter(str, after string, include, ignoreCase bool) string { 1781 i := ks.Index(str, after, ignoreCase) 1782 if i > 0 { 1783 if include { 1784 str = str[0:i] 1785 } else { 1786 str = str[0 : i+len(after)] 1787 } 1788 } 1789 return str 1790 } 1791 1792 // DBC2SBC 半角转全角. 1793 func (ks *LkkString) DBC2SBC(s string) string { 1794 return width.Widen.String(s) 1795 } 1796 1797 // SBC2DBC 全角转半角. 1798 func (ks *LkkString) SBC2DBC(s string) string { 1799 return width.Narrow.String(s) 1800 } 1801 1802 // Levenshtein 计算两个字符串之间的编辑距离,返回值越小字符串越相似. 1803 // 注意字符串最大长度为255. 1804 func (ks *LkkString) Levenshtein(a, b string) int { 1805 la := len(a) 1806 lb := len(b) 1807 1808 if a == b { 1809 return 0 1810 } else if la > 255 || lb > 255 { 1811 return -1 1812 } 1813 1814 d := make([]int, la+1) 1815 var lastdiag, olddiag, temp int 1816 for i := 1; i <= la; i++ { 1817 d[i] = i 1818 } 1819 for i := 1; i <= lb; i++ { 1820 d[0] = i 1821 lastdiag = i - 1 1822 for j := 1; j <= la; j++ { 1823 olddiag = d[j] 1824 min := d[j] + 1 1825 if (d[j-1] + 1) < min { 1826 min = d[j-1] + 1 1827 } 1828 if (a)[j-1] == (b)[i-1] { 1829 temp = 0 1830 } else { 1831 temp = 1 1832 } 1833 if (lastdiag + temp) < min { 1834 min = lastdiag + temp 1835 } 1836 d[j] = min 1837 lastdiag = olddiag 1838 } 1839 } 1840 return d[la] 1841 } 1842 1843 // ClosestWord 获取与原字符串相似度最高的字符串,以及它们的编辑距离. 1844 // word为原字符串,searchs为待查找的字符串数组. 1845 func (ks *LkkString) ClosestWord(word string, searchs []string) (string, int) { 1846 distance := 10000 1847 res := "" 1848 for _, search := range searchs { 1849 newVal := ks.Levenshtein(word, search) 1850 if newVal == 0 { 1851 distance = 0 1852 res = search 1853 break 1854 } 1855 1856 if newVal < distance { 1857 distance = newVal 1858 res = search 1859 } 1860 } 1861 1862 return res, distance 1863 } 1864 1865 // Utf8ToBig5 UTF-8转BIG5编码. 1866 func (ks *LkkString) Utf8ToBig5(s []byte) ([]byte, error) { 1867 reader := transform.NewReader(bytes.NewReader(s), traditionalchinese.Big5.NewEncoder()) 1868 d, e := ioutil.ReadAll(reader) 1869 return d, e 1870 } 1871 1872 // Big5ToUtf8 BIG5转UTF-8编码. 1873 func (ks *LkkString) Big5ToUtf8(s []byte) ([]byte, error) { 1874 reader := transform.NewReader(bytes.NewReader(s), traditionalchinese.Big5.NewDecoder()) 1875 d, e := ioutil.ReadAll(reader) 1876 return d, e 1877 } 1878 1879 // FirstLetter 获取字符串首字母. 1880 func (ks *LkkString) FirstLetter(str string) string { 1881 if str != "" { 1882 // 获取字符串第一个字符 1883 _, size := utf8.DecodeRuneInString(str) 1884 firstChar := str[:size] 1885 1886 if ks.IsLetters(firstChar) { 1887 return firstChar 1888 } else if ks.IsChinese(firstChar) { 1889 // Utf8 转 GBK2312 1890 firstCharGbk, _ := ks.Utf8ToGbk([]byte(firstChar)) 1891 1892 // 获取第一个字符的16进制 1893 firstCharHex := hex.EncodeToString(firstCharGbk) 1894 1895 // 16进制转十进制 1896 firstCharDec, _ := strconv.ParseInt(firstCharHex, 16, 0) 1897 1898 // 十进制落在GB 2312的某个拼音区间即为某个字母 1899 firstCharDecimalRelative := firstCharDec - 65536 1900 if firstCharDecimalRelative >= -20319 && firstCharDecimalRelative <= -20284 { 1901 return "A" 1902 } 1903 if firstCharDecimalRelative >= -20283 && firstCharDecimalRelative <= -19776 { 1904 return "B" 1905 } 1906 if firstCharDecimalRelative >= -19775 && firstCharDecimalRelative <= -19219 { 1907 return "C" 1908 } 1909 if firstCharDecimalRelative >= -19218 && firstCharDecimalRelative <= -18711 { 1910 return "D" 1911 } 1912 if firstCharDecimalRelative >= -18710 && firstCharDecimalRelative <= -18527 { 1913 return "E" 1914 } 1915 if firstCharDecimalRelative >= -18526 && firstCharDecimalRelative <= -18240 { 1916 return "F" 1917 } 1918 if firstCharDecimalRelative >= -18239 && firstCharDecimalRelative <= -17923 { 1919 return "G" 1920 } 1921 if firstCharDecimalRelative >= -17922 && firstCharDecimalRelative <= -17418 { 1922 return "H" 1923 } 1924 if firstCharDecimalRelative >= -17417 && firstCharDecimalRelative <= -16475 { 1925 return "J" 1926 } 1927 if firstCharDecimalRelative >= -16474 && firstCharDecimalRelative <= -16213 { 1928 return "K" 1929 } 1930 if firstCharDecimalRelative >= -16212 && firstCharDecimalRelative <= -15641 { 1931 return "L" 1932 } 1933 if firstCharDecimalRelative >= -15640 && firstCharDecimalRelative <= -15166 { 1934 return "M" 1935 } 1936 if firstCharDecimalRelative >= -15165 && firstCharDecimalRelative <= -14923 { 1937 return "N" 1938 } 1939 if firstCharDecimalRelative >= -14922 && firstCharDecimalRelative <= -14915 { 1940 return "O" 1941 } 1942 if firstCharDecimalRelative >= -14914 && firstCharDecimalRelative <= -14631 { 1943 return "P" 1944 } 1945 if firstCharDecimalRelative >= -14630 && firstCharDecimalRelative <= -14150 { 1946 return "Q" 1947 } 1948 if firstCharDecimalRelative >= -14149 && firstCharDecimalRelative <= -14091 { 1949 return "R" 1950 } 1951 if firstCharDecimalRelative >= -14090 && firstCharDecimalRelative <= -13319 { 1952 return "S" 1953 } 1954 if firstCharDecimalRelative >= -13318 && firstCharDecimalRelative <= -12839 { 1955 return "T" 1956 } 1957 if firstCharDecimalRelative >= -12838 && firstCharDecimalRelative <= -12557 { 1958 return "W" 1959 } 1960 if firstCharDecimalRelative >= -12556 && firstCharDecimalRelative <= -11848 { 1961 return "X" 1962 } 1963 if firstCharDecimalRelative >= -11847 && firstCharDecimalRelative <= -11056 { 1964 return "Y" 1965 } 1966 if firstCharDecimalRelative >= -11055 && firstCharDecimalRelative <= -10247 { 1967 return "Z" 1968 } 1969 } 1970 } 1971 1972 return "" 1973 } 1974 1975 // HideCard 隐藏证件号码. 1976 func (ks *LkkString) HideCard(card string) string { 1977 res := "******" 1978 leng := len(card) 1979 if leng > 4 && leng <= 10 { 1980 res = card[0:4] + "******" 1981 } else if leng > 10 { 1982 res = card[0:4] + "******" + card[(leng-4):leng] 1983 } 1984 1985 return res 1986 } 1987 1988 // HideMobile 隐藏手机号. 1989 func (ks *LkkString) HideMobile(mobile string) string { 1990 res := "***" 1991 leng := len(mobile) 1992 if leng > 7 { 1993 res = mobile[0:3] + "****" + mobile[leng-3:leng] 1994 } 1995 1996 return res 1997 } 1998 1999 // HideTrueName 隐藏真实名称(如姓名、账号、公司等). 2000 func (ks *LkkString) HideTrueName(name string) string { 2001 res := "**" 2002 if name != "" { 2003 runs := []rune(name) 2004 leng := len(runs) 2005 if leng <= 3 { 2006 res = string(runs[0:1]) + res 2007 } else if leng < 5 { 2008 res = string(runs[0:2]) + res 2009 } else if leng < 10 { 2010 res = string(runs[0:2]) + "***" + string(runs[leng-2:leng]) 2011 } else if leng < 16 { 2012 res = string(runs[0:3]) + "****" + string(runs[leng-3:leng]) 2013 } else { 2014 res = string(runs[0:4]) + "*****" + string(runs[leng-4:leng]) 2015 } 2016 } 2017 2018 return res 2019 } 2020 2021 // CountBase64Byte 粗略统计base64字符串大小,字节. 2022 func (ks *LkkString) CountBase64Byte(str string) (res int) { 2023 pos := strings.Index(str, ",") 2024 if pos > 10 { 2025 img := strings.Replace(str[pos:], "=", "", -1) 2026 res = int(float64(len(img)) * float64(3.0/4.0)) 2027 } 2028 2029 return 2030 } 2031 2032 // Strpad 使用fill填充str字符串到指定长度max. 2033 // ptype为填充类型,枚举值(PAD_LEFT,PAD_RIGHT,PAD_BOTH). 2034 func (ks *LkkString) Strpad(str string, fill string, max int, ptype LkkPadType) string { 2035 runeStr := []rune(str) 2036 runeStrLen := len(runeStr) 2037 if runeStrLen >= max || max < 1 || len(fill) == 0 { 2038 return str 2039 } 2040 2041 var leftsize int 2042 var rightsize int 2043 2044 switch ptype { 2045 case PAD_BOTH: 2046 rlsize := float64(max-runeStrLen) / 2 2047 leftsize = int(rlsize) 2048 rightsize = int(rlsize + math.Copysign(0.5, rlsize)) 2049 case PAD_LEFT: 2050 leftsize = max - runeStrLen 2051 case PAD_RIGHT: 2052 rightsize = max - runeStrLen 2053 } 2054 2055 buf := make([]rune, 0, max) 2056 2057 if ptype == PAD_LEFT || ptype == PAD_BOTH { 2058 for i := 0; i < leftsize; { 2059 for _, v := range []rune(fill) { 2060 buf = append(buf, v) 2061 if i >= leftsize-1 { 2062 i = leftsize 2063 break 2064 } else { 2065 i++ 2066 } 2067 } 2068 } 2069 } 2070 2071 buf = append(buf, runeStr...) 2072 2073 if ptype == PAD_RIGHT || ptype == PAD_BOTH { 2074 for i := 0; i < rightsize; { 2075 for _, v := range []rune(fill) { 2076 buf = append(buf, v) 2077 if i >= rightsize-1 { 2078 i = rightsize 2079 break 2080 } else { 2081 i++ 2082 } 2083 } 2084 } 2085 } 2086 2087 return string(buf) 2088 } 2089 2090 // StrpadLeft 字符串左侧填充,请参考Strpad. 2091 func (ks *LkkString) StrpadLeft(str string, fill string, max int) string { 2092 return ks.Strpad(str, fill, max, PAD_LEFT) 2093 } 2094 2095 // StrpadRight 字符串右侧填充,请参考Strpad. 2096 func (ks *LkkString) StrpadRight(str string, fill string, max int) string { 2097 return ks.Strpad(str, fill, max, PAD_RIGHT) 2098 } 2099 2100 // StrpadBoth 字符串两侧填充,请参考Strpad. 2101 func (ks *LkkString) StrpadBoth(str string, fill string, max int) string { 2102 return ks.Strpad(str, fill, max, PAD_BOTH) 2103 } 2104 2105 // CountWords 统计字符串中单词的使用情况. 2106 // 返回结果:单词总数;和一个字典,包含每个单词的单独统计. 2107 // 因为没有分词,对中文尚未很好支持. 2108 func (ks *LkkString) CountWords(str string) (int, map[string]int) { 2109 //过滤标点符号 2110 var buffer bytes.Buffer 2111 for _, r := range str { 2112 if unicode.IsPunct(r) || unicode.IsSymbol(r) || unicode.IsMark(r) { 2113 buffer.WriteRune(' ') 2114 } else { 2115 buffer.WriteRune(r) 2116 } 2117 } 2118 2119 var total int 2120 mp := make(map[string]int) 2121 words := strings.Fields(buffer.String()) 2122 for _, word := range words { 2123 mp[word] += 1 2124 total++ 2125 } 2126 2127 return total, mp 2128 } 2129 2130 // HasEmoji 字符串是否含有表情符. 2131 func (ks *LkkString) HasEmoji(str string) bool { 2132 return str != "" && RegEmoji.MatchString(str) 2133 } 2134 2135 // RemoveEmoji 移除字符串中的表情符(使用正则,效率较低). 2136 func (ks *LkkString) RemoveEmoji(str string) string { 2137 return RegEmoji.ReplaceAllString(str, "") 2138 } 2139 2140 // Gravatar 获取Gravatar头像地址. 2141 // email为邮箱;size为头像尺寸像素. 2142 func (ks *LkkString) Gravatar(email string, size uint16) string { 2143 h := md5.New() 2144 _, _ = io.WriteString(h, email) 2145 return fmt.Sprintf("https://www.gravatar.com/avatar/%x?s=%d", h.Sum(nil), size) 2146 } 2147 2148 // AtWho 查找被@的用户名.minLen为用户名最小长度,默认5. 2149 func (ks *LkkString) AtWho(text string, minLen ...int) []string { 2150 var result = []string{} 2151 var username string 2152 var min int = 5 2153 if len(minLen) > 0 && minLen[0] > 0 { 2154 min = minLen[0] 2155 } 2156 2157 for _, line := range strings.Split(text, "\n") { 2158 if len(line) == 0 { 2159 continue 2160 } 2161 for { 2162 index := strings.Index(line, "@") 2163 if index == -1 { 2164 break 2165 } else if index > 0 { 2166 r := rune(line[index-1]) 2167 if unicode.IsUpper(r) || unicode.IsLower(r) { 2168 line = line[index+1:] 2169 } else { 2170 line = line[index:] 2171 } 2172 } else if index == 0 { 2173 // the "@" is first characters 2174 endIndex := strings.Index(line, " ") 2175 if endIndex == -1 { 2176 username = line[1:] 2177 } else { 2178 username = line[1:endIndex] 2179 } 2180 2181 if len(username) >= min && RegUsernameen.MatchString(username) && !KArr.InStringSlice(username, result) { 2182 result = append(result, username) 2183 } 2184 2185 if endIndex == -1 { 2186 break 2187 } 2188 2189 line = line[endIndex:] 2190 } 2191 } 2192 } 2193 2194 return result 2195 } 2196 2197 // MatchEquations 匹配字符串中所有的等式. 2198 func (ks *LkkString) MatchEquations(str string) (res []string) { 2199 res = RegEquation.FindAllString(equationStr03, -1) 2200 return 2201 } 2202 2203 // GetEquationValue 获取等式str中变量name的值. 2204 func (ks *LkkString) GetEquationValue(str, name string) (res string) { 2205 pattern := `['"]?` + name + `['"]?[\s]*=[\s]*['"]?(.*)['"]?` 2206 reg := regexp.MustCompile(pattern) 2207 mat := reg.FindStringSubmatch(str) 2208 if len(mat) == 2 { 2209 res = mat[1] 2210 } 2211 2212 return 2213 }