mirror of
https://gitee.com/johng/gf.git
synced 2024-11-29 18:57:44 +08:00
286 lines
6.7 KiB
Go
286 lines
6.7 KiB
Go
// 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"
|
||
"fmt"
|
||
"math"
|
||
"regexp"
|
||
"strconv"
|
||
"strings"
|
||
"unicode"
|
||
|
||
"github.com/gogf/gf/v2/util/grand"
|
||
)
|
||
|
||
var (
|
||
// octReg is the regular expression object for checks octal string.
|
||
octReg = regexp.MustCompile(`\\[0-7]{3}`)
|
||
)
|
||
|
||
// Chr return the ascii string of a number(0-255).
|
||
//
|
||
// Example:
|
||
// Chr(65) -> "A"
|
||
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.
|
||
//
|
||
// Example:
|
||
// Chr("A") -> 65
|
||
func Ord(char string) int {
|
||
return int(char[0])
|
||
}
|
||
|
||
// OctStr converts string container octal string to its original string,
|
||
// for example, to Chinese string.
|
||
//
|
||
// Example:
|
||
// OctStr("\346\200\241") -> 怡
|
||
func OctStr(str string) string {
|
||
return octReg.ReplaceAllStringFunc(
|
||
str,
|
||
func(s string) string {
|
||
i, _ := strconv.ParseInt(s[1:], 8, 0)
|
||
return string([]byte{byte(i)})
|
||
},
|
||
)
|
||
}
|
||
|
||
// Reverse returns a string which is the reverse of `str`.
|
||
//
|
||
// Example:
|
||
// Reverse("123456") -> "654321"
|
||
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.
|
||
// Parameter `decimals`: Sets the number of decimal points.
|
||
// Parameter `decPoint`: Sets the separator for the decimal point.
|
||
// Parameter `thousandsSep`: Sets the thousands' separator.
|
||
// See http://php.net/manual/en/function.number-format.php.
|
||
//
|
||
// Example:
|
||
// NumberFormat(1234.56, 2, ".", "") -> 1234,56
|
||
// NumberFormat(1234.56, 2, ",", " ") -> 1 234,56
|
||
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.
|
||
//
|
||
// Example:
|
||
// Shuffle("123456") -> "325164"
|
||
// Shuffle("123456") -> "231546"
|
||
// ...
|
||
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`|<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("<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()
|
||
}
|
||
|
||
// WordWrap wraps a string to a given number of characters.
|
||
// This function supports cut parameters of both english and chinese punctuations.
|
||
// TODO: Enable custom 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)
|
||
strRunes = []rune(str)
|
||
)
|
||
for _, char := range strRunes {
|
||
switch {
|
||
case 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
|
||
|
||
case 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)
|
||
|
||
case isPunctuation(char):
|
||
wordBuf.WriteRune(char)
|
||
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
|
||
current += spaceBuf.Len() + wordBuf.Len()
|
||
_, _ = spaceBuf.WriteTo(buf)
|
||
spaceBuf.Reset()
|
||
_, _ = wordBuf.WriteTo(buf)
|
||
wordBuf.Reset()
|
||
}
|
||
|
||
default:
|
||
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()
|
||
}
|
||
|
||
func isPunctuation(char int32) bool {
|
||
switch char {
|
||
// English Punctuations.
|
||
case ';', '.', ',', ':', '~':
|
||
return true
|
||
// Chinese Punctuations.
|
||
case ';', ',', '。', ':', '?', '!', '…', '、':
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|