github.com/searKing/golang/go@v1.2.117/strings/strings.go (about)

     1  // Copyright 2020 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package strings
     6  
     7  import (
     8  	"strings"
     9  	"unicode"
    10  	"unicode/utf8"
    11  
    12  	bytes_ "github.com/searKing/golang/go/bytes"
    13  	unicode_ "github.com/searKing/golang/go/unicode"
    14  )
    15  
    16  // ContainsRuneAnyFunc reports whether any of the Unicode code point r satisfying f(r) is within s.
    17  func ContainsRuneAnyFunc(s string, f func(rune) bool) bool {
    18  	if f == nil {
    19  		return false
    20  	}
    21  	for _, r := range s {
    22  		if f(r) {
    23  			return true
    24  		}
    25  	}
    26  	return false
    27  }
    28  
    29  // ContainsRuneOnlyFunc reports whether all of the Unicode code point r satisfying f(r) is within s.
    30  func ContainsRuneOnlyFunc(s string, f func(rune) bool) bool {
    31  	if f == nil {
    32  		return true
    33  	}
    34  	for _, r := range s {
    35  		if !f(r) {
    36  			return false
    37  		}
    38  	}
    39  	return true
    40  }
    41  
    42  // ContainsAnyRangeTable reports whether the string contains any rune in any of the specified table of ranges.
    43  func ContainsAnyRangeTable(s string, rangeTabs ...*unicode.RangeTable) bool {
    44  	if len(rangeTabs) == 0 {
    45  		return ContainsRuneAnyFunc(s, nil)
    46  	}
    47  	return ContainsRuneAnyFunc(s, func(r rune) bool {
    48  		for _, t := range rangeTabs {
    49  			if t == nil {
    50  				continue
    51  			}
    52  			if unicode.Is(t, r) {
    53  				return true
    54  			}
    55  		}
    56  		return false
    57  	})
    58  }
    59  
    60  // ContainsOnlyRangeTable reports whether the string contains only rune in all of the specified table of ranges.
    61  func ContainsOnlyRangeTable(s string, rangeTabs ...*unicode.RangeTable) bool {
    62  	if len(rangeTabs) == 0 {
    63  		return ContainsRuneOnlyFunc(s, nil)
    64  	}
    65  	return ContainsRuneOnlyFunc(s, func(r rune) bool {
    66  		for _, t := range rangeTabs {
    67  			if t == nil {
    68  				continue
    69  			}
    70  			if !unicode.Is(t, r) {
    71  				return false
    72  			}
    73  		}
    74  		return true
    75  	})
    76  }
    77  
    78  // ContainsAsciiVisual reports whether the string contains any rune in visual ascii code, that is [0x21, 0x7E].
    79  func ContainsAsciiVisual(s string) bool {
    80  	return ContainsAnyRangeTable(s, unicode_.AsciiVisual)
    81  }
    82  
    83  // ContainsAsciiVisual reports whether the string contains only rune in visual ascii code, that is [0x21, 0x7E].
    84  func ContainsOnlyAsciiVisual(s string) bool {
    85  	return ContainsOnlyRangeTable(s, unicode_.AsciiVisual)
    86  }
    87  
    88  // JoinRepeat behaves like strings.Join([]string{s,...,s}, sep)
    89  func JoinRepeat(s string, sep string, n int) string {
    90  	var b strings.Builder
    91  	for i := 0; i < n-1; i++ {
    92  		b.WriteString(s)
    93  		b.WriteString(sep)
    94  	}
    95  	if n > 0 {
    96  		b.WriteString(s)
    97  	}
    98  	return b.String()
    99  }
   100  
   101  // MapLeading returns a copy of the string s with its first characters modified
   102  // according to the mapping function. If mapping returns a negative value, the character is
   103  // dropped from the string with no replacement.
   104  func MapLeading(mapping func(rune) rune, s string) string {
   105  	if s == "" {
   106  		return s
   107  	}
   108  	rLeading, sRight := ExtractFirstRune(s)
   109  	srMapped := mapping(rLeading)
   110  	if srMapped < 0 {
   111  		return sRight
   112  	}
   113  
   114  	// Fast path for unchanged input
   115  	if rLeading == srMapped {
   116  		return s
   117  	}
   118  	return string(srMapped) + sRight
   119  }
   120  
   121  // ToLowerLeading returns s with it's first Unicode letter mapped to their lower case.
   122  func ToLowerLeading(s string) string {
   123  	return MapLeading(unicode.ToLower, s)
   124  }
   125  
   126  // ToUpperLeading returns s with it's first Unicode letter mapped to their upper case.
   127  func ToUpperLeading(s string) string {
   128  	return MapLeading(unicode.ToUpper, s)
   129  }
   130  
   131  // Truncate shrinks s's len to n at most
   132  func Truncate(s string, n int) string {
   133  	if n < 0 {
   134  		n = 0
   135  	}
   136  	if len(s) <= n {
   137  		return s
   138  	}
   139  	return s[:n]
   140  }
   141  
   142  // PadLeft returns s padded to length n, padded left with repeated pad
   143  // return s directly if pad is empty
   144  // padding s with {{pad}} and spaces(less than len(pad)) as a prefix, as [pad]...[pad][space]...[space][s]
   145  func PadLeft(s string, pad string, n int) string {
   146  	if len(pad) == 0 {
   147  		return s
   148  	}
   149  
   150  	pc, sc := ComputePad(s, pad, n)
   151  
   152  	return strings.Repeat(pad, pc) + strings.Repeat(" ", sc) + s
   153  }
   154  
   155  // PadRight returns s padded to length n, padded right with repeated pad
   156  // return s directly if pad is empty
   157  // padding s with {{pad}} and spaces(less than len(pad))  as a suffix, as [s][space]...[space][pad]...[pad]
   158  func PadRight(s string, pad string, n int) string {
   159  	if len(pad) == 0 {
   160  		return s
   161  	}
   162  	pc, sc := ComputePad(s, pad, n)
   163  
   164  	return s + strings.Repeat(" ", sc) + strings.Repeat(pad, pc)
   165  }
   166  
   167  // ComputePad returns pad's count and space's count(less than len(pad)) will be need to pad s to len n
   168  // padCount = (n-len(s))/len(pad)
   169  // spaceCount = (n-len(s))%len(pad)
   170  func ComputePad(s string, pad string, n int) (padCount, spaceCount int) {
   171  	return bytes_.ComputePad([]byte(s), []byte(pad), n)
   172  }
   173  
   174  // ReverseByByte returns a string with the bytes of s in reverse order.
   175  func ReverseByByte(s string) string {
   176  	var b strings.Builder
   177  	b.Grow(len(s))
   178  	for i := len(s) - 1; i >= 0; i-- {
   179  		b.WriteByte(s[i])
   180  	}
   181  	return b.String()
   182  }
   183  
   184  // ReverseByRune returns a string with the runes of s in reverse order.
   185  // Invalid UTF-8 sequences, if any, will be reversed byte by byte.
   186  func ReverseByRune(s string) string {
   187  	res := make([]byte, len(s))
   188  	prevPos, resPos := 0, len(s)
   189  	for pos := range s {
   190  		resPos -= pos - prevPos
   191  		copy(res[resPos:], s[prevPos:pos])
   192  		prevPos = pos
   193  	}
   194  	copy(res[0:], s[prevPos:])
   195  	return string(res)
   196  }
   197  
   198  // CountPrefix counts the number of non-overlapping instances of continuous substr prefix in s.
   199  // If substr is an empty string, CountPrefix returns 1 + the number of Unicode code points in s.
   200  func CountPrefix(s, substr string) int {
   201  	// special case
   202  	if len(substr) == 0 {
   203  		return utf8.RuneCountInString(s) + 1
   204  	}
   205  	n := 0
   206  	gap := len(substr)
   207  	for i := 0; i+gap <= len(s); i += gap {
   208  		if s[i:i+gap] == substr {
   209  			n++
   210  			continue
   211  		}
   212  		break
   213  	}
   214  	return n
   215  }
   216  
   217  // CountSuffix counts the number of non-overlapping instances of continuous substr suffix in s.
   218  // If substr is an empty string, CountSuffix returns 1 + the number of Unicode code points in s.
   219  func CountSuffix(s, substr string) int {
   220  	// special case
   221  	if len(substr) == 0 {
   222  		return utf8.RuneCountInString(s) + 1
   223  	}
   224  	n := 0
   225  	gap := len(substr)
   226  	for i := len(s); i-gap >= 0; i -= gap {
   227  		if s[i-gap:i] == substr {
   228  			n++
   229  			continue
   230  		}
   231  		break
   232  	}
   233  	return n
   234  }