diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index d92124e25..c94c0b0a3 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -55,7 +55,7 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { labels = append(labels, gtrace.CommonLabels()...) labels = append(labels, attribute.String(tracingAttrDbType, c.db.GetConfig().Type), - attribute.String(string(semconv.DBStatementKey), sql.Format), + semconv.DBStatementKey.String(sql.Format), ) if c.db.GetConfig().Host != "" { labels = append(labels, attribute.String(tracingAttrDbHost, c.db.GetConfig().Host)) diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 0dd27cda4..474463137 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -12,12 +12,13 @@ import ( "reflect" "time" + "github.com/gorilla/websocket" + "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/container/gtype" "github.com/gogf/gf/v2/os/gcache" "github.com/gogf/gf/v2/os/gsession" "github.com/gogf/gf/v2/protocol/goai" - "github.com/gorilla/websocket" ) type ( @@ -132,7 +133,7 @@ const ( contentTypeHtml = "text/html" contentTypeJson = "application/json" swaggerUIPackedPath = "/goframe/swaggerui" - responseTraceIdHeader = "Trace-Id" + responseTraceIDHeader = "Trace-ID" ) var ( diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index d1f0d041c..eb6f89ef1 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -143,7 +143,7 @@ func (r *Response) ClearBuffer() { // Flush outputs the buffer content to the client and clears the buffer. func (r *Response) Flush() { - r.Header().Set(responseTraceIdHeader, gtrace.GetTraceID(r.Request.Context())) + r.Header().Set(responseTraceIDHeader, gtrace.GetTraceID(r.Request.Context())) if r.Server.config.ServerAgent != "" { r.Header().Set("Server", r.Server.config.ServerAgent) } diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index e5b55c819..5c005b8b0 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -12,7 +12,6 @@ import ( "os" "strings" - "github.com/gogf/gf/v2/net/gtrace/internal/provider" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/propagation" @@ -23,6 +22,7 @@ import ( "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/internal/command" "github.com/gogf/gf/v2/net/gipv4" + "github.com/gogf/gf/v2/net/gtrace/internal/provider" "github.com/gogf/gf/v2/util/gconv" ) diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index 277e5ff85..9932c5908 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -7,20 +7,6 @@ // Package gstr provides functions for string handling. package gstr -import ( - "bytes" - "fmt" - "math" - "strconv" - "strings" - "unicode" - "unicode/utf8" - - "github.com/gogf/gf/v2/internal/utils" - "github.com/gogf/gf/v2/util/gconv" - "github.com/gogf/gf/v2/util/grand" -) - const ( // NotFoundIndex is the position index for string not found in searching functions. NotFoundIndex = -1 @@ -29,441 +15,3 @@ const ( const ( defaultSuffixForStrLimit = "..." ) - -// Count counts the number of `substr` appears in `s`. -// It returns 0 if no `substr` found in `s`. -func Count(s, substr string) int { - return strings.Count(s, substr) -} - -// CountI counts the number of `substr` appears in `s`, case-insensitively. -// It returns 0 if no `substr` found in `s`. -func CountI(s, substr string) int { - return strings.Count(ToLower(s), ToLower(substr)) -} - -// ToLower returns a copy of the string s with all Unicode letters mapped to their lower case. -func ToLower(s string) string { - return strings.ToLower(s) -} - -// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case. -func ToUpper(s string) string { - return strings.ToUpper(s) -} - -// UcFirst returns a copy of the string s with the first letter mapped to its upper case. -func UcFirst(s string) string { - return utils.UcFirst(s) -} - -// LcFirst returns a copy of the string s with the first letter mapped to its lower case. -func LcFirst(s string) string { - if len(s) == 0 { - return s - } - if IsLetterUpper(s[0]) { - return string(s[0]+32) + s[1:] - } - return s -} - -// UcWords uppercase the first character of each word in a string. -func UcWords(str string) string { - return strings.Title(str) -} - -// IsLetterLower tests whether the given byte b is in lower case. -func IsLetterLower(b byte) bool { - return utils.IsLetterLower(b) -} - -// IsLetterUpper tests whether the given byte b is in upper case. -func IsLetterUpper(b byte) bool { - return utils.IsLetterUpper(b) -} - -// IsNumeric tests whether the given string s is numeric. -func IsNumeric(s string) bool { - return utils.IsNumeric(s) -} - -// Reverse returns a string which is the reverse of `str`. -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) -} - -// NumberFormat formats 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 -} - -// ChunkSplit splits 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. -// It considers parameter `body` and `end` as unicode string. -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) -} - -// Compare returns an integer comparing two strings lexicographically. -// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. -func Compare(a, b string) int { - return strings.Compare(a, b) -} - -// Equal reports whether `a` and `b`, interpreted as UTF-8 strings, -// are equal under Unicode case-folding, case-insensitively. -func Equal(a, b string) bool { - return strings.EqualFold(a, b) -} - -// Fields returns the words used in a string as slice. -func Fields(str string) []string { - return strings.Fields(str) -} - -// HasPrefix tests whether the string s begins with prefix. -func HasPrefix(s, prefix string) bool { - return strings.HasPrefix(s, prefix) -} - -// HasSuffix tests whether the string s ends with suffix. -func HasSuffix(s, suffix string) bool { - return strings.HasSuffix(s, suffix) -} - -// CountWords returns information about words' count used in a string. -// It considers parameter `str` as unicode string. -func CountWords(str string) map[string]int { - m := make(map[string]int) - buffer := bytes.NewBuffer(nil) - for _, r := range []rune(str) { - if unicode.IsSpace(r) { - if buffer.Len() > 0 { - m[buffer.String()]++ - buffer.Reset() - } - } else { - buffer.WriteRune(r) - } - } - if buffer.Len() > 0 { - m[buffer.String()]++ - } - return m -} - -// CountChars returns information about chars' count used in a string. -// It considers parameter `str` as unicode string. -func CountChars(str string, noSpace ...bool) map[string]int { - m := make(map[string]int) - countSpace := true - if len(noSpace) > 0 && noSpace[0] { - countSpace = false - } - for _, r := range []rune(str) { - if !countSpace && unicode.IsSpace(r) { - continue - } - m[string(r)]++ - } - return m -} - -// WordWrap wraps a string to a given number of characters. -// TODO: Enable cut parameter, see http://php.net/manual/en/function.wordwrap.php. -func WordWrap(str string, width int, br string) string { - if br == "" { - br = "\n" - } - var ( - current int - wordBuf, spaceBuf bytes.Buffer - init = make([]byte, 0, len(str)) - buf = bytes.NewBuffer(init) - ) - for _, char := range []rune(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() -} - -// LenRune returns string length of unicode. -func LenRune(str string) int { - return utf8.RuneCountInString(str) -} - -// Repeat returns a new string consisting of multiplier copies of the string input. -func Repeat(input string, multiplier int) string { - return strings.Repeat(input, multiplier) -} - -// Shuffle randomly shuffles a string. -// It considers parameter `str` as unicode 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) -} - -// Split splits string `str` by a string `delimiter`, to an array. -func Split(str, delimiter string) []string { - return strings.Split(str, delimiter) -} - -// SplitAndTrim splits string `str` by a string `delimiter` to an array, -// and calls Trim to every element of this array. It ignores the elements -// which are empty after Trim. -func SplitAndTrim(str, delimiter string, characterMask ...string) []string { - return utils.SplitAndTrim(str, delimiter, characterMask...) -} - -// Join concatenates the elements of `array` to create a single string. The separator string -// `sep` is placed between elements in the resulting string. -func Join(array []string, sep string) string { - return strings.Join(array, sep) -} - -// JoinAny concatenates the elements of `array` to create a single string. The separator string -// `sep` is placed between elements in the resulting string. -// -// The parameter `array` can be any type of slice, which be converted to string array. -func JoinAny(array interface{}, sep string) string { - return strings.Join(gconv.Strings(array), sep) -} - -// Explode splits string `str` by a string `delimiter`, to an array. -// See http://php.net/manual/en/function.explode.php. -func Explode(delimiter, str string) []string { - return Split(str, delimiter) -} - -// Implode joins array elements `pieces` with a string `glue`. -// http://php.net/manual/en/function.implode.php -func Implode(glue string, pieces []string) string { - return strings.Join(pieces, glue) -} - -// Chr return the ascii string of a number(0-255). -func Chr(ascii int) string { - return string([]byte{byte(ascii % 256)}) -} - -// Ord converts the first byte of a string to a value between 0 and 255. -func Ord(char string) int { - return int(char[0]) -} - -// HideStr replaces part of the string `str` to `hide` by `percentage` from the `middle`. -// It considers parameter `str` as unicode string. -func HideStr(str string, percent int, hide string) string { - array := strings.Split(str, "@") - if len(array) > 1 { - str = array[0] - } - var ( - 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 < 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() -} - -// Nl2Br inserts HTML line breaks(`br`|
) before all newlines in a string: -// \n\r, \r\n, \r, \n. -// It considers parameter `str` as unicode string. -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("
") - } else { - br = []byte("
") - } - 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() -} - -// AddSlashes quotes chars('"\) with slashes. -func AddSlashes(str string) string { - var buf bytes.Buffer - for _, char := range str { - switch char { - case '\'', '"', '\\': - buf.WriteRune('\\') - } - buf.WriteRune(char) - } - return buf.String() -} - -// StripSlashes un-quotes a quoted string by AddSlashes. -func StripSlashes(str string) string { - return utils.StripSlashes(str) -} - -// QuoteMeta returns a version of str with a backslash character (\) -// before every character that is among: .\+*?[^]($) -func QuoteMeta(str string, chars ...string) string { - var buf bytes.Buffer - for _, char := range str { - if len(chars) > 0 { - for _, c := range chars[0] { - if c == char { - buf.WriteRune('\\') - break - } - } - } else { - switch char { - case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?': - buf.WriteRune('\\') - } - } - buf.WriteRune(char) - } - return buf.String() -} diff --git a/text/gstr/gstr_compare.go b/text/gstr/gstr_compare.go new file mode 100644 index 000000000..ae877f677 --- /dev/null +++ b/text/gstr/gstr_compare.go @@ -0,0 +1,21 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +import "strings" + +// Compare returns an integer comparing two strings lexicographically. +// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. +func Compare(a, b string) int { + return strings.Compare(a, b) +} + +// Equal reports whether `a` and `b`, interpreted as UTF-8 strings, +// are equal under Unicode case-folding, case-insensitively. +func Equal(a, b string) bool { + return strings.EqualFold(a, b) +} diff --git a/text/gstr/gstr_convert.go b/text/gstr/gstr_convert.go index 7efce8c2b..7c4a6add3 100644 --- a/text/gstr/gstr_convert.go +++ b/text/gstr/gstr_convert.go @@ -7,10 +7,26 @@ package gstr import ( + "bytes" + "fmt" + "github.com/gogf/gf/v2/util/grand" + "math" "regexp" "strconv" + "strings" + "unicode" ) +// Chr return the ascii string of a number(0-255). +func Chr(ascii int) string { + return string([]byte{byte(ascii % 256)}) +} + +// Ord converts the first byte of a string to a value between 0 and 255. +func Ord(char string) int { + return int(char[0]) +} + var ( // octReg is the regular expression object for checks octal string. octReg = regexp.MustCompile(`\\[0-7]{3}`) @@ -28,3 +44,193 @@ func OctStr(str string) string { }, ) } + +// Reverse returns a string which is the reverse of `str`. +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) +} + +// NumberFormat formats 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 +} + +// Shuffle randomly shuffles a string. +// It considers parameter `str` as unicode 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) +} + +// HideStr replaces part of the string `str` to `hide` by `percentage` from the `middle`. +// It considers parameter `str` as unicode string. +func HideStr(str string, percent int, hide string) string { + array := strings.Split(str, "@") + if len(array) > 1 { + str = array[0] + } + var ( + 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 < 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() +} + +// Nl2Br inserts HTML line breaks(`br`|
) before all newlines in a string: +// \n\r, \r\n, \r, \n. +// It considers parameter `str` as unicode string. +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("
") + } else { + br = []byte("
") + } + 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() +} + +// WordWrap wraps a string to a given number of characters. +// TODO: Enable cut parameter, see http://php.net/manual/en/function.wordwrap.php. +func WordWrap(str string, width int, br string) string { + if br == "" { + br = "\n" + } + var ( + current int + wordBuf, spaceBuf bytes.Buffer + init = make([]byte, 0, len(str)) + buf = bytes.NewBuffer(init) + ) + for _, char := range []rune(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() +} diff --git a/text/gstr/gstr_count.go b/text/gstr/gstr_count.go new file mode 100644 index 000000000..cd0f0a19e --- /dev/null +++ b/text/gstr/gstr_count.go @@ -0,0 +1,63 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +import ( + "bytes" + "strings" + "unicode" +) + +// Count counts the number of `substr` appears in `s`. +// It returns 0 if no `substr` found in `s`. +func Count(s, substr string) int { + return strings.Count(s, substr) +} + +// CountI counts the number of `substr` appears in `s`, case-insensitively. +// It returns 0 if no `substr` found in `s`. +func CountI(s, substr string) int { + return strings.Count(ToLower(s), ToLower(substr)) +} + +// CountWords returns information about words' count used in a string. +// It considers parameter `str` as unicode string. +func CountWords(str string) map[string]int { + m := make(map[string]int) + buffer := bytes.NewBuffer(nil) + for _, r := range []rune(str) { + if unicode.IsSpace(r) { + if buffer.Len() > 0 { + m[buffer.String()]++ + buffer.Reset() + } + } else { + buffer.WriteRune(r) + } + } + if buffer.Len() > 0 { + m[buffer.String()]++ + } + return m +} + +// CountChars returns information about chars' count used in a string. +// It considers parameter `str` as unicode string. +func CountChars(str string, noSpace ...bool) map[string]int { + m := make(map[string]int) + countSpace := true + if len(noSpace) > 0 && noSpace[0] { + countSpace = false + } + for _, r := range []rune(str) { + if !countSpace && unicode.IsSpace(r) { + continue + } + m[string(r)]++ + } + return m +} diff --git a/text/gstr/gstr_create.go b/text/gstr/gstr_create.go new file mode 100644 index 000000000..8e5ff3129 --- /dev/null +++ b/text/gstr/gstr_create.go @@ -0,0 +1,14 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +import "strings" + +// Repeat returns a new string consisting of multiplier copies of the string input. +func Repeat(input string, multiplier int) string { + return strings.Repeat(input, multiplier) +} diff --git a/text/gstr/gstr_is.go b/text/gstr/gstr_is.go new file mode 100644 index 000000000..2f52e9495 --- /dev/null +++ b/text/gstr/gstr_is.go @@ -0,0 +1,14 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +import "github.com/gogf/gf/v2/internal/utils" + +// IsNumeric tests whether the given string s is numeric. +func IsNumeric(s string) bool { + return utils.IsNumeric(s) +} diff --git a/text/gstr/gstr_length.go b/text/gstr/gstr_length.go new file mode 100644 index 000000000..9e5b915b4 --- /dev/null +++ b/text/gstr/gstr_length.go @@ -0,0 +1,14 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +import "unicode/utf8" + +// LenRune returns string length of unicode. +func LenRune(str string) int { + return utf8.RuneCountInString(str) +} diff --git a/text/gstr/gstr_levenshtein.go b/text/gstr/gstr_levenshtein.go deleted file mode 100644 index 489c6eb3b..000000000 --- a/text/gstr/gstr_levenshtein.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. - -package gstr - -// Levenshtein calculates 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. -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 -} diff --git a/text/gstr/gstr_similar.go b/text/gstr/gstr_similar.go new file mode 100644 index 000000000..7c19618fb --- /dev/null +++ b/text/gstr/gstr_similar.go @@ -0,0 +1,158 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +// Levenshtein calculates 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. +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 +} + +// SimilarText calculates 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 +} + +// Soundex calculates the soundex key of a string. +// See http://php.net/manual/en/function.soundex.php. +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) +} diff --git a/text/gstr/gstr_similartext.go b/text/gstr/gstr_similartext.go deleted file mode 100644 index efb253739..000000000 --- a/text/gstr/gstr_similartext.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. - -package gstr - -// SimilarText calculates 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 -} diff --git a/text/gstr/gstr_slashes.go b/text/gstr/gstr_slashes.go new file mode 100644 index 000000000..e897673ba --- /dev/null +++ b/text/gstr/gstr_slashes.go @@ -0,0 +1,53 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +import ( + "bytes" + "github.com/gogf/gf/v2/internal/utils" +) + +// AddSlashes quotes chars('"\) with slashes. +func AddSlashes(str string) string { + var buf bytes.Buffer + for _, char := range str { + switch char { + case '\'', '"', '\\': + buf.WriteRune('\\') + } + buf.WriteRune(char) + } + return buf.String() +} + +// StripSlashes un-quotes a quoted string by AddSlashes. +func StripSlashes(str string) string { + return utils.StripSlashes(str) +} + +// QuoteMeta returns a version of str with a backslash character (\) +// before every character that is among: .\+*?[^]($) +func QuoteMeta(str string, chars ...string) string { + var buf bytes.Buffer + for _, char := range str { + if len(chars) > 0 { + for _, c := range chars[0] { + if c == char { + buf.WriteRune('\\') + break + } + } + } else { + switch char { + case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?': + buf.WriteRune('\\') + } + } + buf.WriteRune(char) + } + return buf.String() +} diff --git a/text/gstr/gstr_soundex.go b/text/gstr/gstr_soundex.go deleted file mode 100644 index bdba8933e..000000000 --- a/text/gstr/gstr_soundex.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. - -package gstr - -// Soundex calculates the soundex key of a string. -// See http://php.net/manual/en/function.soundex.php. -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) -} diff --git a/text/gstr/gstr_split_join.go b/text/gstr/gstr_split_join.go new file mode 100644 index 000000000..737f85c21 --- /dev/null +++ b/text/gstr/gstr_split_join.go @@ -0,0 +1,82 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +import ( + "github.com/gogf/gf/v2/internal/utils" + "github.com/gogf/gf/v2/util/gconv" + "strings" +) + +// Split splits string `str` by a string `delimiter`, to an array. +func Split(str, delimiter string) []string { + return strings.Split(str, delimiter) +} + +// SplitAndTrim splits string `str` by a string `delimiter` to an array, +// and calls Trim to every element of this array. It ignores the elements +// which are empty after Trim. +func SplitAndTrim(str, delimiter string, characterMask ...string) []string { + return utils.SplitAndTrim(str, delimiter, characterMask...) +} + +// Join concatenates the elements of `array` to create a single string. The separator string +// `sep` is placed between elements in the resulting string. +func Join(array []string, sep string) string { + return strings.Join(array, sep) +} + +// JoinAny concatenates the elements of `array` to create a single string. The separator string +// `sep` is placed between elements in the resulting string. +// +// The parameter `array` can be any type of slice, which be converted to string array. +func JoinAny(array interface{}, sep string) string { + return strings.Join(gconv.Strings(array), sep) +} + +// Explode splits string `str` by a string `delimiter`, to an array. +// See http://php.net/manual/en/function.explode.php. +func Explode(delimiter, str string) []string { + return Split(str, delimiter) +} + +// Implode joins array elements `pieces` with a string `glue`. +// http://php.net/manual/en/function.implode.php +func Implode(glue string, pieces []string) string { + return strings.Join(pieces, glue) +} + +// ChunkSplit splits 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. +// It considers parameter `body` and `end` as unicode string. +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) +} + +// Fields returns the words used in a string as slice. +func Fields(str string) []string { + return strings.Fields(str) +} diff --git a/text/gstr/gstr_str.go b/text/gstr/gstr_str.go deleted file mode 100644 index 8b316efd1..000000000 --- a/text/gstr/gstr_str.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. - -package gstr - -import "strings" - -// Str 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. -func Str(haystack string, needle string) string { - if needle == "" { - return "" - } - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex { - return "" - } - return haystack[pos+len([]byte(needle))-1:] -} - -// StrEx returns part of `haystack` string starting from and excluding -// the first occurrence of `needle` to the end of `haystack`. -func StrEx(haystack string, needle string) string { - if s := Str(haystack, needle); s != "" { - return s[1:] - } - return "" -} - -// StrTill returns part of `haystack` string ending to and including -// the first occurrence of `needle` from the start of `haystack`. -func StrTill(haystack string, needle string) string { - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex || pos == 0 { - return "" - } - return haystack[:pos+1] -} - -// StrTillEx returns part of `haystack` string ending to and excluding -// the first occurrence of `needle` from the start of `haystack`. -func StrTillEx(haystack string, needle string) string { - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex || pos == 0 { - return "" - } - return haystack[:pos] -} diff --git a/text/gstr/gstr_substr.go b/text/gstr/gstr_sub.go similarity index 73% rename from text/gstr/gstr_substr.go rename to text/gstr/gstr_sub.go index 18481ca55..8eef7edc6 100644 --- a/text/gstr/gstr_substr.go +++ b/text/gstr/gstr_sub.go @@ -6,6 +6,51 @@ package gstr +import "strings" + +// Str 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. +func Str(haystack string, needle string) string { + if needle == "" { + return "" + } + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex { + return "" + } + return haystack[pos+len([]byte(needle))-1:] +} + +// StrEx returns part of `haystack` string starting from and excluding +// the first occurrence of `needle` to the end of `haystack`. +func StrEx(haystack string, needle string) string { + if s := Str(haystack, needle); s != "" { + return s[1:] + } + return "" +} + +// StrTill returns part of `haystack` string ending to and including +// the first occurrence of `needle` from the start of `haystack`. +func StrTill(haystack string, needle string) string { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex || pos == 0 { + return "" + } + return haystack[:pos+1] +} + +// StrTillEx returns part of `haystack` string ending to and excluding +// the first occurrence of `needle` from the start of `haystack`. +func StrTillEx(haystack string, needle string) string { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex || pos == 0 { + return "" + } + return haystack[:pos] +} + // SubStr returns a portion of string `str` specified by the `start` and `length` parameters. // The parameter `length` is optional, it uses the length of `str` in default. func SubStr(str string, start int, length ...int) (substr string) { diff --git a/text/gstr/gstr_trim.go b/text/gstr/gstr_trim.go index ea46102f1..f7701505e 100644 --- a/text/gstr/gstr_trim.go +++ b/text/gstr/gstr_trim.go @@ -102,3 +102,13 @@ func TrimAll(str string, characterMask ...string) string { } return string(slice) } + +// HasPrefix tests whether the string s begins with prefix. +func HasPrefix(s, prefix string) bool { + return strings.HasPrefix(s, prefix) +} + +// HasSuffix tests whether the string s ends with suffix. +func HasSuffix(s, suffix string) bool { + return strings.HasSuffix(s, suffix) +} diff --git a/text/gstr/gstr_upper_lower.go b/text/gstr/gstr_upper_lower.go new file mode 100644 index 000000000..3d6c75ad9 --- /dev/null +++ b/text/gstr/gstr_upper_lower.go @@ -0,0 +1,53 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package gstr + +import ( + "github.com/gogf/gf/v2/internal/utils" + "strings" +) + +// ToLower returns a copy of the string s with all Unicode letters mapped to their lower case. +func ToLower(s string) string { + return strings.ToLower(s) +} + +// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case. +func ToUpper(s string) string { + return strings.ToUpper(s) +} + +// UcFirst returns a copy of the string s with the first letter mapped to its upper case. +func UcFirst(s string) string { + return utils.UcFirst(s) +} + +// LcFirst returns a copy of the string s with the first letter mapped to its lower case. +func LcFirst(s string) string { + if len(s) == 0 { + return s + } + if IsLetterUpper(s[0]) { + return string(s[0]+32) + s[1:] + } + return s +} + +// UcWords uppercase the first character of each word in a string. +func UcWords(str string) string { + return strings.Title(str) +} + +// IsLetterLower tests whether the given byte b is in lower case. +func IsLetterLower(b byte) bool { + return utils.IsLetterLower(b) +} + +// IsLetterUpper tests whether the given byte b is in upper case. +func IsLetterUpper(b byte) bool { + return utils.IsLetterUpper(b) +}