mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-06-16 20:08:59 -04:00
The goutil that OpenCloud currently uses is one version from the release that adds FreeBSD support, this now compiles successfully on FreeBSD.
514 lines
12 KiB
Go
514 lines
12 KiB
Go
package strutil
|
|
|
|
import (
|
|
"path"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
|
|
"github.com/gookit/goutil/internal/checkfn"
|
|
)
|
|
|
|
// Equal check, alias of strings.EqualFold
|
|
var Equal = strings.EqualFold
|
|
var IsHttpURL = checkfn.IsHttpURL
|
|
|
|
// IsNumChar returns true if the given character is a numeric, otherwise false.
|
|
func IsNumChar(c byte) bool { return c >= '0' && c <= '9' }
|
|
|
|
var (
|
|
uintReg = regexp.MustCompile(`^\d+$`)
|
|
intReg = regexp.MustCompile(`^[-+]?\d+$`)
|
|
|
|
floatReg = regexp.MustCompile(`^[-+]?\d*\.?\d+$`)
|
|
)
|
|
|
|
// IsInt check the string is an integer number
|
|
func IsInt(s string) bool {
|
|
if s == "" {
|
|
return false
|
|
}
|
|
return intReg.MatchString(s)
|
|
}
|
|
|
|
// IsUint check the string is an unsigned integer number
|
|
func IsUint(s string) bool {
|
|
if s == "" {
|
|
return false
|
|
}
|
|
return uintReg.MatchString(s)
|
|
}
|
|
|
|
// IsFloat check the string is a float number
|
|
func IsFloat(s string) bool {
|
|
if s == "" {
|
|
return false
|
|
}
|
|
return floatReg.MatchString(s)
|
|
}
|
|
|
|
// IsNumeric returns true if the given string is a numeric(int/float), otherwise false.
|
|
func IsNumeric(s string) bool { return checkfn.IsNumeric(s) }
|
|
|
|
// IsPositiveNum check the string is a positive number
|
|
func IsPositiveNum(s string) bool { return checkfn.IsPositiveNum(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'
|
|
}
|
|
|
|
// IsUpper returns true if the given string is an uppercase, otherwise false.
|
|
func IsUpper(s string) bool {
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] >= 'A' && s[i] <= 'Z' {
|
|
continue
|
|
}
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// IsLower returns true if the given string is a lowercase, otherwise false.
|
|
func IsLower(s string) bool {
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] >= 'a' && s[i] <= 'z' {
|
|
continue
|
|
}
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// 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) }
|
|
|
|
// IEqual ignore case check given two strings are equals.
|
|
func IEqual(s1, s2 string) bool { return strings.EqualFold(s1, s2) }
|
|
|
|
// NoCaseEq check two strings is equals and case-insensitivity
|
|
func NoCaseEq(s, t string) bool { return strings.EqualFold(s, t) }
|
|
|
|
// IContains ignore case check substr in the given string.
|
|
func IContains(s, sub string) bool {
|
|
return strings.Contains(strings.ToLower(s), strings.ToLower(sub))
|
|
}
|
|
|
|
// ContainsByte in given string.
|
|
func ContainsByte(s string, c byte) bool { return strings.IndexByte(s, c) >= 0 }
|
|
|
|
// ContainsByteOne in given string.
|
|
func ContainsByteOne(s string, bs []byte) bool {
|
|
for _, b := range bs {
|
|
if strings.IndexByte(s, b) >= 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// InArray alias of HasOneSub()
|
|
var InArray = HasOneSub
|
|
|
|
// ContainsOne substr(s) in the given string. alias of HasOneSub()
|
|
func ContainsOne(s string, subs []string) bool { return HasOneSub(s, subs) }
|
|
|
|
// HasOneSub substr(s) in the given string.
|
|
func HasOneSub(s string, subs []string) bool {
|
|
for _, sub := range subs {
|
|
if strings.Contains(s, sub) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IContainsOne ignore case check has one substr(s) in the given string.
|
|
func IContainsOne(s string, subs []string) bool {
|
|
s = strings.ToLower(s)
|
|
for _, sub := range subs {
|
|
if strings.Contains(s, strings.ToLower(sub)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ContainsAll given string should contain all substrings. alias of HasAllSubs()
|
|
func ContainsAll(s string, subs []string) bool { return HasAllSubs(s, subs) }
|
|
|
|
// HasAllSubs given string should contain all substrings
|
|
func HasAllSubs(s string, subs []string) bool {
|
|
for _, sub := range subs {
|
|
if !strings.Contains(s, sub) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// IContainsAll like ContainsAll(), but ignore case
|
|
func IContainsAll(s string, subs []string) bool {
|
|
s = strings.ToLower(s)
|
|
for _, sub := range subs {
|
|
if !strings.Contains(s, strings.ToLower(sub)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// StartsWithAny alias of the HasOnePrefix
|
|
var StartsWithAny = HasOneSuffix
|
|
|
|
// IsStartsOf alias of the HasOnePrefix
|
|
func IsStartsOf(s string, prefixes []string) bool {
|
|
return HasOnePrefix(s, prefixes)
|
|
}
|
|
|
|
// HasOnePrefix the string starts with one of the subs
|
|
func HasOnePrefix(s string, prefixes []string) bool {
|
|
for _, prefix := range prefixes {
|
|
if len(s) >= len(prefix) && s[0:len(prefix)] == prefix {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// StartsWith alias func for HasPrefix
|
|
var StartsWith = strings.HasPrefix
|
|
|
|
// 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) }
|
|
|
|
// HasOneSuffix the string end with one of the subs
|
|
func HasOneSuffix(s string, suffixes []string) bool {
|
|
for _, suffix := range suffixes {
|
|
if strings.HasSuffix(s, suffix) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 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) }
|
|
|
|
// HasEmpty value for input strings
|
|
func HasEmpty(ss ...string) bool {
|
|
for _, s := range ss {
|
|
if s == "" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsAllEmpty for input strings
|
|
func IsAllEmpty(ss ...string) bool {
|
|
for _, s := range ss {
|
|
if s != "" {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
var (
|
|
// regex for check version number
|
|
verRegex = regexp.MustCompile(`^[0-9][\d.]+(-\w+)?$`)
|
|
// regex for check variable name
|
|
varRegex = regexp.MustCompile(`^[a-zA-Z][\w-]*$`)
|
|
// regex for check env var name
|
|
envRegex = regexp.MustCompile(`^[A-Z][A-Z0-9_]*$`)
|
|
// IsVariableName alias for IsVarName
|
|
IsVariableName = IsVarName
|
|
// regex for check uuid string. format: 8-4-4-4-12
|
|
uuidPattern = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
|
|
)
|
|
|
|
// IsVersion number. eg: 1.2.0
|
|
func IsVersion(s string) bool { return verRegex.MatchString(s) }
|
|
|
|
// IsVarName is valid variable name.
|
|
func IsVarName(s string) bool { return varRegex.MatchString(s) }
|
|
|
|
// IsEnvName is valid ENV var name. eg: APP_NAME
|
|
func IsEnvName(s string) bool { return envRegex.MatchString(s) }
|
|
|
|
// IsUUID check if the string is a valid UUID format.
|
|
func IsUUID(s string) bool { return uuidPattern.MatchString(s) }
|
|
|
|
// Compare for two strings.
|
|
func Compare(s1, s2, op string) bool {
|
|
switch op {
|
|
case ">", "gt":
|
|
return s1 > s2
|
|
case "<", "lt":
|
|
return s1 < s2
|
|
case ">=", "gte":
|
|
return s1 >= s2
|
|
case "<=", "lte":
|
|
return s1 <= s2
|
|
case "!=", "ne", "neq":
|
|
return s1 != s2
|
|
default: // eq
|
|
return s1 == s2
|
|
}
|
|
}
|
|
|
|
// VersionCompare for two version strings. eg: 1.2.0 > 1.1.0
|
|
func VersionCompare(v1, v2, op string) bool {
|
|
parts1 := parseVersion(v1)
|
|
parts2 := parseVersion(v2)
|
|
|
|
result := compareVersions(parts1, parts2)
|
|
switch op {
|
|
case ">", "gt":
|
|
return result > 0
|
|
case "<", "lt":
|
|
return result < 0
|
|
case "=", "==", "eq":
|
|
return result == 0
|
|
case "!=", "ne", "neq":
|
|
return result != 0
|
|
case ">=", "gte":
|
|
return result >= 0
|
|
case "<=", "lte":
|
|
return result <= 0
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// parseVersion 将版本号字符串解析为整数数组
|
|
func parseVersion(version string) []int {
|
|
parts := strings.Split(version, ".")
|
|
result := make([]int, len(parts))
|
|
|
|
for i, part := range parts {
|
|
num, _ := strconv.Atoi(part)
|
|
result[i] = num
|
|
}
|
|
return result
|
|
}
|
|
|
|
// compareVersions 比较两个版本号数组
|
|
// 返回: -1 表示 v1 < v2, 0 表示 v1 = v2, 1 表示 v1 > v2
|
|
func compareVersions(v1, v2 []int) int {
|
|
maxLen := len(v1)
|
|
if len(v2) > maxLen {
|
|
maxLen = len(v2)
|
|
}
|
|
|
|
for i := 0; i < maxLen; i++ {
|
|
num1 := 0
|
|
if i < len(v1) {
|
|
num1 = v1[i]
|
|
}
|
|
|
|
num2 := 0
|
|
if i < len(v2) {
|
|
num2 = v2[i]
|
|
}
|
|
|
|
if num1 > num2 {
|
|
return 1
|
|
} else if num1 < num2 {
|
|
return -1
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// SimpleMatch all substring in the give text string.
|
|
//
|
|
// Difference the ContainsAll:
|
|
//
|
|
// - start with ^ for exclude contains check.
|
|
// - end with $ for the check end with keyword.
|
|
func SimpleMatch(s string, keywords []string) bool {
|
|
for _, keyword := range keywords {
|
|
kln := len(keyword)
|
|
if kln == 0 {
|
|
continue
|
|
}
|
|
|
|
// exclude
|
|
if kln > 1 && keyword[0] == '^' {
|
|
if strings.Contains(s, keyword[1:]) {
|
|
return false
|
|
}
|
|
continue
|
|
}
|
|
|
|
// end with
|
|
if kln > 1 && keyword[kln-1] == '$' {
|
|
return strings.HasSuffix(s, keyword[:kln-1])
|
|
}
|
|
|
|
// include
|
|
if !strings.Contains(s, keyword) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// QuickMatch check for a string. pattern can be a substring.
|
|
func QuickMatch(pattern, s string) bool {
|
|
if strings.ContainsRune(pattern, '*') {
|
|
return GlobMatch(pattern, s)
|
|
}
|
|
return strings.Contains(s, pattern)
|
|
}
|
|
|
|
// PathMatch check for a string match the pattern. alias of the path.Match()
|
|
//
|
|
// TIP: `*` can match any char, not contain `/`.
|
|
func PathMatch(pattern, s string) bool {
|
|
ok, err := path.Match(pattern, s)
|
|
if err != nil {
|
|
ok = false
|
|
}
|
|
return ok
|
|
}
|
|
|
|
// GlobMatch check for a string match the pattern.
|
|
//
|
|
// Difference with PathMatch() is: `*` can match any char, contain `/`.
|
|
func GlobMatch(pattern, s string) bool {
|
|
// replace `/` to `S` for path.Match
|
|
pattern = strings.Replace(pattern, "/", "S", -1)
|
|
s = strings.Replace(s, "/", "S", -1)
|
|
|
|
ok, err := path.Match(pattern, s)
|
|
if err != nil {
|
|
ok = false
|
|
}
|
|
return ok
|
|
}
|
|
|
|
// LikeMatch simple check for a string match the pattern. pattern like the SQL LIKE.
|
|
func LikeMatch(pattern, s string) bool {
|
|
ln := len(pattern)
|
|
if ln < 2 {
|
|
return false
|
|
}
|
|
|
|
// eg `%abc` `%abc%`
|
|
if pattern[0] == '%' {
|
|
if ln > 2 && pattern[ln-1] == '%' {
|
|
return strings.Contains(s, pattern[1:ln-1])
|
|
}
|
|
return strings.HasSuffix(s, pattern[1:])
|
|
}
|
|
|
|
// eg `abc%`
|
|
if pattern[ln-1] == '%' {
|
|
return strings.HasPrefix(s, pattern[:ln-1])
|
|
}
|
|
return pattern == s
|
|
}
|
|
|
|
// MatchNodePath check for a string match the pattern.
|
|
//
|
|
// Use on a pattern:
|
|
// - `*` match any to sep
|
|
// - `**` match any to end. only allow at start or end on pattern.
|
|
//
|
|
// Example:
|
|
//
|
|
// strutil.MatchNodePath()
|
|
func MatchNodePath(pattern, s string, sep string) bool {
|
|
if pattern == "**" || pattern == s {
|
|
return true
|
|
}
|
|
if pattern == "" {
|
|
return len(s) == 0
|
|
}
|
|
|
|
if i := strings.Index(pattern, "**"); i >= 0 {
|
|
if i == 0 { // at start
|
|
return strings.HasSuffix(s, pattern[2:])
|
|
}
|
|
return strings.HasPrefix(s, pattern[:len(pattern)-2])
|
|
}
|
|
|
|
pattern = strings.Replace(pattern, sep, "/", -1)
|
|
s = strings.Replace(s, sep, "/", -1)
|
|
|
|
ok, err := path.Match(pattern, s)
|
|
if err != nil {
|
|
ok = false
|
|
}
|
|
return ok
|
|
}
|