switch to go vendoring
This commit is contained in:
+192
@@ -0,0 +1,192 @@
|
||||
# String Util
|
||||
|
||||
This is a go string operate util package.
|
||||
|
||||
- Github: https://github.com/gookit/goutil/strutil
|
||||
- GoDoc: https://godoc.org/github.com/gookit/goutil/strutil
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/gookit/goutil/strutil
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
ss := strutil.ToArray("a,b,c", ",")
|
||||
// Output: []string{"a", "b", "c"}
|
||||
|
||||
ints, err := strutil.ToIntSlice("1,2,3")
|
||||
// Output: []int{1, 2, 3}
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
```go
|
||||
func AddSlashes(s string) string
|
||||
func AnyToString(val interface{}, defaultAsErr bool) (str string, err error)
|
||||
func B32Decode(str string) string
|
||||
func B32Encode(str string) string
|
||||
func B64Decode(str string) string
|
||||
func B64Encode(str string) string
|
||||
func Base64(str string) string
|
||||
func Bool(s string) (bool, error)
|
||||
func Byte2str(b []byte) string
|
||||
func Byte2string(b []byte) string
|
||||
func BytePos(s string, bt byte) int
|
||||
func Camel(s string, sep ...string) string
|
||||
func CamelCase(s string, sep ...string) string
|
||||
func Compare(s1, s2, op string) bool
|
||||
func Cut(s, sep string) (before string, after string, found bool)
|
||||
func EscapeHTML(s string) string
|
||||
func EscapeJS(s string) string
|
||||
func FilterEmail(s string) string
|
||||
func GenMd5(src interface{}) string
|
||||
func HasAllSubs(s string, subs []string) bool
|
||||
func HasOnePrefix(s string, prefixes []string) bool
|
||||
func HasOneSub(s string, subs []string) bool
|
||||
func HasPrefix(s string, prefix string) bool
|
||||
func HasSuffix(s string, suffix string) bool
|
||||
func Implode(sep string, ss ...string) string
|
||||
func Indent(s, prefix string) string
|
||||
func IndentBytes(b, prefix []byte) []byte
|
||||
func Int(s string) (int, error)
|
||||
func Int64(s string) int64
|
||||
func Int64OrErr(s string) (int64, error)
|
||||
func Int64OrPanic(s string) int64
|
||||
func IntOrPanic(s string) int
|
||||
func Ints(s string, sep ...string) []int
|
||||
func IsAlphaNum(c uint8) bool
|
||||
func IsAlphabet(char uint8) bool
|
||||
func IsBlank(s string) bool
|
||||
func IsBlankBytes(bs []byte) bool
|
||||
func IsEmpty(s string) bool
|
||||
func IsEndOf(s, suffix string) bool
|
||||
func IsNotBlank(s string) bool
|
||||
func IsNumChar(c byte) bool
|
||||
func IsNumeric(s string) bool
|
||||
func IsSpace(c byte) bool
|
||||
func IsSpaceRune(r rune) bool
|
||||
func IsStartOf(s, prefix string) bool
|
||||
func IsStartsOf(s string, prefixes []string) bool
|
||||
func IsSymbol(r rune) bool
|
||||
func IsValidUtf8(s string) bool
|
||||
func IsVersion(s string) bool
|
||||
func Join(sep string, ss ...string) string
|
||||
func JoinList(sep string, ss []string) string
|
||||
func LTrim(s string, cutSet ...string) string
|
||||
func Lower(s string) string
|
||||
func LowerFirst(s string) string
|
||||
func Lowercase(s string) string
|
||||
func Ltrim(s string, cutSet ...string) string
|
||||
func MD5(src interface{}) string
|
||||
func Md5(src interface{}) string
|
||||
func MicroTimeHexID() string
|
||||
func MicroTimeID() string
|
||||
func MustBool(s string) bool
|
||||
func MustCut(s, sep string) (before string, after string)
|
||||
func MustInt(s string) int
|
||||
func MustInt64(s string) int64
|
||||
func MustString(in interface{}) string
|
||||
func MustToTime(s string, layouts ...string) time.Time
|
||||
func NoCaseEq(s, t string) bool
|
||||
func PadLeft(s, pad string, length int) string
|
||||
func PadRight(s, pad string, length int) string
|
||||
func Padding(s, pad string, length int, pos uint8) string
|
||||
func PrettyJSON(v interface{}) (string, error)
|
||||
func QuietBool(s string) bool
|
||||
func QuietInt(s string) int
|
||||
func QuietInt64(s string) int64
|
||||
func QuietString(in interface{}) string
|
||||
func Quote(s string) string
|
||||
func RTrim(s string, cutSet ...string) string
|
||||
func RandomBytes(length int) ([]byte, error)
|
||||
func RandomChars(ln int) string
|
||||
func RandomCharsV2(ln int) string
|
||||
func RandomCharsV3(ln int) string
|
||||
func RandomString(length int) (string, error)
|
||||
func RenderTemplate(input string, data interface{}, fns template.FuncMap, isFile ...bool) string
|
||||
func RenderText(input string, data interface{}, fns template.FuncMap, isFile ...bool) string
|
||||
func Repeat(s string, times int) string
|
||||
func RepeatBytes(char byte, times int) (chars []byte)
|
||||
func RepeatRune(char rune, times int) (chars []rune)
|
||||
func Replaces(str string, pairs map[string]string) string
|
||||
func Rtrim(s string, cutSet ...string) string
|
||||
func RuneCount(s string) int
|
||||
func RuneIsLower(c rune) bool
|
||||
func RuneIsUpper(c rune) bool
|
||||
func RuneIsWord(c rune) bool
|
||||
func RunePos(s string, ru rune) int
|
||||
func RuneWidth(r rune) int
|
||||
func Similarity(s, t string, rate float32) (float32, bool)
|
||||
func SnakeCase(s string, sep ...string) string
|
||||
func Split(s, sep string) (ss []string)
|
||||
func SplitInlineComment(val string) (string, string)
|
||||
func SplitN(s, sep string, n int) (ss []string)
|
||||
func SplitNTrimmed(s, sep string, n int) (ss []string)
|
||||
func SplitNValid(s, sep string, n int) (ss []string)
|
||||
func SplitTrimmed(s, sep string) (ss []string)
|
||||
func SplitValid(s, sep string) (ss []string)
|
||||
func StrPos(s, sub string) int
|
||||
func String(val interface{}) (string, error)
|
||||
func StringOrErr(val interface{}) (string, error)
|
||||
func Strings(s string, sep ...string) []string
|
||||
func StripSlashes(s string) string
|
||||
func Substr(s string, pos, length int) string
|
||||
func TextSplit(s string, w int) []string
|
||||
func TextTruncate(s string, w int, tail string) string
|
||||
func TextWidth(s string) int
|
||||
func TextWrap(s string, w int) string
|
||||
func Title(s string) string
|
||||
func ToArray(s string, sep ...string) []string
|
||||
func ToBool(s string) (bool, error)
|
||||
func ToBytes(s string) (b []byte)
|
||||
func ToDuration(s string) (time.Duration, error)
|
||||
func ToInt(s string) (int, error)
|
||||
func ToInt64(s string) (int64, error)
|
||||
func ToIntSlice(s string, sep ...string) (ints []int, err error)
|
||||
func ToInts(s string, sep ...string) ([]int, error)
|
||||
func ToSlice(s string, sep ...string) []string
|
||||
func ToString(val interface{}) (string, error)
|
||||
func ToStrings(s string, sep ...string) []string
|
||||
func ToTime(s string, layouts ...string) (t time.Time, err error)
|
||||
func Trim(s string, cutSet ...string) string
|
||||
func TrimCut(s, sep string) (string, string)
|
||||
func TrimLeft(s string, cutSet ...string) string
|
||||
func TrimRight(s string, cutSet ...string) string
|
||||
func URLDecode(s string) string
|
||||
func URLEncode(s string) string
|
||||
func Unquote(s string) string
|
||||
func Upper(s string) string
|
||||
func UpperFirst(s string) string
|
||||
func UpperWord(s string) string
|
||||
func Uppercase(s string) string
|
||||
func Utf8Len(s string) int
|
||||
func Utf8Split(s string, w int) []string
|
||||
func Utf8Truncate(s string, w int, tail string) string
|
||||
func Utf8Width(s string) (size int)
|
||||
func Utf8len(s string) int
|
||||
func VersionCompare(v1, v2, op string) bool
|
||||
func WidthWrap(s string, w int) string
|
||||
func WrapTag(s, tag string) string
|
||||
```
|
||||
|
||||
## Code Check & Testing
|
||||
|
||||
```bash
|
||||
gofmt -w -l ./
|
||||
golint ./...
|
||||
```
|
||||
|
||||
**Testing**:
|
||||
|
||||
```shell
|
||||
go test -v ./strutil/...
|
||||
```
|
||||
|
||||
**Test limit by regexp**:
|
||||
|
||||
```shell
|
||||
go test -v -run ^TestSetByKeys ./strutil/...
|
||||
```
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Buffer wrap and extends the bytes.Buffer
|
||||
type Buffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
// NewBuffer instance
|
||||
func NewBuffer() *Buffer {
|
||||
return &Buffer{}
|
||||
}
|
||||
|
||||
// WriteAny type value to buffer
|
||||
func (b *Buffer) WriteAny(vs ...any) {
|
||||
for _, v := range vs {
|
||||
_, _ = b.Buffer.WriteString(fmt.Sprint(v))
|
||||
}
|
||||
}
|
||||
|
||||
// QuietWriteByte to buffer
|
||||
func (b *Buffer) QuietWriteByte(c byte) {
|
||||
_ = b.WriteByte(c)
|
||||
}
|
||||
|
||||
// QuietWritef write message to buffer
|
||||
func (b *Buffer) QuietWritef(tpl string, vs ...any) {
|
||||
_, _ = b.WriteString(fmt.Sprintf(tpl, vs...))
|
||||
}
|
||||
|
||||
// Writeln write message to buffer with newline
|
||||
func (b *Buffer) Writeln(ss ...string) {
|
||||
b.QuietWriteln(ss...)
|
||||
}
|
||||
|
||||
// QuietWriteln write message to buffer with newline
|
||||
func (b *Buffer) QuietWriteln(ss ...string) {
|
||||
_, _ = b.WriteString(strings.Join(ss, ""))
|
||||
_ = b.WriteByte('\n')
|
||||
}
|
||||
|
||||
// QuietWriteString to buffer
|
||||
func (b *Buffer) QuietWriteString(ss ...string) {
|
||||
_, _ = b.WriteString(strings.Join(ss, ""))
|
||||
}
|
||||
|
||||
// MustWriteString to buffer
|
||||
func (b *Buffer) MustWriteString(ss ...string) {
|
||||
_, err := b.WriteString(strings.Join(ss, ""))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ResetAndGet buffer string.
|
||||
func (b *Buffer) ResetAndGet() string {
|
||||
s := b.String()
|
||||
b.Reset()
|
||||
return s
|
||||
}
|
||||
|
||||
// ByteChanPool struct
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// bp := strutil.NewByteChanPool(500, 1024, 1024)
|
||||
// buf:=bp.Get()
|
||||
// defer bp.Put(buf)
|
||||
// // use buf do something ...
|
||||
//
|
||||
// refer https://www.flysnow.org/2020/08/21/golang-chan-byte-pool.html
|
||||
// from https://github.com/minio/minio/blob/master/internal/bpool/bpool.go
|
||||
type ByteChanPool struct {
|
||||
c chan []byte
|
||||
w int
|
||||
wcap int
|
||||
}
|
||||
|
||||
// NewByteChanPool instance
|
||||
func NewByteChanPool(maxSize int, width int, capWidth int) *ByteChanPool {
|
||||
return &ByteChanPool{
|
||||
c: make(chan []byte, maxSize),
|
||||
w: width,
|
||||
wcap: capWidth,
|
||||
}
|
||||
}
|
||||
|
||||
// Get gets a []byte from the BytePool, or creates a new one if none are
|
||||
// available in the pool.
|
||||
func (bp *ByteChanPool) Get() (b []byte) {
|
||||
select {
|
||||
case b = <-bp.c:
|
||||
// reuse existing buffer
|
||||
default:
|
||||
// create new buffer
|
||||
if bp.wcap > 0 {
|
||||
b = make([]byte, bp.w, bp.wcap)
|
||||
} else {
|
||||
b = make([]byte, bp.w)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Put returns the given Buffer to the BytePool.
|
||||
func (bp *ByteChanPool) Put(b []byte) {
|
||||
select {
|
||||
case bp.c <- b:
|
||||
// buffer went back into pool
|
||||
default:
|
||||
// buffer didn't go back into pool, just discard
|
||||
}
|
||||
}
|
||||
|
||||
// Width returns the width of the byte arrays in this pool.
|
||||
func (bp *ByteChanPool) Width() (n int) {
|
||||
return bp.w
|
||||
}
|
||||
|
||||
// WidthCap returns the cap width of the byte arrays in this pool.
|
||||
func (bp *ByteChanPool) WidthCap() (n int) {
|
||||
return bp.wcap
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Equal check, alias of strings.EqualFold
|
||||
var Equal = strings.EqualFold
|
||||
|
||||
// NoCaseEq check two strings is equals and case-insensitivity
|
||||
func NoCaseEq(s, t string) bool {
|
||||
return strings.EqualFold(s, t)
|
||||
}
|
||||
|
||||
// IsNumChar returns true if the given character is a numeric, otherwise false.
|
||||
func IsNumChar(c byte) bool {
|
||||
return c >= '0' && c <= '9'
|
||||
}
|
||||
|
||||
var numReg = regexp.MustCompile(`^\d+$`)
|
||||
|
||||
// IsNumeric returns true if the given string is a numeric, otherwise false.
|
||||
func IsNumeric(s string) bool {
|
||||
return numReg.MatchString(s)
|
||||
}
|
||||
|
||||
// IsAlphabet char
|
||||
func IsAlphabet(char uint8) bool {
|
||||
// A 65 -> Z 90
|
||||
if char >= 'A' && char <= 'Z' {
|
||||
return true
|
||||
}
|
||||
|
||||
// a 97 -> z 122
|
||||
if char >= 'a' && char <= 'z' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAlphaNum reports whether the byte is an ASCII letter, number, or underscore
|
||||
func IsAlphaNum(c uint8) bool {
|
||||
return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
|
||||
}
|
||||
|
||||
// StrPos alias of the strings.Index
|
||||
func StrPos(s, sub string) int {
|
||||
return strings.Index(s, sub)
|
||||
}
|
||||
|
||||
// BytePos alias of the strings.IndexByte
|
||||
func BytePos(s string, bt byte) int {
|
||||
return strings.IndexByte(s, bt)
|
||||
}
|
||||
|
||||
// HasOneSub substr in the given string.
|
||||
func HasOneSub(s string, subs []string) bool {
|
||||
for _, sub := range subs {
|
||||
if strings.Contains(s, sub) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasAllSubs all substr in the given string.
|
||||
func HasAllSubs(s string, subs []string) bool {
|
||||
for _, sub := range subs {
|
||||
if !strings.Contains(s, sub) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsStartsOf alias of the HasOnePrefix
|
||||
func IsStartsOf(s string, prefixes []string) bool {
|
||||
return HasOnePrefix(s, prefixes)
|
||||
}
|
||||
|
||||
// HasOnePrefix the string start withs one of the subs
|
||||
func HasOnePrefix(s string, prefixes []string) bool {
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasPrefix substr in the given string.
|
||||
func HasPrefix(s string, prefix string) bool { return strings.HasPrefix(s, prefix) }
|
||||
|
||||
// IsStartOf alias of the strings.HasPrefix
|
||||
func IsStartOf(s, prefix string) bool { return strings.HasPrefix(s, prefix) }
|
||||
|
||||
// HasSuffix substr in the given string.
|
||||
func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffix) }
|
||||
|
||||
// IsEndOf alias of the strings.HasSuffix
|
||||
func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) }
|
||||
|
||||
// IsValidUtf8 valid utf8 string check
|
||||
func IsValidUtf8(s string) bool { return utf8.ValidString(s) }
|
||||
|
||||
// ----- refer from github.com/yuin/goldmark/util
|
||||
|
||||
// refer from github.com/yuin/goldmark/util
|
||||
var spaceTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
// IsSpace returns true if the given character is a space, otherwise false.
|
||||
func IsSpace(c byte) bool {
|
||||
return spaceTable[c] == 1
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the given string is empty.
|
||||
func IsEmpty(s string) bool { return len(s) == 0 }
|
||||
|
||||
// IsBlank returns true if the given string is all space characters.
|
||||
func IsBlank(s string) bool {
|
||||
return IsBlankBytes([]byte(s))
|
||||
}
|
||||
|
||||
// IsNotBlank returns true if the given string is not blank.
|
||||
func IsNotBlank(s string) bool {
|
||||
return !IsBlankBytes([]byte(s))
|
||||
}
|
||||
|
||||
// IsBlankBytes returns true if the given []byte is all space characters.
|
||||
func IsBlankBytes(bs []byte) bool {
|
||||
for _, b := range bs {
|
||||
if !IsSpace(b) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsSymbol reports whether the rune is a symbolic character.
|
||||
func IsSymbol(r rune) bool {
|
||||
return unicode.IsSymbol(r)
|
||||
}
|
||||
|
||||
var verRegex = regexp.MustCompile(`^[0-9][\d.]+(-\w+)?$`)
|
||||
|
||||
// IsVersion number. eg: 1.2.0
|
||||
func IsVersion(s string) bool {
|
||||
return verRegex.MatchString(s)
|
||||
}
|
||||
|
||||
// Compare for two string.
|
||||
func Compare(s1, s2, op string) bool {
|
||||
return VersionCompare(s1, s2, op)
|
||||
}
|
||||
|
||||
// VersionCompare for two version string.
|
||||
func VersionCompare(v1, v2, op string) bool {
|
||||
switch op {
|
||||
case ">", "gt":
|
||||
return v1 > v2
|
||||
case "<", "lt":
|
||||
return v1 < v2
|
||||
case ">=", "gte":
|
||||
return v1 >= v2
|
||||
case "<=", "lte":
|
||||
return v1 <= v2
|
||||
case "!=", "ne", "neq":
|
||||
return v1 != v2
|
||||
default: // eq
|
||||
return v1 == v2
|
||||
}
|
||||
}
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gookit/goutil/comdef"
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
"github.com/gookit/goutil/mathutil"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDateLayout = errors.New("invalid date layout string")
|
||||
ErrInvalidParam = errors.New("invalid input parameter")
|
||||
|
||||
// some regex for convert string.
|
||||
toSnakeReg = regexp.MustCompile("[A-Z][a-z]")
|
||||
toCamelRegs = map[string]*regexp.Regexp{
|
||||
" ": regexp.MustCompile(" +[a-zA-Z]"),
|
||||
"-": regexp.MustCompile("-+[a-zA-Z]"),
|
||||
"_": regexp.MustCompile("_+[a-zA-Z]"),
|
||||
}
|
||||
)
|
||||
|
||||
// Internal func refers:
|
||||
// strconv.QuoteRune()
|
||||
// strconv.QuoteToASCII()
|
||||
// strconv.AppendQuote()
|
||||
// strconv.AppendQuoteRune()
|
||||
|
||||
// Quote alias of strings.Quote
|
||||
func Quote(s string) string { return strconv.Quote(s) }
|
||||
|
||||
// Unquote remove start and end quotes by single-quote or double-quote
|
||||
func Unquote(s string) string {
|
||||
ln := len(s)
|
||||
if ln < 2 {
|
||||
return s
|
||||
}
|
||||
|
||||
qs, qe := s[0], s[ln-1]
|
||||
|
||||
var valid bool
|
||||
if qs == '"' && qe == '"' {
|
||||
valid = true
|
||||
} else if qs == '\'' && qe == '\'' {
|
||||
valid = true
|
||||
}
|
||||
|
||||
if valid {
|
||||
s = s[1 : ln-1] // exclude quotes
|
||||
}
|
||||
// strconv.Unquote cannot unquote single-quote
|
||||
// if ns, err := strconv.Unquote(s); err == nil {
|
||||
// return ns
|
||||
// }
|
||||
return s
|
||||
}
|
||||
|
||||
// Join alias of strings.Join
|
||||
func Join(sep string, ss ...string) string { return strings.Join(ss, sep) }
|
||||
|
||||
// JoinList alias of strings.Join
|
||||
func JoinList(sep string, ss []string) string { return strings.Join(ss, sep) }
|
||||
|
||||
// Implode alias of strings.Join
|
||||
func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) }
|
||||
|
||||
/*************************************************************
|
||||
* convert value to string
|
||||
*************************************************************/
|
||||
|
||||
// String convert val to string
|
||||
func String(val any) (string, error) {
|
||||
return AnyToString(val, true)
|
||||
}
|
||||
|
||||
// QuietString convert value to string, will ignore error
|
||||
func QuietString(in any) string {
|
||||
val, _ := AnyToString(in, false)
|
||||
return val
|
||||
}
|
||||
|
||||
// MustString convert value to string, TODO will panic on error
|
||||
func MustString(in any) string {
|
||||
val, _ := AnyToString(in, false)
|
||||
return val
|
||||
}
|
||||
|
||||
// StringOrErr convert value to string, return error on failed
|
||||
func StringOrErr(val any) (string, error) {
|
||||
return AnyToString(val, true)
|
||||
}
|
||||
|
||||
// ToString convert value to string
|
||||
func ToString(val any) (string, error) {
|
||||
return AnyToString(val, true)
|
||||
}
|
||||
|
||||
// AnyToString convert value to string.
|
||||
//
|
||||
// if defaultAsErr:
|
||||
//
|
||||
// False will use fmt.Sprint convert complex type
|
||||
// True will return error on fail.
|
||||
func AnyToString(val any, defaultAsErr bool) (str string, err error) {
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch value := val.(type) {
|
||||
case int:
|
||||
str = strconv.Itoa(value)
|
||||
case int8:
|
||||
str = strconv.Itoa(int(value))
|
||||
case int16:
|
||||
str = strconv.Itoa(int(value))
|
||||
case int32: // same as `rune`
|
||||
str = strconv.Itoa(int(value))
|
||||
case int64:
|
||||
str = strconv.FormatInt(value, 10)
|
||||
case uint:
|
||||
str = strconv.FormatUint(uint64(value), 10)
|
||||
case uint8:
|
||||
str = strconv.FormatUint(uint64(value), 10)
|
||||
case uint16:
|
||||
str = strconv.FormatUint(uint64(value), 10)
|
||||
case uint32:
|
||||
str = strconv.FormatUint(uint64(value), 10)
|
||||
case uint64:
|
||||
str = strconv.FormatUint(value, 10)
|
||||
case float32:
|
||||
str = strconv.FormatFloat(float64(value), 'f', -1, 32)
|
||||
case float64:
|
||||
str = strconv.FormatFloat(value, 'f', -1, 64)
|
||||
case bool:
|
||||
str = strconv.FormatBool(value)
|
||||
case string:
|
||||
str = value
|
||||
case []byte:
|
||||
str = string(value)
|
||||
case time.Duration:
|
||||
str = value.String()
|
||||
case json.Number:
|
||||
str = value.String()
|
||||
default:
|
||||
if defaultAsErr {
|
||||
err = comdef.ErrConvType
|
||||
} else {
|
||||
str = fmt.Sprint(value)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* convert string value to byte
|
||||
* refer from https://github.com/valyala/fastjson/blob/master/util.go
|
||||
*************************************************************/
|
||||
|
||||
// Byte2str convert bytes to string
|
||||
func Byte2str(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
// Byte2string convert bytes to string
|
||||
func Byte2string(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
// ToBytes convert string to bytes
|
||||
func ToBytes(s string) (b []byte) {
|
||||
strh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
|
||||
sh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
sh.Data = strh.Data
|
||||
sh.Len = strh.Len
|
||||
sh.Cap = strh.Len
|
||||
return b
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* convert string value to bool
|
||||
*************************************************************/
|
||||
|
||||
// ToBool convert string to bool
|
||||
func ToBool(s string) (bool, error) {
|
||||
return comfunc.StrToBool(s)
|
||||
}
|
||||
|
||||
// QuietBool convert to bool, will ignore error
|
||||
func QuietBool(s string) bool {
|
||||
val, _ := comfunc.StrToBool(strings.TrimSpace(s))
|
||||
return val
|
||||
}
|
||||
|
||||
// MustBool convert, will panic on error
|
||||
func MustBool(s string) bool {
|
||||
val, _ := comfunc.StrToBool(strings.TrimSpace(s))
|
||||
return val
|
||||
}
|
||||
|
||||
// Bool parse string to bool. like strconv.ParseBool()
|
||||
func Bool(s string) (bool, error) {
|
||||
return comfunc.StrToBool(s)
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* convert string value to int, float
|
||||
*************************************************************/
|
||||
|
||||
// Int convert string to int, alias of ToInt()
|
||||
func Int(s string) (int, error) {
|
||||
return strconv.Atoi(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
// ToInt convert string to int, return error on fail
|
||||
func ToInt(s string) (int, error) {
|
||||
return strconv.Atoi(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
// QuietInt convert string to int, will ignore error
|
||||
func QuietInt(s string) int {
|
||||
val, _ := ToInt(s)
|
||||
return val
|
||||
}
|
||||
|
||||
// MustInt convert string to int, will panic on error
|
||||
func MustInt(s string) int {
|
||||
val, _ := ToInt(s)
|
||||
return val
|
||||
}
|
||||
|
||||
// IntOrPanic convert value to int, will panic on error
|
||||
func IntOrPanic(s string) int {
|
||||
val, err := ToInt(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// Int64 convert string to int, will ignore error
|
||||
func Int64(s string) int64 {
|
||||
val, _ := Int64OrErr(s)
|
||||
return val
|
||||
}
|
||||
|
||||
// QuietInt64 convert string to int, will ignore error
|
||||
func QuietInt64(s string) int64 {
|
||||
val, _ := Int64OrErr(s)
|
||||
return val
|
||||
}
|
||||
|
||||
// ToInt64 convert string to int, return error on fail
|
||||
func ToInt64(s string) (int64, error) {
|
||||
return strconv.ParseInt(s, 10, 0)
|
||||
}
|
||||
|
||||
// Int64OrErr convert string to int, return error on fail
|
||||
func Int64OrErr(s string) (int64, error) {
|
||||
return strconv.ParseInt(s, 10, 0)
|
||||
}
|
||||
|
||||
// MustInt64 convert value to int, will panic on error
|
||||
func MustInt64(s string) int64 {
|
||||
return Int64OrPanic(s)
|
||||
}
|
||||
|
||||
// Int64OrPanic convert value to int, will panic on error
|
||||
func Int64OrPanic(s string) int64 {
|
||||
val, err := strconv.ParseInt(s, 10, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* convert string value to int/string slice, time.Time
|
||||
*************************************************************/
|
||||
|
||||
// Ints alias of the ToIntSlice(). default sep is comma(,)
|
||||
func Ints(s string, sep ...string) []int {
|
||||
ints, _ := ToIntSlice(s, sep...)
|
||||
return ints
|
||||
}
|
||||
|
||||
// ToInts alias of the ToIntSlice(). default sep is comma(,)
|
||||
func ToInts(s string, sep ...string) ([]int, error) { return ToIntSlice(s, sep...) }
|
||||
|
||||
// ToIntSlice split string to slice and convert item to int.
|
||||
//
|
||||
// Default sep is comma(,)
|
||||
func ToIntSlice(s string, sep ...string) (ints []int, err error) {
|
||||
ss := ToSlice(s, sep...)
|
||||
for _, item := range ss {
|
||||
iVal, err := mathutil.ToInt(item)
|
||||
if err != nil {
|
||||
return []int{}, err
|
||||
}
|
||||
|
||||
ints = append(ints, iVal)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToArray alias of the ToSlice()
|
||||
func ToArray(s string, sep ...string) []string { return ToSlice(s, sep...) }
|
||||
|
||||
// Strings alias of the ToSlice()
|
||||
func Strings(s string, sep ...string) []string { return ToSlice(s, sep...) }
|
||||
|
||||
// ToStrings alias of the ToSlice()
|
||||
func ToStrings(s string, sep ...string) []string { return ToSlice(s, sep...) }
|
||||
|
||||
// ToSlice split string to array.
|
||||
func ToSlice(s string, sep ...string) []string {
|
||||
if len(sep) > 0 {
|
||||
return Split(s, sep[0])
|
||||
}
|
||||
return Split(s, ",")
|
||||
}
|
||||
|
||||
// ToOSArgs split string to string[](such as os.Args)
|
||||
// func ToOSArgs(s string) []string {
|
||||
// return cliutil.StringToOSArgs(s) // error: import cycle not allowed
|
||||
// }
|
||||
|
||||
// MustToTime convert date string to time.Time
|
||||
func MustToTime(s string, layouts ...string) time.Time {
|
||||
t, err := ToTime(s, layouts...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// auto match use some commonly layouts.
|
||||
// key is layout length.
|
||||
var layoutMap = map[int][]string{
|
||||
6: {"200601", "060102", time.Kitchen},
|
||||
8: {"20060102"},
|
||||
10: {"2006-01-02"},
|
||||
13: {"2006-01-02 15"},
|
||||
15: {time.Stamp},
|
||||
16: {"2006-01-02 15:04"},
|
||||
19: {"2006-01-02 15:04:05", time.RFC822, time.StampMilli},
|
||||
20: {"2006-01-02 15:04:05Z"},
|
||||
21: {time.RFC822Z},
|
||||
22: {time.StampMicro},
|
||||
24: {time.ANSIC},
|
||||
25: {time.RFC3339, time.StampNano},
|
||||
// 26: {time.Layout}, // must go >= 1.19
|
||||
28: {time.UnixDate},
|
||||
29: {time.RFC1123},
|
||||
30: {time.RFC850},
|
||||
31: {time.RFC1123Z},
|
||||
35: {time.RFC3339Nano},
|
||||
}
|
||||
|
||||
// ToTime convert date string to time.Time
|
||||
func ToTime(s string, layouts ...string) (t time.Time, err error) {
|
||||
// custom layout
|
||||
if len(layouts) > 0 {
|
||||
if len(layouts[0]) > 0 {
|
||||
return time.Parse(layouts[0], s)
|
||||
}
|
||||
|
||||
err = ErrDateLayout
|
||||
return
|
||||
}
|
||||
|
||||
// auto match use some commonly layouts.
|
||||
strLn := len(s)
|
||||
maybeLayouts, ok := layoutMap[strLn]
|
||||
if !ok {
|
||||
err = ErrInvalidParam
|
||||
return
|
||||
}
|
||||
|
||||
var hasAlphaT bool
|
||||
if pos := strings.IndexByte(s, 'T'); pos > 0 && pos < 12 {
|
||||
hasAlphaT = true
|
||||
}
|
||||
|
||||
hasSlashR := strings.IndexByte(s, '/') > 0
|
||||
for _, layout := range maybeLayouts {
|
||||
// date string has "T". eg: "2006-01-02T15:04:05"
|
||||
if hasAlphaT {
|
||||
layout = strings.Replace(layout, " ", "T", 1)
|
||||
}
|
||||
|
||||
// date string has "/". eg: "2006/01/02 15:04:05"
|
||||
if hasSlashR {
|
||||
layout = strings.Replace(layout, "-", "/", -1)
|
||||
}
|
||||
|
||||
t, err = time.Parse(layout, s)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// t, err = time.ParseInLocation(layout, s, time.Local)
|
||||
return
|
||||
}
|
||||
|
||||
// ToDuration parses a duration string. such as "300ms", "-1.5h" or "2h45m".
|
||||
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
func ToDuration(s string) (time.Duration, error) {
|
||||
return time.ParseDuration(s)
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
//
|
||||
// -------------------- escape --------------------
|
||||
//
|
||||
|
||||
// EscapeJS escape javascript string
|
||||
func EscapeJS(s string) string {
|
||||
return template.JSEscapeString(s)
|
||||
}
|
||||
|
||||
// EscapeHTML escape html string
|
||||
func EscapeHTML(s string) string {
|
||||
return template.HTMLEscapeString(s)
|
||||
}
|
||||
|
||||
// AddSlashes add slashes for the string.
|
||||
func AddSlashes(s string) string {
|
||||
if ln := len(s); ln == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, char := range s {
|
||||
switch char {
|
||||
case '\'', '"', '\\':
|
||||
buf.WriteRune('\\')
|
||||
}
|
||||
buf.WriteRune(char)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// StripSlashes strip slashes for the string.
|
||||
func StripSlashes(s string) string {
|
||||
ln := len(s)
|
||||
if ln == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var skip bool
|
||||
var buf bytes.Buffer
|
||||
|
||||
for i, char := range s {
|
||||
if skip {
|
||||
skip = false
|
||||
} else if char == '\\' {
|
||||
if i+1 < ln && s[i+1] == '\\' {
|
||||
skip = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
buf.WriteRune(char)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
//
|
||||
// -------------------- encode --------------------
|
||||
//
|
||||
|
||||
// Md5 Generate a 32-bit md5 string
|
||||
func Md5(src any) string {
|
||||
return hex.EncodeToString(Md5Bytes(src))
|
||||
}
|
||||
|
||||
// MD5 Generate a 32-bit md5 string
|
||||
func MD5(src any) string { return Md5(src) }
|
||||
|
||||
// GenMd5 Generate a 32-bit md5 string
|
||||
func GenMd5(src any) string { return Md5(src) }
|
||||
|
||||
// Md5Bytes Generate a 32-bit md5 bytes
|
||||
func Md5Bytes(src any) []byte {
|
||||
h := md5.New()
|
||||
if s, ok := src.(string); ok {
|
||||
h.Write([]byte(s))
|
||||
} else {
|
||||
h.Write([]byte(fmt.Sprint(src)))
|
||||
}
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// URLEncode encode url string.
|
||||
func URLEncode(s string) string {
|
||||
if pos := strings.IndexRune(s, '?'); pos > -1 { // escape query data
|
||||
return s[0:pos+1] + url.QueryEscape(s[pos+1:])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// URLDecode decode url string.
|
||||
func URLDecode(s string) string {
|
||||
if pos := strings.IndexRune(s, '?'); pos > -1 { // un-escape query data
|
||||
qy, err := url.QueryUnescape(s[pos+1:])
|
||||
if err == nil {
|
||||
return s[0:pos+1] + qy
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
//
|
||||
// -------------------- base encode --------------------
|
||||
//
|
||||
|
||||
// B32Encode base32 encode
|
||||
func B32Encode(str string) string {
|
||||
return base32.StdEncoding.EncodeToString([]byte(str))
|
||||
}
|
||||
|
||||
// B32Decode base32 decode
|
||||
func B32Decode(str string) string {
|
||||
dec, _ := base32.StdEncoding.DecodeString(str)
|
||||
return string(dec)
|
||||
}
|
||||
|
||||
// B64Encode base64 encode
|
||||
func B64Encode(str string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(str))
|
||||
}
|
||||
|
||||
// B64EncodeBytes base64 encode
|
||||
func B64EncodeBytes(src []byte) []byte {
|
||||
buf := make([]byte, base64.StdEncoding.EncodedLen(len(src)))
|
||||
base64.StdEncoding.Encode(buf, src)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// B64Decode base64 decode
|
||||
func B64Decode(str string) string {
|
||||
dec, _ := base64.StdEncoding.DecodeString(str)
|
||||
return string(dec)
|
||||
}
|
||||
|
||||
// B64DecodeBytes base64 decode
|
||||
func B64DecodeBytes(str string) []byte {
|
||||
dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(str)))
|
||||
n, _ := base64.StdEncoding.Decode(dbuf, []byte(str))
|
||||
|
||||
return dbuf[:n]
|
||||
}
|
||||
|
||||
// BaseEncoder interface
|
||||
type BaseEncoder interface {
|
||||
Encode(dst []byte, src []byte)
|
||||
EncodeToString(src []byte) string
|
||||
Decode(dst []byte, src []byte) (n int, err error)
|
||||
DecodeString(s string) ([]byte, error)
|
||||
}
|
||||
|
||||
// BaseType for base encoding
|
||||
type BaseType uint8
|
||||
|
||||
// types for base encoding
|
||||
const (
|
||||
BaseTypeStd BaseType = iota
|
||||
BaseTypeHex
|
||||
BaseTypeURL
|
||||
BaseTypeRawStd
|
||||
BaseTypeRawURL
|
||||
)
|
||||
|
||||
// Encoding instance
|
||||
func Encoding(base int, typ BaseType) BaseEncoder {
|
||||
if base == 32 {
|
||||
switch typ {
|
||||
case BaseTypeHex:
|
||||
return base32.HexEncoding
|
||||
default:
|
||||
return base32.StdEncoding
|
||||
}
|
||||
}
|
||||
|
||||
// base 64
|
||||
switch typ {
|
||||
case BaseTypeURL:
|
||||
return base64.URLEncoding
|
||||
case BaseTypeRawURL:
|
||||
return base64.RawURLEncoding
|
||||
case BaseTypeRawStd:
|
||||
return base64.RawStdEncoding
|
||||
default:
|
||||
return base64.StdEncoding
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package strutil
|
||||
|
||||
import "strings"
|
||||
|
||||
/*************************************************************
|
||||
* String filtering
|
||||
*************************************************************/
|
||||
|
||||
// Trim string. if cutSet is empty, will trim SPACE.
|
||||
func Trim(s string, cutSet ...string) string {
|
||||
if ln := len(cutSet); ln > 0 && cutSet[0] != "" {
|
||||
if ln == 1 {
|
||||
return strings.Trim(s, cutSet[0])
|
||||
}
|
||||
|
||||
return strings.Trim(s, strings.Join(cutSet, ""))
|
||||
}
|
||||
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
// Ltrim alias of TrimLeft
|
||||
func Ltrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) }
|
||||
|
||||
// LTrim alias of TrimLeft
|
||||
func LTrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) }
|
||||
|
||||
// TrimLeft char in the string. if cutSet is empty, will trim SPACE.
|
||||
func TrimLeft(s string, cutSet ...string) string {
|
||||
if ln := len(cutSet); ln > 0 && cutSet[0] != "" {
|
||||
if ln == 1 {
|
||||
return strings.TrimLeft(s, cutSet[0])
|
||||
}
|
||||
|
||||
return strings.TrimLeft(s, strings.Join(cutSet, ""))
|
||||
}
|
||||
|
||||
return strings.TrimLeft(s, " ")
|
||||
}
|
||||
|
||||
// Rtrim alias of TrimRight
|
||||
func Rtrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) }
|
||||
|
||||
// RTrim alias of TrimRight
|
||||
func RTrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) }
|
||||
|
||||
// TrimRight char in the string. if cutSet is empty, will trim SPACE.
|
||||
func TrimRight(s string, cutSet ...string) string {
|
||||
if ln := len(cutSet); ln > 0 && cutSet[0] != "" {
|
||||
if ln == 1 {
|
||||
return strings.TrimRight(s, cutSet[0])
|
||||
}
|
||||
return strings.TrimRight(s, strings.Join(cutSet, ""))
|
||||
}
|
||||
|
||||
return strings.TrimRight(s, " ")
|
||||
}
|
||||
|
||||
// FilterEmail filter email, clear invalid chars.
|
||||
func FilterEmail(s string) string {
|
||||
s = strings.TrimSpace(s)
|
||||
i := strings.LastIndex(s, "@")
|
||||
if i == -1 {
|
||||
return s
|
||||
}
|
||||
|
||||
// According to rfc5321, "The local-part of a mailbox MUST BE treated as case sensitive"
|
||||
return s[0:i] + "@" + strings.ToLower(s[i+1:])
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
/*************************************************************
|
||||
* change string case
|
||||
*************************************************************/
|
||||
|
||||
// methods aliases
|
||||
var (
|
||||
UpWords = UpperWord
|
||||
LoFirst = LowerFirst
|
||||
UpFirst = UpperFirst
|
||||
|
||||
Snake = SnakeCase
|
||||
)
|
||||
|
||||
// Title alias of the strings.ToTitle()
|
||||
func Title(s string) string { return strings.ToTitle(s) }
|
||||
|
||||
// Lower alias of the strings.ToLower()
|
||||
func Lower(s string) string { return strings.ToLower(s) }
|
||||
|
||||
// Lowercase alias of the strings.ToLower()
|
||||
func Lowercase(s string) string { return strings.ToLower(s) }
|
||||
|
||||
// Upper alias of the strings.ToUpper()
|
||||
func Upper(s string) string { return strings.ToUpper(s) }
|
||||
|
||||
// Uppercase alias of the strings.ToUpper()
|
||||
func Uppercase(s string) string { return strings.ToUpper(s) }
|
||||
|
||||
// UpperWord Change the first character of each word to uppercase
|
||||
func UpperWord(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
if len(s) == 1 {
|
||||
return strings.ToUpper(s)
|
||||
}
|
||||
|
||||
inWord := true
|
||||
buf := make([]byte, 0, len(s))
|
||||
|
||||
i := 0
|
||||
rs := []rune(s)
|
||||
if RuneIsLower(rs[i]) {
|
||||
buf = append(buf, []byte(string(unicode.ToUpper(rs[i])))...)
|
||||
} else {
|
||||
buf = append(buf, []byte(string(rs[i]))...)
|
||||
}
|
||||
|
||||
for j := i + 1; j < len(rs); j++ {
|
||||
if !RuneIsWord(rs[i]) && RuneIsWord(rs[j]) {
|
||||
inWord = false
|
||||
}
|
||||
|
||||
if RuneIsLower(rs[j]) && !inWord {
|
||||
buf = append(buf, []byte(string(unicode.ToUpper(rs[j])))...)
|
||||
inWord = true
|
||||
} else {
|
||||
buf = append(buf, []byte(string(rs[j]))...)
|
||||
}
|
||||
|
||||
if RuneIsWord(rs[j]) {
|
||||
inWord = true
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// LowerFirst lower first char
|
||||
func LowerFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
rs := []rune(s)
|
||||
f := rs[0]
|
||||
|
||||
if 'A' <= f && f <= 'Z' {
|
||||
return string(unicode.ToLower(f)) + string(rs[1:])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// UpperFirst upper first char
|
||||
func UpperFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
rs := []rune(s)
|
||||
f := rs[0]
|
||||
|
||||
if 'a' <= f && f <= 'z' {
|
||||
return string(unicode.ToUpper(f)) + string(rs[1:])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SnakeCase convert. eg "RangePrice" -> "range_price"
|
||||
func SnakeCase(s string, sep ...string) string {
|
||||
sepChar := "_"
|
||||
if len(sep) > 0 {
|
||||
sepChar = sep[0]
|
||||
}
|
||||
|
||||
str := toSnakeReg.ReplaceAllStringFunc(s, func(s string) string {
|
||||
return sepChar + LowerFirst(s)
|
||||
})
|
||||
|
||||
return strings.TrimLeft(str, sepChar)
|
||||
}
|
||||
|
||||
// Camel alias of the CamelCase
|
||||
func Camel(s string, sep ...string) string { return CamelCase(s, sep...) }
|
||||
|
||||
// CamelCase convert string to camel case.
|
||||
//
|
||||
// Support:
|
||||
//
|
||||
// "range_price" -> "rangePrice"
|
||||
// "range price" -> "rangePrice"
|
||||
// "range-price" -> "rangePrice"
|
||||
func CamelCase(s string, sep ...string) string {
|
||||
sepChar := "_"
|
||||
if len(sep) > 0 {
|
||||
sepChar = sep[0]
|
||||
}
|
||||
|
||||
// Not contains sep char
|
||||
if !strings.Contains(s, sepChar) {
|
||||
return s
|
||||
}
|
||||
|
||||
// Get regexp instance
|
||||
rgx, ok := toCamelRegs[sepChar]
|
||||
if !ok {
|
||||
rgx = regexp.MustCompile(regexp.QuoteMeta(sepChar) + "+[a-zA-Z]")
|
||||
}
|
||||
|
||||
return rgx.ReplaceAllStringFunc(s, func(s string) string {
|
||||
s = strings.TrimLeft(s, sepChar)
|
||||
return UpperFirst(s)
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Indent format multi line text
|
||||
// from package: github.com/kr/text
|
||||
//
|
||||
|
||||
// Indent inserts prefix at the beginning of each non-empty line of s. The
|
||||
// end-of-line marker is NL.
|
||||
func Indent(s, prefix string) string {
|
||||
return string(IndentBytes([]byte(s), []byte(prefix)))
|
||||
}
|
||||
|
||||
// IndentBytes inserts prefix at the beginning of each non-empty line of b.
|
||||
// The end-of-line marker is NL.
|
||||
func IndentBytes(b, prefix []byte) []byte {
|
||||
if len(b) == 0 {
|
||||
return b
|
||||
}
|
||||
|
||||
bol := true
|
||||
res := make([]byte, 0, len(b)+len(prefix)*4)
|
||||
|
||||
for _, c := range b {
|
||||
if bol && c != '\n' {
|
||||
res = append(res, prefix...)
|
||||
}
|
||||
res = append(res, c)
|
||||
bol = c == '\n'
|
||||
}
|
||||
return res
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gookit/goutil/mathutil"
|
||||
)
|
||||
|
||||
// global id:
|
||||
//
|
||||
// https://github.com/rs/xid
|
||||
// https://github.com/satori/go.uuid
|
||||
var (
|
||||
DefMinInt = 1000
|
||||
DefMaxInt = 9999
|
||||
)
|
||||
|
||||
// MicroTimeID generate.
|
||||
// return like: 16074145697981929446(len: 20)
|
||||
func MicroTimeID() string {
|
||||
ms := time.Now().UnixNano() / 1000
|
||||
ri := mathutil.RandomInt(DefMinInt, DefMaxInt)
|
||||
|
||||
return strconv.FormatInt(ms, 10) + strconv.FormatInt(int64(ri), 10)
|
||||
}
|
||||
|
||||
// MicroTimeHexID generate.
|
||||
// return like: 5b5f0588af1761ad3(len: 16-17)
|
||||
func MicroTimeHexID() string {
|
||||
ms := time.Now().UnixNano() / 1000
|
||||
ri := mathutil.RandomInt(DefMinInt, DefMaxInt)
|
||||
|
||||
return strconv.FormatInt(ms, 16) + strconv.FormatInt(int64(ri), 16)
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PosFlag type
|
||||
type PosFlag uint8
|
||||
|
||||
// Position for padding/resize string
|
||||
const (
|
||||
PosLeft PosFlag = iota
|
||||
PosRight
|
||||
PosMiddle
|
||||
)
|
||||
|
||||
/*************************************************************
|
||||
* String padding operation
|
||||
*************************************************************/
|
||||
|
||||
// Padding a string.
|
||||
func Padding(s, pad string, length int, pos PosFlag) string {
|
||||
diff := len(s) - length
|
||||
if diff >= 0 { // do not need padding.
|
||||
return s
|
||||
}
|
||||
|
||||
if pad == "" || pad == " " {
|
||||
mark := ""
|
||||
if pos == PosRight { // to right
|
||||
mark = "-"
|
||||
}
|
||||
|
||||
// padding left: "%7s", padding right: "%-7s"
|
||||
tpl := fmt.Sprintf("%s%d", mark, length)
|
||||
return fmt.Sprintf(`%`+tpl+`s`, s)
|
||||
}
|
||||
|
||||
if pos == PosRight { // to right
|
||||
return s + Repeat(pad, -diff)
|
||||
}
|
||||
return Repeat(pad, -diff) + s
|
||||
}
|
||||
|
||||
// PadLeft a string.
|
||||
func PadLeft(s, pad string, length int) string {
|
||||
return Padding(s, pad, length, PosLeft)
|
||||
}
|
||||
|
||||
// PadRight a string.
|
||||
func PadRight(s, pad string, length int) string {
|
||||
return Padding(s, pad, length, PosRight)
|
||||
}
|
||||
|
||||
// Resize a string by given length and align settings. padding space.
|
||||
func Resize(s string, length int, align PosFlag) string {
|
||||
diff := len(s) - length
|
||||
if diff >= 0 { // do not need padding.
|
||||
return s
|
||||
}
|
||||
|
||||
if align == PosMiddle {
|
||||
strLn := len(s)
|
||||
padLn := (length - strLn) / 2
|
||||
padStr := string(make([]byte, padLn))
|
||||
|
||||
if diff := length - padLn*2; diff > 0 {
|
||||
s += " "
|
||||
}
|
||||
return padStr + s + padStr
|
||||
}
|
||||
|
||||
return Padding(s, " ", length, align)
|
||||
}
|
||||
|
||||
// PadChars padding a rune/byte to want length and with position flag
|
||||
func PadChars[T byte | rune](cs []T, pad T, length int, pos PosFlag) []T {
|
||||
ln := len(cs)
|
||||
if ln >= length {
|
||||
ns := make([]T, length)
|
||||
copy(ns, cs[:length])
|
||||
return ns
|
||||
}
|
||||
|
||||
idx := length - ln
|
||||
ns := make([]T, length)
|
||||
ps := RepeatChars(pad, idx)
|
||||
if pos == PosRight {
|
||||
copy(ns, cs)
|
||||
copy(ns[idx:], ps)
|
||||
} else { // to left
|
||||
copy(ns[:idx], ps)
|
||||
copy(ns[idx:], cs)
|
||||
}
|
||||
|
||||
return ns
|
||||
}
|
||||
|
||||
// PadBytes padding a byte to want length and with position flag
|
||||
func PadBytes(bs []byte, pad byte, length int, pos PosFlag) []byte {
|
||||
return PadChars(bs, pad, length, pos)
|
||||
}
|
||||
|
||||
// PadBytesLeft a byte to want length
|
||||
func PadBytesLeft(bs []byte, pad byte, length int) []byte {
|
||||
return PadChars(bs, pad, length, PosLeft)
|
||||
}
|
||||
|
||||
// PadBytesRight a byte to want length
|
||||
func PadBytesRight(bs []byte, pad byte, length int) []byte {
|
||||
return PadChars(bs, pad, length, PosRight)
|
||||
}
|
||||
|
||||
// PadRunes padding a rune to want length and with position flag
|
||||
func PadRunes(rs []rune, pad rune, length int, pos PosFlag) []rune {
|
||||
return PadChars(rs, pad, length, pos)
|
||||
}
|
||||
|
||||
// PadRunesLeft a rune to want length
|
||||
func PadRunesLeft(rs []rune, pad rune, length int) []rune {
|
||||
return PadChars(rs, pad, length, PosLeft)
|
||||
}
|
||||
|
||||
// PadRunesRight a rune to want length
|
||||
func PadRunesRight(rs []rune, pad rune, length int) []rune {
|
||||
return PadChars(rs, pad, length, PosRight)
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* String repeat operation
|
||||
*************************************************************/
|
||||
|
||||
// Repeat a string
|
||||
func Repeat(s string, times int) string {
|
||||
if times <= 0 {
|
||||
return ""
|
||||
}
|
||||
if times == 1 {
|
||||
return s
|
||||
}
|
||||
|
||||
ss := make([]string, 0, times)
|
||||
for i := 0; i < times; i++ {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
|
||||
return strings.Join(ss, "")
|
||||
}
|
||||
|
||||
// RepeatRune repeat a rune char.
|
||||
func RepeatRune(char rune, times int) []rune {
|
||||
return RepeatChars(char, times)
|
||||
}
|
||||
|
||||
// RepeatBytes repeat a byte char.
|
||||
func RepeatBytes(char byte, times int) []byte {
|
||||
return RepeatChars(char, times)
|
||||
}
|
||||
|
||||
// RepeatChars repeat a byte char.
|
||||
func RepeatChars[T byte | rune](char T, times int) []T {
|
||||
chars := make([]T, 0, times)
|
||||
for i := 0; i < times; i++ {
|
||||
chars = append(chars, char)
|
||||
}
|
||||
return chars
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
mathRand "math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// some consts string chars
|
||||
const (
|
||||
Numbers = "0123456789"
|
||||
AlphaBet = "abcdefghijklmnopqrstuvwxyz"
|
||||
AlphaBet1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
|
||||
AlphaNum = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
AlphaNum2 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
AlphaNum3 = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
|
||||
)
|
||||
|
||||
// RandomChars generate give length random chars at `a-z`
|
||||
func RandomChars(ln int) string {
|
||||
cs := make([]byte, ln)
|
||||
for i := 0; i < ln; i++ {
|
||||
// 1607400451937462000
|
||||
mathRand.Seed(time.Now().UnixNano())
|
||||
idx := mathRand.Intn(25) // 0 - 25
|
||||
cs[i] = AlphaBet[idx]
|
||||
}
|
||||
|
||||
return string(cs)
|
||||
}
|
||||
|
||||
// RandomCharsV2 generate give length random chars in `0-9a-z`
|
||||
func RandomCharsV2(ln int) string {
|
||||
cs := make([]byte, ln)
|
||||
for i := 0; i < ln; i++ {
|
||||
// 1607400451937462000
|
||||
mathRand.Seed(time.Now().UnixNano())
|
||||
idx := mathRand.Intn(35) // 0 - 35
|
||||
cs[i] = AlphaNum[idx]
|
||||
}
|
||||
|
||||
return string(cs)
|
||||
}
|
||||
|
||||
// RandomCharsV3 generate give length random chars in `0-9a-zA-Z`
|
||||
func RandomCharsV3(ln int) string {
|
||||
cs := make([]byte, ln)
|
||||
for i := 0; i < ln; i++ {
|
||||
// 1607400451937462000
|
||||
mathRand.Seed(time.Now().UnixNano())
|
||||
idx := mathRand.Intn(61) // 0 - 61
|
||||
cs[i] = AlphaNum2[idx]
|
||||
}
|
||||
|
||||
return string(cs)
|
||||
}
|
||||
|
||||
// RandomBytes generate
|
||||
func RandomBytes(length int) ([]byte, error) {
|
||||
b := make([]byte, length)
|
||||
_, err := rand.Read(b)
|
||||
// Note that err == nil only if we read len(b) bytes.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// RandomString generate.
|
||||
// Example:
|
||||
//
|
||||
// // this will give us a 44 byte, base64 encoded output
|
||||
// token, err := RandomString(32)
|
||||
// if err != nil {
|
||||
// // Serve an appropriately vague error to the
|
||||
// // user, but log the details internally.
|
||||
// }
|
||||
func RandomString(length int) (string, error) {
|
||||
b, err := RandomBytes(length)
|
||||
return base64.URLEncoding.EncodeToString(b), err
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/width"
|
||||
)
|
||||
|
||||
// RuneIsWord char: a-zA-Z
|
||||
func RuneIsWord(c rune) bool {
|
||||
return RuneIsLower(c) || RuneIsUpper(c)
|
||||
}
|
||||
|
||||
// RuneIsLower char
|
||||
func RuneIsLower(c rune) bool {
|
||||
return 'a' <= c && c <= 'z'
|
||||
}
|
||||
|
||||
// RuneIsUpper char
|
||||
func RuneIsUpper(c rune) bool {
|
||||
return 'A' <= c && c <= 'Z'
|
||||
}
|
||||
|
||||
// RunePos alias of the strings.IndexRune
|
||||
func RunePos(s string, ru rune) int { return strings.IndexRune(s, ru) }
|
||||
|
||||
// IsSpaceRune returns true if the given rune is a space, otherwise false.
|
||||
func IsSpaceRune(r rune) bool {
|
||||
return r <= 256 && IsSpace(byte(r)) || unicode.IsSpace(r)
|
||||
}
|
||||
|
||||
// Utf8Len count of the string
|
||||
func Utf8Len(s string) int { return utf8.RuneCountInString(s) }
|
||||
|
||||
// Utf8len of the string
|
||||
func Utf8len(s string) int { return utf8.RuneCountInString(s) }
|
||||
|
||||
// RuneCount of the string
|
||||
func RuneCount(s string) int { return len([]rune(s)) }
|
||||
|
||||
// RuneWidth of the rune.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// RuneWidth('你') // 2
|
||||
// RuneWidth('a') // 1
|
||||
// RuneWidth('\n') // 0
|
||||
func RuneWidth(r rune) int {
|
||||
p := width.LookupRune(r)
|
||||
k := p.Kind()
|
||||
|
||||
// eg: "\n"
|
||||
if k == width.Neutral {
|
||||
return 0
|
||||
}
|
||||
|
||||
if k == width.EastAsianFullwidth || k == width.EastAsianWide || k == width.EastAsianAmbiguous {
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// TextWidth utf8 string width. alias of RunesWidth()
|
||||
func TextWidth(s string) int { return Utf8Width(s) }
|
||||
|
||||
// Utf8Width utf8 string width. alias of RunesWidth
|
||||
func Utf8Width(s string) int { return RunesWidth([]rune(s)) }
|
||||
|
||||
// RunesWidth utf8 runes string width.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// str := "hi,你好"
|
||||
//
|
||||
// strutil.Utf8Width(str) => 7
|
||||
// len(str) => 9
|
||||
// len([]rune(str)) = utf8.RuneCountInString(s) => 5
|
||||
func RunesWidth(rs []rune) (w int) {
|
||||
if len(rs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, runeVal := range rs {
|
||||
w += RuneWidth(runeVal)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// TextTruncate alias of the Utf8Truncate()
|
||||
func TextTruncate(s string, w int, tail string) string { return Utf8Truncate(s, w, tail) }
|
||||
|
||||
// Utf8Truncate a string with given width.
|
||||
func Utf8Truncate(s string, w int, tail string) string {
|
||||
if sw := Utf8Width(s); sw <= w {
|
||||
return s
|
||||
}
|
||||
|
||||
i := 0
|
||||
r := []rune(s)
|
||||
w -= TextWidth(tail)
|
||||
|
||||
tmpW := 0
|
||||
for ; i < len(r); i++ {
|
||||
cw := RuneWidth(r[i])
|
||||
if tmpW+cw > w {
|
||||
break
|
||||
}
|
||||
tmpW += cw
|
||||
}
|
||||
return string(r[0:i]) + tail
|
||||
}
|
||||
|
||||
// TextSplit alias of the Utf8Split()
|
||||
func TextSplit(s string, w int) []string { return Utf8Split(s, w) }
|
||||
|
||||
// Utf8Split split a string by width.
|
||||
func Utf8Split(s string, w int) []string {
|
||||
sw := Utf8Width(s)
|
||||
if sw <= w {
|
||||
return []string{s}
|
||||
}
|
||||
|
||||
tmpW := 0
|
||||
tmpS := ""
|
||||
|
||||
ss := make([]string, 0, sw/w+1)
|
||||
for _, r := range s {
|
||||
rw := RuneWidth(r)
|
||||
if tmpW+rw == w {
|
||||
tmpS += string(r)
|
||||
ss = append(ss, tmpS)
|
||||
|
||||
tmpW, tmpS = 0, "" // reset
|
||||
continue
|
||||
}
|
||||
|
||||
if tmpW+rw > w {
|
||||
ss = append(ss, tmpS)
|
||||
|
||||
// append to next line.
|
||||
tmpW, tmpS = rw, string(r)
|
||||
continue
|
||||
}
|
||||
|
||||
tmpW += rw
|
||||
tmpS += string(r)
|
||||
}
|
||||
|
||||
if tmpW > 0 {
|
||||
ss = append(ss, tmpS)
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
// TextWrap a string by "\n"
|
||||
func TextWrap(s string, w int) string { return WidthWrap(s, w) }
|
||||
|
||||
// WidthWrap a string by "\n"
|
||||
func WidthWrap(s string, w int) string {
|
||||
tmpW := 0
|
||||
out := ""
|
||||
|
||||
for _, r := range s {
|
||||
cw := RuneWidth(r)
|
||||
if r == '\n' {
|
||||
out += string(r)
|
||||
tmpW = 0
|
||||
continue
|
||||
}
|
||||
|
||||
if tmpW+cw > w {
|
||||
out += "\n"
|
||||
tmpW = 0
|
||||
out += string(r)
|
||||
tmpW += cw
|
||||
continue
|
||||
}
|
||||
|
||||
out += string(r)
|
||||
tmpW += cw
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// WordWrap text string and limit width.
|
||||
func WordWrap(s string, w int) string {
|
||||
tmpW := 0
|
||||
out := ""
|
||||
|
||||
for _, sub := range strings.Split(s, " ") {
|
||||
cw := TextWidth(sub)
|
||||
if tmpW+cw > w {
|
||||
if tmpW != 0 {
|
||||
out += "\n"
|
||||
}
|
||||
|
||||
tmpW = 0
|
||||
out += sub
|
||||
tmpW += cw
|
||||
continue
|
||||
}
|
||||
|
||||
out += sub
|
||||
tmpW += cw
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Runes data slice
|
||||
type Runes []rune
|
||||
|
||||
// Padding a rune to want length and with position
|
||||
func (rs Runes) Padding(pad rune, length int, pos PosFlag) []rune {
|
||||
return PadChars(rs, pad, length, pos)
|
||||
}
|
||||
|
||||
// PadLeft a rune to want length
|
||||
func (rs Runes) PadLeft(pad rune, length int) []rune {
|
||||
return rs.Padding(pad, length, PosLeft)
|
||||
}
|
||||
|
||||
// PadRight a rune to want length
|
||||
func (rs Runes) PadRight(pad rune, length int) []rune {
|
||||
return rs.Padding(pad, length, PosRight)
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package strutil
|
||||
|
||||
// SimilarComparator definition
|
||||
// links:
|
||||
//
|
||||
// https://github.com/mkideal/cli/blob/master/fuzzy.go
|
||||
type SimilarComparator struct {
|
||||
src, dst string
|
||||
}
|
||||
|
||||
// NewComparator create
|
||||
func NewComparator(src, dst string) *SimilarComparator {
|
||||
return &SimilarComparator{src, dst}
|
||||
}
|
||||
|
||||
// Similar by minDifferRate
|
||||
// Usage:
|
||||
//
|
||||
// c := NewComparator("hello", "he")
|
||||
// rate, ok :c.Similar(0.3)
|
||||
func (c *SimilarComparator) Similar(minDifferRate float32) (float32, bool) {
|
||||
dist := c.editDistance([]byte(c.src), []byte(c.dst))
|
||||
differRate := dist / float32(max(len(c.src), len(c.dst))+4)
|
||||
|
||||
return differRate, differRate >= minDifferRate
|
||||
}
|
||||
|
||||
func (c *SimilarComparator) editDistance(s, t []byte) float32 {
|
||||
var (
|
||||
m = len(s)
|
||||
n = len(t)
|
||||
d = make([][]float32, m+1)
|
||||
)
|
||||
for i := 0; i < m+1; i++ {
|
||||
d[i] = make([]float32, n+1)
|
||||
d[i][0] = float32(i)
|
||||
}
|
||||
for j := 0; j < n+1; j++ {
|
||||
d[0][j] = float32(j)
|
||||
}
|
||||
|
||||
for j := 1; j < n+1; j++ {
|
||||
for i := 1; i < m+1; i++ {
|
||||
if s[i-1] == t[j-1] {
|
||||
d[i][j] = d[i-1][j-1]
|
||||
} else {
|
||||
d[i][j] = min(d[i-1][j]+1, min(d[i][j-1]+1, d[i-1][j-1]+1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return d[m][n]
|
||||
}
|
||||
|
||||
func min(x, y float32) float32 {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
|
||||
return y
|
||||
}
|
||||
|
||||
func max(x, y int) int {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
|
||||
return y
|
||||
}
|
||||
|
||||
// Similarity calc for two string.
|
||||
// Usage:
|
||||
//
|
||||
// rate, ok := Similarity("hello", "he")
|
||||
func Similarity(s, t string, rate float32) (float32, bool) {
|
||||
return NewComparator(s, t).Similar(rate)
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package strutil
|
||||
|
||||
import "strings"
|
||||
|
||||
/*************************************************************
|
||||
* String split operation
|
||||
*************************************************************/
|
||||
|
||||
// Cut same of the strings.Cut
|
||||
func Cut(s, sep string) (before string, after string, found bool) {
|
||||
if i := strings.Index(s, sep); i >= 0 {
|
||||
return s[:i], s[i+len(sep):], true
|
||||
}
|
||||
return s, "", false
|
||||
}
|
||||
|
||||
// MustCut always returns two substring.
|
||||
func MustCut(s, sep string) (before string, after string) {
|
||||
before, after, _ = Cut(s, sep)
|
||||
return
|
||||
}
|
||||
|
||||
// TrimCut always returns two substring and trim space for items.
|
||||
func TrimCut(s, sep string) (string, string) {
|
||||
before, after, _ := Cut(s, sep)
|
||||
return strings.TrimSpace(before), strings.TrimSpace(after)
|
||||
}
|
||||
|
||||
// SplitValid string to slice. will trim each item and filter empty string node.
|
||||
func SplitValid(s, sep string) (ss []string) { return Split(s, sep) }
|
||||
|
||||
// Split string to slice. will trim each item and filter empty string node.
|
||||
func Split(s, sep string) (ss []string) {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, val := range strings.Split(s, sep) {
|
||||
if val = strings.TrimSpace(val); val != "" {
|
||||
ss = append(ss, val)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SplitNValid string to slice. will filter empty string node.
|
||||
func SplitNValid(s, sep string, n int) (ss []string) { return SplitN(s, sep, n) }
|
||||
|
||||
// SplitN string to slice. will filter empty string node.
|
||||
func SplitN(s, sep string, n int) (ss []string) {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
rawList := strings.Split(s, sep)
|
||||
for i, val := range rawList {
|
||||
if val = strings.TrimSpace(val); val != "" {
|
||||
if len(ss) == n-1 {
|
||||
ss = append(ss, strings.TrimSpace(strings.Join(rawList[i:], sep)))
|
||||
break
|
||||
}
|
||||
|
||||
ss = append(ss, val)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SplitTrimmed split string to slice.
|
||||
// will trim space for each node, but not filter empty
|
||||
func SplitTrimmed(s, sep string) (ss []string) {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, val := range strings.Split(s, sep) {
|
||||
ss = append(ss, strings.TrimSpace(val))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SplitNTrimmed split string to slice.
|
||||
// will trim space for each node, but not filter empty
|
||||
func SplitNTrimmed(s, sep string, n int) (ss []string) {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, val := range strings.SplitN(s, sep, n) {
|
||||
ss = append(ss, strings.TrimSpace(val))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Substr for a string.
|
||||
// if length <= 0, return pos to end.
|
||||
func Substr(s string, pos, length int) string {
|
||||
runes := []rune(s)
|
||||
strLn := len(runes)
|
||||
|
||||
// pos is too large
|
||||
if pos >= strLn {
|
||||
return ""
|
||||
}
|
||||
|
||||
stopIdx := pos + length
|
||||
if length == 0 || stopIdx > strLn {
|
||||
stopIdx = strLn
|
||||
} else if length < 0 {
|
||||
stopIdx = strLn + length
|
||||
}
|
||||
|
||||
return string(runes[pos:stopIdx])
|
||||
}
|
||||
|
||||
// SplitInlineComment for a text string.
|
||||
func SplitInlineComment(val string) (string, string) {
|
||||
if pos := strings.IndexByte(val, '#'); pos > -1 {
|
||||
return strings.TrimRight(val[0:pos], " "), val[pos:]
|
||||
}
|
||||
|
||||
if pos := strings.Index(val, "//"); pos > -1 {
|
||||
return strings.TrimRight(val[0:pos], " "), val[pos:]
|
||||
}
|
||||
return val, ""
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// Package strutil provide some string,char,byte util functions
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// Replaces replace multi strings
|
||||
//
|
||||
// pairs: {old1: new1, old2: new2, ...}
|
||||
//
|
||||
// Can also use:
|
||||
//
|
||||
// strings.NewReplacer("old1", "new1", "old2", "new2").Replace(str)
|
||||
func Replaces(str string, pairs map[string]string) string {
|
||||
ss := make([]string, len(pairs)*2)
|
||||
for old, newVal := range pairs {
|
||||
ss = append(ss, old, newVal)
|
||||
}
|
||||
|
||||
return strings.NewReplacer(ss...).Replace(str)
|
||||
}
|
||||
|
||||
// PrettyJSON get pretty Json string
|
||||
// Deprecated: please use fmtutil.PrettyJSON() or jsonutil.Pretty() instead it
|
||||
func PrettyJSON(v any) (string, error) {
|
||||
out, err := json.MarshalIndent(v, "", " ")
|
||||
return string(out), err
|
||||
}
|
||||
|
||||
// RenderTemplate render text template
|
||||
func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string {
|
||||
return RenderText(input, data, fns, isFile...)
|
||||
}
|
||||
|
||||
// RenderText render text template
|
||||
func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string {
|
||||
t := template.New("simple-text")
|
||||
t.Funcs(template.FuncMap{
|
||||
// don't escape content
|
||||
"raw": func(s string) string {
|
||||
return s
|
||||
},
|
||||
"trim": func(s string) string {
|
||||
return strings.TrimSpace(s)
|
||||
},
|
||||
// join strings
|
||||
"join": func(ss []string, sep string) string {
|
||||
return strings.Join(ss, sep)
|
||||
},
|
||||
// lower first char
|
||||
"lcFirst": func(s string) string {
|
||||
return LowerFirst(s)
|
||||
},
|
||||
// upper first char
|
||||
"upFirst": func(s string) string {
|
||||
return UpperFirst(s)
|
||||
},
|
||||
})
|
||||
|
||||
// add custom template functions
|
||||
if len(fns) > 0 {
|
||||
t.Funcs(fns)
|
||||
}
|
||||
|
||||
if len(isFile) > 0 && isFile[0] {
|
||||
template.Must(t.ParseFiles(input))
|
||||
} else {
|
||||
template.Must(t.Parse(input))
|
||||
}
|
||||
|
||||
// use buffer receive rendered content
|
||||
buf := new(bytes.Buffer)
|
||||
if err := t.Execute(buf, data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// WrapTag for given string.
|
||||
func WrapTag(s, tag string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("<%s>%s</%s>", tag, s, tag)
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package strutil
|
||||
|
||||
import "strings"
|
||||
|
||||
// Value string
|
||||
type Value string
|
||||
|
||||
// StrVal string. alias of Value
|
||||
type StrVal = Value
|
||||
|
||||
// Set value
|
||||
func (s *Value) Set(val string) error {
|
||||
*s = Value(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsEmpty check
|
||||
func (s *Value) IsEmpty() bool {
|
||||
return string(*s) == ""
|
||||
}
|
||||
|
||||
// IsStartWith prefix
|
||||
func (s *Value) IsStartWith(sub string) bool {
|
||||
return strings.HasPrefix(string(*s), sub)
|
||||
}
|
||||
|
||||
// HasPrefix prefix
|
||||
func (s *Value) HasPrefix(sub string) bool {
|
||||
return strings.HasPrefix(string(*s), sub)
|
||||
}
|
||||
|
||||
// IsEndWith suffix
|
||||
func (s *Value) IsEndWith(sub string) bool {
|
||||
return strings.HasSuffix(string(*s), sub)
|
||||
}
|
||||
|
||||
// HasSuffix suffix
|
||||
func (s *Value) HasSuffix(sub string) bool {
|
||||
return strings.HasSuffix(string(*s), sub)
|
||||
}
|
||||
|
||||
// Bytes string to bytes
|
||||
func (s *Value) Bytes() []byte {
|
||||
return []byte(*s)
|
||||
}
|
||||
|
||||
// Val string
|
||||
func (s *Value) Val() string {
|
||||
return string(*s)
|
||||
}
|
||||
|
||||
// Int convert
|
||||
func (s *Value) Int() int {
|
||||
return QuietInt(string(*s))
|
||||
}
|
||||
|
||||
// Int64 convert
|
||||
func (s *Value) Int64() int64 {
|
||||
return QuietInt64(string(*s))
|
||||
}
|
||||
|
||||
// Bool convert
|
||||
func (s *Value) Bool() bool {
|
||||
return QuietBool(string(*s))
|
||||
}
|
||||
|
||||
// Value string
|
||||
func (s *Value) String() string {
|
||||
return string(*s)
|
||||
}
|
||||
|
||||
// Split string
|
||||
func (s *Value) Split(sep string) []string {
|
||||
return strings.Split(string(*s), sep)
|
||||
}
|
||||
|
||||
// SplitN string
|
||||
func (s *Value) SplitN(sep string, n int) []string {
|
||||
return strings.SplitN(string(*s), sep, n)
|
||||
}
|
||||
|
||||
// TrimSpace string and return new
|
||||
func (s *Value) TrimSpace() Value {
|
||||
return Value(strings.TrimSpace(string(*s)))
|
||||
}
|
||||
Reference in New Issue
Block a user