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  }