mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
merge qiangg_php
This commit is contained in:
105
g/string/gregex/gregex.go
Normal file
105
g/string/gregex/gregex.go
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2017-2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// Package gregex provides high performance API for regular expression functionality.
|
||||
//
|
||||
// 正则表达式.
|
||||
package gregex
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// 转移正则规则字符串,例如:Quote(`[foo]`) 返回 `\[foo\]`
|
||||
func Quote(s string) string {
|
||||
return regexp.QuoteMeta(s)
|
||||
}
|
||||
|
||||
// 校验所给定的正则表达式是否符合规范
|
||||
func Validate(pattern string) error {
|
||||
_, err := getRegexp(pattern)
|
||||
return err
|
||||
}
|
||||
|
||||
// 正则表达式是否匹配
|
||||
func IsMatch(pattern string, src []byte) bool {
|
||||
if r, err := getRegexp(pattern); err == nil {
|
||||
return r.Match(src)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsMatchString(pattern string, src string) bool {
|
||||
return IsMatch(pattern, []byte(src))
|
||||
}
|
||||
|
||||
// 正则匹配,并返回匹配的列表(参数[]byte)
|
||||
func Match(pattern string, src []byte) ([][]byte, error) {
|
||||
if r, err := getRegexp(pattern); err == nil {
|
||||
return r.FindSubmatch(src), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 正则匹配,并返回匹配的列表(参数[]string)
|
||||
func MatchString(pattern string, src string) ([]string, error) {
|
||||
if r, err := getRegexp(pattern); err == nil {
|
||||
return r.FindStringSubmatch(src), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 正则匹配,并返回所有匹配的列表(参数[]string)
|
||||
func MatchAll(pattern string, src []byte) ([][][]byte, error) {
|
||||
if r, err := getRegexp(pattern); err == nil {
|
||||
return r.FindAllSubmatch(src, -1), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 正则匹配,并返回所有匹配的列表(参数[][]string)
|
||||
func MatchAllString(pattern string, src string) ([][]string, error) {
|
||||
if r, err := getRegexp(pattern); err == nil {
|
||||
return r.FindAllStringSubmatch(src, -1), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 正则替换(全部替换)
|
||||
func Replace(pattern string, replace, src []byte) ([]byte, error) {
|
||||
if r, err := getRegexp(pattern); err == nil {
|
||||
return r.ReplaceAll(src, replace), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 正则替换(全部替换),字符串
|
||||
func ReplaceString(pattern, replace, src string) (string, error) {
|
||||
r, e := Replace(pattern, []byte(replace), []byte(src))
|
||||
return string(r), e
|
||||
}
|
||||
|
||||
// 正则替换(全部替换),给定自定义替换方法
|
||||
func ReplaceFunc(pattern string, src []byte, replaceFunc func(b []byte) []byte) ([]byte, error) {
|
||||
if r, err := getRegexp(pattern); err == nil {
|
||||
return r.ReplaceAllFunc(src, replaceFunc), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 正则替换(全部替换),给定自定义替换方法
|
||||
func ReplaceStringFunc(pattern string, src string, replaceFunc func(s string) string) (string, error) {
|
||||
bytes, err := ReplaceFunc(pattern, []byte(src), func(bytes []byte) []byte {
|
||||
return []byte(replaceFunc(string(bytes)))
|
||||
})
|
||||
return string(bytes), err
|
||||
}
|
||||
46
g/string/gregex/gregex_cache.go
Normal file
46
g/string/gregex/gregex_cache.go
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
package gregex
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 缓存对象,主要用于缓存底层regx对象
|
||||
var (
|
||||
regexMu = sync.RWMutex{}
|
||||
regexMap = make(map[string]*regexp.Regexp)
|
||||
)
|
||||
|
||||
// 根据pattern生成对应的regexp正则对象
|
||||
func getRegexp(pattern string) (*regexp.Regexp, error) {
|
||||
if r := getCache(pattern); r != nil {
|
||||
return r, nil
|
||||
}
|
||||
if r, err := regexp.Compile(pattern); err == nil {
|
||||
setCache(pattern, r)
|
||||
return r, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 获得正则缓存对象
|
||||
func getCache(pattern string) (regex *regexp.Regexp) {
|
||||
regexMu.RLock()
|
||||
regex = regexMap[pattern]
|
||||
regexMu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// 设置正则缓存对象
|
||||
func setCache(pattern string, regex *regexp.Regexp) {
|
||||
regexMu.Lock()
|
||||
regexMap[pattern] = regex
|
||||
regexMu.Unlock()
|
||||
}
|
||||
59
g/string/gregex/gregex_z_bench_test.go
Normal file
59
g/string/gregex/gregex_z_bench_test.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2017-2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gregex
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var pattern = `(.+):(\d+)`
|
||||
var src = "johng.cn:80"
|
||||
var replace = "johng.cn"
|
||||
|
||||
func BenchmarkValidate(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Validate(pattern)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsMatch(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsMatch(pattern, []byte(src))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsMatchString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsMatchString(pattern, src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMatchString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
MatchString(pattern, src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMatchAllString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
MatchAllString(pattern, src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReplace(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Replace(pattern, []byte(replace), []byte(src))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReplaceString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ReplaceString(pattern, replace, src)
|
||||
}
|
||||
}
|
||||
622
g/string/gstr/gstr.go
Normal file
622
g/string/gstr/gstr.go
Normal file
@ -0,0 +1,622 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// Package gstr provides useful API for string handling.
|
||||
//
|
||||
// 字符串操作.
|
||||
package gstr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/util/grand"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// 字符串替换(大小写敏感)
|
||||
func Replace(origin, search, replace string, count...int) string {
|
||||
n := -1
|
||||
if len(count) > 0 {
|
||||
n = count[0]
|
||||
}
|
||||
return strings.Replace(origin, search, replace, n)
|
||||
}
|
||||
|
||||
// 使用map进行字符串替换(大小写敏感)
|
||||
func ReplaceByMap(origin string, replaces map[string]string) string {
|
||||
result := origin
|
||||
for k, v := range replaces {
|
||||
result = strings.Replace(result, k, v, -1)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 字符串转换为小写
|
||||
func ToLower(s string) string {
|
||||
return strings.ToLower(s)
|
||||
}
|
||||
|
||||
// 字符串转换为大写
|
||||
func ToUpper(s string) string {
|
||||
return strings.ToUpper(s)
|
||||
}
|
||||
|
||||
// 字符串首字母转换为大写
|
||||
func UcFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
if IsLetterLower(s[0]) {
|
||||
return string(s[0] - 32) + s[1 :]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 字符串首字母转换为小写
|
||||
func LcFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
if IsLetterUpper(s[0]) {
|
||||
return string(s[0] + 32) + s[1 :]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Uppercase the first character of each word in a string.
|
||||
//
|
||||
// 大写字符串中每个单词的第一个字符。
|
||||
func UcWords(str string) string {
|
||||
return strings.Title(str)
|
||||
}
|
||||
|
||||
// Traverse the array to find the string index position, if not exist, return-1.
|
||||
//
|
||||
// 遍历数组查找字符串索引位置,如果不存在则返回-1,使用完整遍历查找.
|
||||
func SearchArray (a []string, s string) int {
|
||||
for i, v := range a {
|
||||
if s == v {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// 判断字符串是否在数组中
|
||||
func InArray (a []string, s string) bool {
|
||||
return SearchArray(a, s) != -1
|
||||
}
|
||||
|
||||
// 判断给定字符是否小写
|
||||
func IsLetterLower(b byte) bool {
|
||||
if b >= byte('a') && b <= byte('z') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断给定字符是否大写
|
||||
func IsLetterUpper(b byte) bool {
|
||||
if b >= byte('A') && b <= byte('Z') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断锁给字符串是否为数字
|
||||
func IsNumeric(s string) bool {
|
||||
length := len(s)
|
||||
if length == 0 {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] < byte('0') || s[i] > byte('9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 字符串截取,支持中文
|
||||
func SubStr(str string, start int, length...int) (substr string) {
|
||||
// 将字符串的转换成[]rune
|
||||
rs := []rune(str)
|
||||
lth := len(rs)
|
||||
// 简单的越界判断
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if start >= lth {
|
||||
start = lth
|
||||
}
|
||||
end := lth
|
||||
if len(length) > 0 {
|
||||
end = start + length[0]
|
||||
if end < start {
|
||||
end = lth
|
||||
}
|
||||
}
|
||||
if end > lth {
|
||||
end = lth
|
||||
}
|
||||
// 返回子串
|
||||
return string(rs[start : end])
|
||||
}
|
||||
|
||||
// 字符串长度截取限制,超过长度限制被截取并在字符串末尾追加指定的内容,支持中文
|
||||
func StrLimit(str string, length int, suffix...string) (string) {
|
||||
rs := []rune(str)
|
||||
if len(str) < length {
|
||||
return str
|
||||
}
|
||||
addStr := "..."
|
||||
if len(suffix) > 0 {
|
||||
addStr = suffix[0]
|
||||
}
|
||||
return string(rs[0 : length]) + addStr
|
||||
}
|
||||
|
||||
// Reverse a string.
|
||||
func Reverse(str string) string {
|
||||
runes := []rune(str)
|
||||
for i, j := 0, len(runes) - 1; i < j; i, j = i + 1, j - 1 {
|
||||
runes[i], runes[j] = runes[j], runes[i]
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
// Format a number with grouped thousands.
|
||||
// decimals: Sets the number of decimal points.
|
||||
// decPoint: Sets the separator for the decimal point.
|
||||
// thousandsSep: Sets the thousands separator.
|
||||
// See http://php.net/manual/en/function.number-format.php.
|
||||
//
|
||||
// 以千位分隔符方式格式化一个数字.
|
||||
func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) string {
|
||||
neg := false
|
||||
if number < 0 {
|
||||
number = -number
|
||||
neg = true
|
||||
}
|
||||
// Will round off
|
||||
str := fmt.Sprintf("%."+strconv.Itoa(decimals)+"F", number)
|
||||
prefix, suffix := "", ""
|
||||
if decimals > 0 {
|
||||
prefix = str[ : len(str) - (decimals + 1)]
|
||||
suffix = str[len(str) - decimals : ]
|
||||
} else {
|
||||
prefix = str
|
||||
}
|
||||
sep := []byte(thousandsSep)
|
||||
n, l1, l2 := 0, len(prefix), len(sep)
|
||||
// thousands sep num
|
||||
c := (l1 - 1) / 3
|
||||
tmp := make([]byte, l2*c+l1)
|
||||
pos := len(tmp) - 1
|
||||
for i := l1 - 1; i >= 0; i, n, pos = i-1, n+1, pos-1 {
|
||||
if l2 > 0 && n > 0 && n%3 == 0 {
|
||||
for j := range sep {
|
||||
tmp[pos] = sep[l2-j-1]
|
||||
pos--
|
||||
}
|
||||
}
|
||||
tmp[pos] = prefix[i]
|
||||
}
|
||||
s := string(tmp)
|
||||
if decimals > 0 {
|
||||
s += decPoint + suffix
|
||||
}
|
||||
if neg {
|
||||
s = "-" + s
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Split a string into smaller chunks.
|
||||
// Can be used to split a string into smaller chunks which is useful for
|
||||
// e.g. converting BASE64 string output to match RFC 2045 semantics.
|
||||
// It inserts end every chunkLen characters.
|
||||
//
|
||||
// 将字符串分割成小块。使用此函数将字符串分割成小块非常有用。
|
||||
// 例如将BASE64的输出转换成符合RFC2045语义的字符串。
|
||||
// 它会在每 chunkLen 个字符后边插入 end。
|
||||
func ChunkSplit(body string, chunkLen int, end string) string {
|
||||
if end == "" {
|
||||
end = "\r\n"
|
||||
}
|
||||
runes, endRunes := []rune(body), []rune(end)
|
||||
l := len(runes)
|
||||
if l <= 1 || l < chunkLen {
|
||||
return body + end
|
||||
}
|
||||
ns := make([]rune, 0, len(runes) + len(endRunes))
|
||||
for i := 0; i < l; i += chunkLen {
|
||||
if i + chunkLen > l {
|
||||
ns = append(ns, runes[i : ]...)
|
||||
} else {
|
||||
ns = append(ns, runes[i : i + chunkLen]...)
|
||||
}
|
||||
ns = append(ns, endRunes...)
|
||||
}
|
||||
return string(ns)
|
||||
}
|
||||
|
||||
// Return information about words used in a string.
|
||||
func WordCount(str string) []string {
|
||||
return strings.Fields(str)
|
||||
}
|
||||
|
||||
// Wraps a string to a given number of characters.
|
||||
// TODO: Enable cut param, see http://php.net/manual/en/function.wordwrap.php.
|
||||
func WordWrap(str string, width int, br string) string {
|
||||
if br == "" {
|
||||
br = "\n"
|
||||
}
|
||||
init := make([]byte, 0, len(str))
|
||||
buf := bytes.NewBuffer(init)
|
||||
var current int
|
||||
var wordBuf, spaceBuf bytes.Buffer
|
||||
for _, char := range str {
|
||||
if char == '\n' {
|
||||
if wordBuf.Len() == 0 {
|
||||
if current + spaceBuf.Len() > width {
|
||||
current = 0
|
||||
} else {
|
||||
current += spaceBuf.Len()
|
||||
spaceBuf.WriteTo(buf)
|
||||
}
|
||||
spaceBuf.Reset()
|
||||
} else {
|
||||
current += spaceBuf.Len() + wordBuf.Len()
|
||||
spaceBuf.WriteTo(buf)
|
||||
spaceBuf.Reset()
|
||||
wordBuf.WriteTo(buf)
|
||||
wordBuf.Reset()
|
||||
}
|
||||
buf.WriteRune(char)
|
||||
current = 0
|
||||
} else if unicode.IsSpace(char) {
|
||||
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
|
||||
current += spaceBuf.Len() + wordBuf.Len()
|
||||
spaceBuf.WriteTo(buf)
|
||||
spaceBuf.Reset()
|
||||
wordBuf.WriteTo(buf)
|
||||
wordBuf.Reset()
|
||||
}
|
||||
spaceBuf.WriteRune(char)
|
||||
} else {
|
||||
wordBuf.WriteRune(char)
|
||||
if current + spaceBuf.Len()+wordBuf.Len() > width && wordBuf.Len() < width {
|
||||
buf.WriteString(br)
|
||||
current = 0
|
||||
spaceBuf.Reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if wordBuf.Len() == 0 {
|
||||
if current + spaceBuf.Len() <= width {
|
||||
spaceBuf.WriteTo(buf)
|
||||
}
|
||||
} else {
|
||||
spaceBuf.WriteTo(buf)
|
||||
wordBuf.WriteTo(buf)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Get string length of unicode.
|
||||
func RuneLen(str string) int {
|
||||
return utf8.RuneCountInString(str)
|
||||
}
|
||||
|
||||
// Repeat a string.
|
||||
func Repeat(input string, multiplier int) string {
|
||||
return strings.Repeat(input, multiplier)
|
||||
}
|
||||
|
||||
// Returns part of haystack string starting from and including the first occurrence of needle to the end of haystack.
|
||||
// See http://php.net/manual/en/function.strstr.php.
|
||||
//
|
||||
// 查找字符串的首次出现。返回 haystack 字符串从 needle 第一次出现的位置开始到 haystack 结尾的字符串。
|
||||
func Str(haystack string, needle string) string {
|
||||
if needle == "" {
|
||||
return ""
|
||||
}
|
||||
idx := strings.Index(haystack, needle)
|
||||
if idx == -1 {
|
||||
return ""
|
||||
}
|
||||
return haystack[idx + len([]byte(needle)) - 1 : ]
|
||||
}
|
||||
|
||||
// Translate characters or replace substrings.
|
||||
//
|
||||
// If the params length is 1, type is: map[string]string
|
||||
// Tr("baab", map[string]string{"ab": "01"}) will return "ba01".
|
||||
// If the params length is 2, type is: string, string
|
||||
// Tr("baab", "ab", "01") will return "1001", a => 0; b => 1.
|
||||
// See http://php.net/manual/en/function.strtr.php.
|
||||
//
|
||||
// 转换或者替换指定字符。
|
||||
func Tr(haystack string, params ...interface{}) string {
|
||||
ac := len(params)
|
||||
if ac == 1 {
|
||||
pairs := params[0].(map[string]string)
|
||||
length := len(pairs)
|
||||
if length == 0 {
|
||||
return haystack
|
||||
}
|
||||
oldnew := make([]string, length*2)
|
||||
for o, n := range pairs {
|
||||
if o == "" {
|
||||
return haystack
|
||||
}
|
||||
oldnew = append(oldnew, o, n)
|
||||
}
|
||||
return strings.NewReplacer(oldnew...).Replace(haystack)
|
||||
} else if ac == 2 {
|
||||
from := params[0].(string)
|
||||
to := params[1].(string)
|
||||
trlen, lt := len(from), len(to)
|
||||
if trlen > lt {
|
||||
trlen = lt
|
||||
}
|
||||
if trlen == 0 {
|
||||
return haystack
|
||||
}
|
||||
|
||||
str := make([]uint8, len(haystack))
|
||||
var xlat [256]uint8
|
||||
var i int
|
||||
var j uint8
|
||||
if trlen == 1 {
|
||||
for i = 0; i < len(haystack); i++ {
|
||||
if haystack[i] == from[0] {
|
||||
str[i] = to[0]
|
||||
} else {
|
||||
str[i] = haystack[i]
|
||||
}
|
||||
}
|
||||
return string(str)
|
||||
}
|
||||
// trlen != 1
|
||||
for {
|
||||
xlat[j] = j
|
||||
if j++; j == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i = 0; i < trlen; i++ {
|
||||
xlat[from[i]] = to[i]
|
||||
}
|
||||
for i = 0; i < len(haystack); i++ {
|
||||
str[i] = xlat[haystack[i]]
|
||||
}
|
||||
return string(str)
|
||||
}
|
||||
|
||||
return haystack
|
||||
}
|
||||
|
||||
// Randomly shuffles a string.
|
||||
//
|
||||
// 将字符串打乱。
|
||||
func Shuffle(str string) string {
|
||||
runes := []rune(str)
|
||||
s := make([]rune, len(runes))
|
||||
for i, v := range grand.Perm(len(runes)) {
|
||||
s[i] = runes[v]
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Strip whitespace (or other characters) from the beginning and end of a string.
|
||||
//
|
||||
// 去除字符串首尾处的空白字符(或者其他字符)。
|
||||
func Trim(str string, characterMask ...string) string {
|
||||
if len(characterMask) > 0 {
|
||||
return strings.Trim(str, characterMask[0])
|
||||
} else {
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
}
|
||||
|
||||
// Strip whitespace (or other characters) from the beginning of a string.
|
||||
//
|
||||
// 去除字符串首的空白字符(或者其他字符)。
|
||||
func TrimLeft(str string, characterMask ...string) string {
|
||||
mask := ""
|
||||
if len(characterMask) == 0 {
|
||||
mask = string([]byte{'\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0})
|
||||
} else {
|
||||
mask = characterMask[0]
|
||||
}
|
||||
return strings.TrimLeft(str, mask)
|
||||
}
|
||||
|
||||
// Strip whitespace (or other characters) from the end of a string.
|
||||
//
|
||||
// 去除字符串尾的空白字符(或者其他字符)。
|
||||
func TrimRight(str string, characterMask ...string) string {
|
||||
mask := ""
|
||||
if len(characterMask) == 0 {
|
||||
mask = string([]byte{'\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0})
|
||||
} else {
|
||||
mask = characterMask[0]
|
||||
}
|
||||
return strings.TrimRight(str, mask)
|
||||
}
|
||||
|
||||
// Split a string by a string, to an array.
|
||||
//
|
||||
// 此函数返回由字符串组成的数组,每个元素都是 str 的一个子串,它们被字符串 delimiter 作为边界点分割出来。
|
||||
func Split(str, delimiter string) []string {
|
||||
return strings.Split(str, delimiter)
|
||||
}
|
||||
|
||||
// Join concatenates the elements of a to create a single string. The separator string
|
||||
// sep is placed between elements in the resulting string.
|
||||
//
|
||||
// 用sep将字符串数组array连接为一个字符串。
|
||||
func Join(array []string, sep string) string {
|
||||
return strings.Join(array, sep)
|
||||
}
|
||||
|
||||
// Split a string by a string, to an array.
|
||||
// See http://php.net/manual/en/function.explode.php.
|
||||
//
|
||||
// 此函数返回由字符串组成的数组,每个元素都是 str 的一个子串,它们被字符串 delimiter 作为边界点分割出来。
|
||||
func Explode(delimiter, str string) []string {
|
||||
return Split(str, delimiter)
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
// http://php.net/manual/en/function.implode.php
|
||||
//
|
||||
// 用glue将字符串数组pieces连接为一个字符串。
|
||||
func Implode(glue string, pieces []string) string {
|
||||
var buf bytes.Buffer
|
||||
l := len(pieces)
|
||||
for _, str := range pieces {
|
||||
buf.WriteString(str)
|
||||
if l--; l > 0 {
|
||||
buf.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Generate a single-byte string from a number.
|
||||
//
|
||||
// 返回相对应于 ascii 所指定的单个字符。
|
||||
func Chr(ascii int) string {
|
||||
return string(ascii)
|
||||
}
|
||||
|
||||
// Convert the first byte of a string to a value between 0 and 255.
|
||||
//
|
||||
// 解析 char 二进制值第一个字节为 0 到 255 范围的无符号整型类型。
|
||||
func Ord(char string) int {
|
||||
return int(char[0])
|
||||
}
|
||||
|
||||
// 按照百分比从字符串中间向两边隐藏字符(主要用于姓名、手机号、邮箱地址、身份证号等的隐藏),支持utf-8中文,支持email格式。
|
||||
func HideStr(str string, percent int, hide string) string {
|
||||
array := strings.Split(str, "@")
|
||||
if len(array) > 1 {
|
||||
str = array[0]
|
||||
}
|
||||
rs := []rune(str)
|
||||
length := len(rs)
|
||||
mid := math.Floor(float64(length/2))
|
||||
hideLen := int(math.Floor(float64(length) * (float64(percent)/100)))
|
||||
start := int(mid - math.Floor(float64(hideLen) / 2))
|
||||
hideStr := []rune("")
|
||||
hideRune := []rune(hide)
|
||||
for i := 0; i < int(hideLen); i++ {
|
||||
hideStr = append(hideStr, hideRune...)
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteString(string(rs[0 : start]))
|
||||
buffer.WriteString(string(hideStr))
|
||||
buffer.WriteString(string(rs[start + hideLen : ]))
|
||||
if len(array) > 1 {
|
||||
buffer.WriteString(array[1])
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Inserts HTML line breaks before all newlines in a string.
|
||||
// \n\r, \r\n, \r, \n
|
||||
//
|
||||
// 在字符串 string 所有新行之前插入 '<br />' 或 '<br>',并返回。
|
||||
func Nl2Br(str string, isXhtml...bool) string {
|
||||
r, n, runes := '\r', '\n', []rune(str)
|
||||
var br []byte
|
||||
if len(isXhtml) > 0 && isXhtml[0] {
|
||||
br = []byte("<br />")
|
||||
} else {
|
||||
br = []byte("<br>")
|
||||
}
|
||||
skip := false
|
||||
length := len(runes)
|
||||
var buf bytes.Buffer
|
||||
for i, v := range runes {
|
||||
if skip {
|
||||
skip = false
|
||||
continue
|
||||
}
|
||||
switch v {
|
||||
case n, r:
|
||||
if (i+1 < length) && (v == r && runes[i+1] == n) || (v == n && runes[i+1] == r) {
|
||||
buf.Write(br)
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
buf.Write(br)
|
||||
default:
|
||||
buf.WriteRune(v)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Quote string with slashes.
|
||||
//
|
||||
// 转义字符串中的单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)。
|
||||
func AddSlashes(str string) string {
|
||||
var buf bytes.Buffer
|
||||
for _, char := range str {
|
||||
switch char {
|
||||
case '\'', '"', '\\':
|
||||
buf.WriteRune('\\')
|
||||
}
|
||||
buf.WriteRune(char)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Un-quotes a quoted string.
|
||||
//
|
||||
// 反转义字符串。
|
||||
func StripSlashes(str string) string {
|
||||
var buf bytes.Buffer
|
||||
l, skip := len(str), false
|
||||
for i, char := range str {
|
||||
if skip {
|
||||
skip = false
|
||||
} else if char == '\\' {
|
||||
if i + 1 < l && str[i + 1] == '\\' {
|
||||
skip = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
buf.WriteRune(char)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Returns a version of str with a backslash character (\) before every character that is among:
|
||||
// .\+*?[^]($)
|
||||
//
|
||||
// 转义字符串,转义的特殊字符包括:.\+*?[^]($)。
|
||||
func QuoteMeta(str string) string {
|
||||
var buf bytes.Buffer
|
||||
for _, char := range str {
|
||||
switch char {
|
||||
case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?':
|
||||
buf.WriteRune('\\')
|
||||
}
|
||||
buf.WriteRune(char)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
57
g/string/gstr/gstr_levenshtein.go
Normal file
57
g/string/gstr/gstr_levenshtein.go
Normal file
@ -0,0 +1,57 @@
|
||||
package gstr
|
||||
|
||||
// Calculate Levenshtein distance between two strings.
|
||||
// costIns: Defines the cost of insertion.
|
||||
// costRep: Defines the cost of replacement.
|
||||
// costDel: Defines the cost of deletion.
|
||||
// See http://php.net/manual/en/function.levenshtein.php.
|
||||
//
|
||||
// 计算两个字符串之间的编辑距离(Levenshtein distance)。
|
||||
func Levenshtein(str1, str2 string, costIns, costRep, costDel int) int {
|
||||
var maxLen = 255
|
||||
l1 := len(str1)
|
||||
l2 := len(str2)
|
||||
if l1 == 0 {
|
||||
return l2 * costIns
|
||||
}
|
||||
if l2 == 0 {
|
||||
return l1 * costDel
|
||||
}
|
||||
if l1 > maxLen || l2 > maxLen {
|
||||
return -1
|
||||
}
|
||||
|
||||
tmp := make([]int, l2+1)
|
||||
p1 := make([]int, l2+1)
|
||||
p2 := make([]int, l2+1)
|
||||
var c0, c1, c2 int
|
||||
var i1, i2 int
|
||||
for i2 := 0; i2 <= l2; i2++ {
|
||||
p1[i2] = i2 * costIns
|
||||
}
|
||||
for i1 = 0; i1 < l1; i1++ {
|
||||
p2[0] = p1[0] + costDel
|
||||
for i2 = 0; i2 < l2; i2++ {
|
||||
if str1[i1] == str2[i2] {
|
||||
c0 = p1[i2]
|
||||
} else {
|
||||
c0 = p1[i2] + costRep
|
||||
}
|
||||
c1 = p1[i2+1] + costDel
|
||||
if c1 < c0 {
|
||||
c0 = c1
|
||||
}
|
||||
c2 = p2[i2] + costIns
|
||||
if c2 < c0 {
|
||||
c0 = c2
|
||||
}
|
||||
p2[i2+1] = c0
|
||||
}
|
||||
tmp = p1
|
||||
p1 = p2
|
||||
p2 = tmp
|
||||
}
|
||||
c0 = p1[l2]
|
||||
|
||||
return c0
|
||||
}
|
||||
159
g/string/gstr/gstr_parse.go
Normal file
159
g/string/gstr/gstr_parse.go
Normal file
@ -0,0 +1,159 @@
|
||||
package gstr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parses the string into variables.
|
||||
// f1=m&f2=n -> map[f1:m f2:n]
|
||||
// f[a]=m&f[b]=n -> map[f:map[a:m b:n]]
|
||||
// f[a][a]=m&f[a][b]=n -> map[f:map[a:map[a:m b:n]]]
|
||||
// f[]=m&f[]=n -> map[f:[m n]]
|
||||
// f[a][]=m&f[a][]=n -> map[f:map[a:[m n]]]
|
||||
// f[][]=m&f[][]=n -> map[f:[map[]]] // Currently does not support nested slice.
|
||||
// f=m&f[a]=n -> error // This is not the same as PHP.
|
||||
// a .[[b=c -> map[a___[b:c]
|
||||
//
|
||||
// 将字符串解析成Map。
|
||||
func Parse(encodedString string, result map[string]interface{}) error {
|
||||
// build nested map.
|
||||
var build func(map[string]interface{}, []string, interface{}) error
|
||||
build = func(result map[string]interface{}, keys []string, value interface{}) error {
|
||||
length := len(keys)
|
||||
// trim ',"
|
||||
key := strings.Trim(keys[0], "'\"")
|
||||
if length == 1 {
|
||||
result[key] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// The end is slice. like f[], f[a][]
|
||||
if keys[1] == "" && length == 2 {
|
||||
// todo nested slice
|
||||
if key == "" {
|
||||
return nil
|
||||
}
|
||||
val, ok := result[key]
|
||||
if !ok {
|
||||
result[key] = []interface{}{value}
|
||||
return nil
|
||||
}
|
||||
children, ok := val.([]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("expected type '[]interface{}' for key '%s', but got '%T'", key, val)
|
||||
}
|
||||
result[key] = append(children, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// The end is slice + map. like f[][a]
|
||||
if keys[1] == "" && length > 2 && keys[2] != "" {
|
||||
val, ok := result[key]
|
||||
if !ok {
|
||||
result[key] = []interface{}{}
|
||||
val = result[key]
|
||||
}
|
||||
children, ok := val.([]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("expected type '[]interface{}' for key '%s', but got '%T'", key, val)
|
||||
}
|
||||
if l := len(children); l > 0 {
|
||||
if child, ok := children[l-1].(map[string]interface{}); ok {
|
||||
if _, ok := child[keys[2]]; !ok {
|
||||
build(child, keys[2:], value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
child := map[string]interface{}{}
|
||||
build(child, keys[2:], value)
|
||||
result[key] = append(children, child)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// map. like f[a], f[a][b]
|
||||
val, ok := result[key]
|
||||
if !ok {
|
||||
result[key] = map[string]interface{}{}
|
||||
val = result[key]
|
||||
}
|
||||
children, ok := val.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("expected type 'map[string]interface{}' for key '%s', but got '%T'", key, val)
|
||||
}
|
||||
if err := build(children, keys[1:], value); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// split encodedString.
|
||||
parts := strings.Split(encodedString, "&")
|
||||
for _, part := range parts {
|
||||
pos := strings.Index(part, "=")
|
||||
if pos <= 0 {
|
||||
continue
|
||||
}
|
||||
key, err := url.QueryUnescape(part[:pos])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key[0] == ' ' {
|
||||
key = key[1:]
|
||||
}
|
||||
if key == "" || key[0] == '[' {
|
||||
continue
|
||||
}
|
||||
value, err := url.QueryUnescape(part[pos+1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// split into multiple keys
|
||||
var keys []string
|
||||
left := 0
|
||||
for i, k := range key {
|
||||
if k == '[' && left == 0 {
|
||||
left = i
|
||||
} else if k == ']' {
|
||||
if left > 0 {
|
||||
if len(keys) == 0 {
|
||||
keys = append(keys, key[:left])
|
||||
}
|
||||
keys = append(keys, key[left+1:i])
|
||||
left = 0
|
||||
if i+1 < len(key) && key[i+1] != '[' {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
// first key
|
||||
first := ""
|
||||
for i, chr := range keys[0] {
|
||||
if chr == ' ' || chr == '.' || chr == '[' {
|
||||
first += "_"
|
||||
} else {
|
||||
first += string(chr)
|
||||
}
|
||||
if chr == '[' {
|
||||
first += keys[0][i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
keys[0] = first
|
||||
|
||||
// build nested map
|
||||
if err := build(result, keys, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
100
g/string/gstr/gstr_pos.go
Normal file
100
g/string/gstr/gstr_pos.go
Normal file
@ -0,0 +1,100 @@
|
||||
package gstr
|
||||
|
||||
import "strings"
|
||||
|
||||
// Find the position of the first occurrence of a substring in a string.
|
||||
//
|
||||
// 返回 needle 在 haystack 中首次出现的数字位置。
|
||||
func Pos(haystack, needle string, startOffset...int) int {
|
||||
length := len(haystack)
|
||||
offset := 0
|
||||
if len(startOffset) > 0 {
|
||||
offset = startOffset[0]
|
||||
}
|
||||
if length == 0 || offset > length || -offset > length {
|
||||
return -1
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
offset += length
|
||||
}
|
||||
pos := strings.Index(haystack[offset : ], needle)
|
||||
if pos == -1 {
|
||||
return -1
|
||||
}
|
||||
return pos + offset
|
||||
}
|
||||
|
||||
// Find the position of the first occurrence of a case-insensitive substring in a string.
|
||||
//
|
||||
// 返回在字符串 haystack 中 needle 首次出现的数字位置(不区分大小写)。
|
||||
func PosI(haystack, needle string, startOffset...int) int {
|
||||
length := len(haystack)
|
||||
offset := 0
|
||||
if len(startOffset) > 0 {
|
||||
offset = startOffset[0]
|
||||
}
|
||||
if length == 0 || offset > length || -offset > length {
|
||||
return -1
|
||||
}
|
||||
|
||||
haystack = haystack[offset : ]
|
||||
if offset < 0 {
|
||||
offset += length
|
||||
}
|
||||
pos := strings.Index(strings.ToLower(haystack), strings.ToLower(needle))
|
||||
if pos == -1 {
|
||||
return -1
|
||||
}
|
||||
return pos + offset
|
||||
}
|
||||
|
||||
// Find the position of the last occurrence of a substring in a string.
|
||||
//
|
||||
// 查找指定字符串在目标字符串中最后一次出现的位置。
|
||||
func PosR(haystack, needle string, startOffset...int) int {
|
||||
offset := 0
|
||||
if len(startOffset) > 0 {
|
||||
offset = startOffset[0]
|
||||
}
|
||||
pos, length := 0, len(haystack)
|
||||
if length == 0 || offset > length || -offset > length {
|
||||
return -1
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
haystack = haystack[ : offset + length + 1]
|
||||
} else {
|
||||
haystack = haystack[offset : ]
|
||||
}
|
||||
pos = strings.LastIndex(haystack, needle)
|
||||
if offset > 0 && pos != -1 {
|
||||
pos += offset
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
// Find the position of the last occurrence of a case-insensitive substring in a string.
|
||||
//
|
||||
// 以不区分大小写的方式查找指定字符串在目标字符串中最后一次出现的位置。
|
||||
func PosRI(haystack, needle string, startOffset...int) int {
|
||||
offset := 0
|
||||
if len(startOffset) > 0 {
|
||||
offset = startOffset[0]
|
||||
}
|
||||
pos, length := 0, len(haystack)
|
||||
if length == 0 || offset > length || -offset > length {
|
||||
return -1
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
haystack = haystack[:offset+length+1]
|
||||
} else {
|
||||
haystack = haystack[offset:]
|
||||
}
|
||||
pos = strings.LastIndex(strings.ToLower(haystack), strings.ToLower(needle))
|
||||
if offset > 0 && pos != -1 {
|
||||
pos += offset
|
||||
}
|
||||
return pos
|
||||
}
|
||||
49
g/string/gstr/gstr_similartext.go
Normal file
49
g/string/gstr/gstr_similartext.go
Normal file
@ -0,0 +1,49 @@
|
||||
package gstr
|
||||
|
||||
// Calculate the similarity between two strings.
|
||||
// See http://php.net/manual/en/function.similar-text.php.
|
||||
//
|
||||
// 计算两个字符串的相似度。
|
||||
func SimilarText(first, second string, percent *float64) int {
|
||||
var similarText func(string, string, int, int) int
|
||||
similarText = func(str1, str2 string, len1, len2 int) int {
|
||||
var sum, max int
|
||||
pos1, pos2 := 0, 0
|
||||
|
||||
// Find the longest segment of the same section in two strings
|
||||
for i := 0; i < len1; i++ {
|
||||
for j := 0; j < len2; j++ {
|
||||
for l := 0; (i+l < len1) && (j+l < len2) && (str1[i+l] == str2[j+l]); l++ {
|
||||
if l+1 > max {
|
||||
max = l + 1
|
||||
pos1 = i
|
||||
pos2 = j
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sum = max; sum > 0 {
|
||||
if pos1 > 0 && pos2 > 0 {
|
||||
sum += similarText(str1, str2, pos1, pos2)
|
||||
}
|
||||
if (pos1+max < len1) && (pos2+max < len2) {
|
||||
s1 := []byte(str1)
|
||||
s2 := []byte(str2)
|
||||
sum += similarText(string(s1[pos1+max:]), string(s2[pos2+max:]), len1-pos1-max, len2-pos2-max)
|
||||
}
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
l1, l2 := len(first), len(second)
|
||||
if l1+l2 == 0 {
|
||||
return 0
|
||||
}
|
||||
sim := similarText(first, second, l1, l2)
|
||||
if percent != nil {
|
||||
*percent = float64(sim*200) / float64(l1+l2)
|
||||
}
|
||||
return sim
|
||||
}
|
||||
54
g/string/gstr/gstr_soundex.go
Normal file
54
g/string/gstr/gstr_soundex.go
Normal file
@ -0,0 +1,54 @@
|
||||
package gstr
|
||||
|
||||
// Calculate the soundex key of a string.
|
||||
// See http://php.net/manual/en/function.soundex.php.
|
||||
//
|
||||
// 计算字符串的SOUNDEX值,SOUNDEX为由四个字符组成的代码以评估两个字符串的相似性。
|
||||
func Soundex(str string) string {
|
||||
if str == "" {
|
||||
panic("str: cannot be an empty string")
|
||||
}
|
||||
table := [26]rune{
|
||||
'0', '1', '2', '3', // A, B, C, D
|
||||
'0', '1', '2', // E, F, G
|
||||
'0', // H
|
||||
'0', '2', '2', '4', '5', '5', // I, J, K, L, M, N
|
||||
'0', '1', '2', '6', '2', '3', // O, P, Q, R, S, T
|
||||
'0', '1', // U, V
|
||||
'0', '2', // W, X
|
||||
'0', '2', // Y, Z
|
||||
}
|
||||
last, code, small := -1, 0, 0
|
||||
sd := make([]rune, 4)
|
||||
// build soundex string
|
||||
for i := 0; i < len(str) && small < 4; i++ {
|
||||
// ToUpper
|
||||
char := str[i]
|
||||
if char < '\u007F' && 'a' <= char && char <= 'z' {
|
||||
code = int(char - 'a' + 'A')
|
||||
} else {
|
||||
code = int(char)
|
||||
}
|
||||
if code >= 'A' && code <= 'Z' {
|
||||
if small == 0 {
|
||||
sd[small] = rune(code)
|
||||
small++
|
||||
last = int(table[code-'A'])
|
||||
} else {
|
||||
code = int(table[code-'A'])
|
||||
if code != last {
|
||||
if code != 0 {
|
||||
sd[small] = rune(code)
|
||||
small++
|
||||
}
|
||||
last = code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// pad with "0"
|
||||
for ; small < 4; small++ {
|
||||
sd[small] = '0'
|
||||
}
|
||||
return string(sd)
|
||||
}
|
||||
34
g/string/gstr/gstr_z_bench_test.go
Normal file
34
g/string/gstr/gstr_z_bench_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gstr_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
str = "This is the test string for gstr."
|
||||
bytes = []byte(str)
|
||||
)
|
||||
|
||||
func Benchmark_StringToBytes(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if []byte(str) != nil {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_BytesToString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if string(bytes) != "" {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
54
g/string/gstr/gstr_z_unit_pos_test.go
Normal file
54
g/string/gstr/gstr_z_unit_pos_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gstr_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/string/gstr"
|
||||
"gitee.com/johng/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Pos(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFGabcdefg"
|
||||
gtest.Assert(gstr.Pos(s1, "ab"), 0)
|
||||
gtest.Assert(gstr.Pos(s1, "ab", 2), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PosI(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFGabcdefg"
|
||||
gtest.Assert(gstr.PosI(s1, "zz"), -1)
|
||||
gtest.Assert(gstr.PosI(s1, "ab"), 0)
|
||||
gtest.Assert(gstr.PosI(s1, "ef", 2), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PosR(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFGabcdefg"
|
||||
s2 := "abcdEFGz1cdeab"
|
||||
gtest.Assert(gstr.PosR(s1, "zz"), -1)
|
||||
gtest.Assert(gstr.PosR(s1, "ab"), 7)
|
||||
gtest.Assert(gstr.PosR(s2, "ab", -2), 0)
|
||||
gtest.Assert(gstr.PosR(s1, "ef"), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PosRI(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFGabcdefg"
|
||||
s2 := "abcdEFGz1cdeab"
|
||||
gtest.Assert(gstr.PosRI(s1, "zz"), -1)
|
||||
gtest.Assert(gstr.PosRI(s1, "AB"), 7)
|
||||
gtest.Assert(gstr.PosRI(s2, "AB", -2), 0)
|
||||
gtest.Assert(gstr.PosRI(s1, "EF"), 11)
|
||||
})
|
||||
}
|
||||
287
g/string/gstr/gstr_z_unit_test.go
Normal file
287
g/string/gstr/gstr_z_unit_test.go
Normal file
@ -0,0 +1,287 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gstr_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g"
|
||||
"gitee.com/johng/gf/g/string/gstr"
|
||||
"gitee.com/johng/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Replace(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFG乱入的中文abcdefg"
|
||||
gtest.Assert(gstr.Replace(s1, "ab", "AB"), "ABcdEFG乱入的中文ABcdefg")
|
||||
gtest.Assert(gstr.Replace(s1, "EF", "ef"), "abcdefG乱入的中文abcdefg")
|
||||
gtest.Assert(gstr.Replace(s1, "MN", "mn"), s1)
|
||||
gtest.Assert(gstr.ReplaceByMap(s1, g.MapStrStr{
|
||||
"a" : "A",
|
||||
"G" : "g",
|
||||
}), "AbcdEFg乱入的中文Abcdefg")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ToLower(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFG乱入的中文abcdefg"
|
||||
e1 := "abcdefg乱入的中文abcdefg"
|
||||
gtest.Assert(gstr.ToLower(s1), e1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ToUpper(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFG乱入的中文abcdefg"
|
||||
e1 := "ABCDEFG乱入的中文ABCDEFG"
|
||||
gtest.Assert(gstr.ToUpper(s1), e1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_UcFirst(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "abcdEFG乱入的中文abcdefg"
|
||||
e1 := "AbcdEFG乱入的中文abcdefg"
|
||||
gtest.Assert(gstr.UcFirst(s1), e1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_LcFirst(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "AbcdEFG乱入的中文abcdefg"
|
||||
e1 := "abcdEFG乱入的中文abcdefg"
|
||||
gtest.Assert(gstr.LcFirst(s1), e1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_UcWords(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := "我爱GF: i love go frame"
|
||||
e1 := "我爱GF: I Love Go Frame"
|
||||
gtest.Assert(gstr.UcWords(s1), e1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SearchArray(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
array := []string{"a", "b", "c"}
|
||||
gtest.Assert(gstr.SearchArray(array, "a"), 0)
|
||||
gtest.Assert(gstr.SearchArray(array, "b"), 1)
|
||||
gtest.Assert(gstr.SearchArray(array, "c"), 2)
|
||||
gtest.Assert(gstr.SearchArray(array, "d"), -1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_InArray(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
array := []string{"a", "b", "c"}
|
||||
gtest.Assert(gstr.InArray(array, "a"), true)
|
||||
gtest.Assert(gstr.InArray(array, "b"), true)
|
||||
gtest.Assert(gstr.InArray(array, "c"), true)
|
||||
gtest.Assert(gstr.InArray(array, "d"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IsLetterLower(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.IsLetterLower('a'), true)
|
||||
gtest.Assert(gstr.IsLetterLower('A'), false)
|
||||
gtest.Assert(gstr.IsLetterLower('1'), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IsLetterUpper(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.IsLetterUpper('a'), false)
|
||||
gtest.Assert(gstr.IsLetterUpper('A'), true)
|
||||
gtest.Assert(gstr.IsLetterUpper('1'), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IsNumeric(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.IsNumeric("1a我"), false)
|
||||
gtest.Assert(gstr.IsNumeric("0123"), true)
|
||||
gtest.Assert(gstr.IsNumeric("我是中国人"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SubStr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 0), "我爱GoFrame")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 2), "GoFrame")
|
||||
gtest.Assert(gstr.SubStr("我爱GoFrame", 2, 2), "Go")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrLimit(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.StrLimit("我爱GoFrame", 2), "我爱...")
|
||||
gtest.Assert(gstr.StrLimit("我爱GoFrame", 2, ""), "我爱")
|
||||
gtest.Assert(gstr.StrLimit("我爱GoFrame", 2, "**"), "我爱**")
|
||||
gtest.Assert(gstr.StrLimit("我爱GoFrame", 4, ""), "我爱Go")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Reverse(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Reverse("我爱123"), "321爱我")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_NumberFormat(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.NumberFormat(1234567.8910, 2, ".", ","), "1,234,567.89")
|
||||
gtest.Assert(gstr.NumberFormat(1234567.8910, 2, "#", "/"), "1/234/567#89")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ChunkSplit(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.ChunkSplit("1234", 1, "#"), "1#2#3#4#")
|
||||
gtest.Assert(gstr.ChunkSplit("我爱123", 1, "#"), "我#爱#1#2#3#")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_WordCount(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.WordCount("我爱 GoFrame!"), []string{"我爱", "GoFrame!"})
|
||||
gtest.Assert(gstr.WordCount("I love GoFrame!"), []string{"I", "love", "GoFrame!"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_WordWrap(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.WordWrap("12 34", 2, "<br>"), "12<br>34")
|
||||
gtest.Assert(gstr.WordWrap("A very long woooooooooooooooooord. and something", 2, "<br>"),
|
||||
"A very<br>long<br>woooooooooooooooooord.<br>and<br>something")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_RuneLen(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.RuneLen("1234"), 4)
|
||||
gtest.Assert(gstr.RuneLen("我爱GoFrame"), 9)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Repeat(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Repeat("go", 3), "gogogo")
|
||||
gtest.Assert(gstr.Repeat("好的", 3), "好的好的好的")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Str(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Str("name@example.com", "@"), "@example.com")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Tr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Tr("name@example.com", "@"), "@example.com")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Shuffle(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(len(gstr.Shuffle("123456")), 6)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Trim(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Trim(" 123456\n "), "123456")
|
||||
gtest.Assert(gstr.Trim("#123456#;", "#;"), "123456")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TrimRight(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.TrimRight(" 123456\n "), " 123456")
|
||||
gtest.Assert(gstr.TrimRight("#123456#;", "#;"), "#123456")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TrimLeft(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.TrimLeft(" \r123456\n "), "123456\n ")
|
||||
gtest.Assert(gstr.TrimLeft("#;123456#;", "#;"), "123456#;")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Split(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Split("1.2", "."), []string{"1", "2"})
|
||||
gtest.Assert(gstr.Split("我爱 - GoFrame", " - "), []string{"我爱", "GoFrame"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Join([]string{"我爱", "GoFrame"}, " - "), "我爱 - GoFrame")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Explode(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Explode(" - ", "我爱 - GoFrame"), []string{"我爱", "GoFrame"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Implode(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Implode(" - ", []string{"我爱", "GoFrame"}), "我爱 - GoFrame")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Chr(65), "A")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Ord(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Ord("A"), 65)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_HideStr(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.HideStr("15928008611", 40, "*"), "159****8611")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Nl2Br(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Nl2Br("1\n2"), "1<br>2")
|
||||
gtest.Assert(gstr.Nl2Br("1\r\n2"), "1<br>2")
|
||||
gtest.Assert(gstr.Nl2Br("1\r\n2", true), "1<br />2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AddSlashes(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.AddSlashes(`1'2"3\`), `1\'2\"3\\`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StripSlashes(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.StripSlashes(`1\'2\"3\\`), `1'2"3\`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_QuoteMeta(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.QuoteMeta(`.\+*?[^]($)`), `\.\\\+\*\?\[\^\]\(\$\)`)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user