gf/text/gstr/gstr_convert.go
2021-12-21 23:24:38 +08:00

237 lines
5.6 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"
"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}`)
)
// OctStr converts string container octal string to its original string,
// for example, to Chinese string.
// Eg: `\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`.
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`|<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.
// 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()
}