github.com/songzhibin97/gkit@v1.2.13/sys/stringx/stringx.go (about)

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