You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
655 lines
14 KiB
655 lines
14 KiB
package xstring |
|
|
|
import ( |
|
"bytes" |
|
"encoding/base64" |
|
"encoding/json" |
|
"fmt" |
|
"github.com/PuerkitoBio/goquery" |
|
"github.com/pkg/errors" |
|
"reflect" |
|
"regexp" |
|
"sort" |
|
"strconv" |
|
"strings" |
|
"unicode" |
|
) |
|
|
|
func TableFieldNames(obj any) ([]string, string) { |
|
v := reflect.TypeOf(obj) |
|
cols := []string{} |
|
primary := "" |
|
for i := 0; i < v.NumField(); i++ { |
|
cols = append(cols, v.Field(i).Tag.Get("db")) |
|
if primary == "" { |
|
primary = v.Field(i).Tag.Get("primaryKey") |
|
} |
|
} |
|
return cols, primary |
|
} |
|
|
|
func Remove[T string](cols []T, key T) []T { |
|
arr := make([]T, 0) |
|
for _, v := range cols { |
|
if key != v { |
|
arr = append(arr, v) |
|
} |
|
} |
|
return arr |
|
} |
|
|
|
func NamedSql(cls []string) string { |
|
str := []string{} |
|
for _, v := range cls { |
|
str = append(str, fmt.Sprintf("%s=:%s", v, v)) |
|
} |
|
return strings.Join(str, ",") |
|
} |
|
|
|
func StripTags(content string) string { |
|
re := regexp.MustCompile(`<(.|\n)*?>`) |
|
return re.ReplaceAllString(content, "") |
|
} |
|
func SubString(str string, begin, length int) string { |
|
rs := []rune(str) |
|
lth := len(rs) |
|
if begin < 0 { |
|
begin = 0 |
|
} |
|
if begin >= lth { |
|
begin = lth |
|
} |
|
end := begin + length |
|
|
|
if end > lth { |
|
end = lth |
|
} |
|
return string(rs[begin:end]) |
|
} |
|
func Nl2br(str string) string { |
|
return strings.Join(strings.Split(strings.Join(strings.Split(str, "\r"), "<br/>"), "\n"), "<br/>") |
|
} |
|
|
|
func Serialize(value interface{}) ([]byte, error) { |
|
|
|
if value == nil { |
|
return MarshalNil(), nil |
|
} |
|
|
|
t := reflect.TypeOf(value) |
|
switch t.Kind() { |
|
case reflect.Bool: |
|
return MarshalBool(value.(bool)), nil |
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, |
|
reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, |
|
reflect.Float32, reflect.Float64: |
|
return MarshalNumber(value), nil |
|
case reflect.String: |
|
return MarshalString(value.(string)), nil |
|
case reflect.Map: |
|
return MarshalMap(value) |
|
case reflect.Slice: |
|
return MarshalSlice(value) |
|
default: |
|
return nil, fmt.Errorf("Marshal: Unknown type %T with value %#v", t, value) |
|
} |
|
} |
|
|
|
func MarshalNil() []byte { |
|
return []byte("N;") |
|
} |
|
|
|
func MarshalBool(value bool) []byte { |
|
if value { |
|
return []byte("b:1;") |
|
} |
|
|
|
return []byte("b:0;") |
|
} |
|
|
|
func MarshalNumber(value interface{}) []byte { |
|
var val string |
|
|
|
isFloat := false |
|
|
|
switch value.(type) { |
|
default: |
|
val = "0" |
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: |
|
val, _ = NumericalToString(value) |
|
case float32, float64: |
|
val, _ = NumericalToString(value) |
|
isFloat = true |
|
} |
|
|
|
if isFloat { |
|
return []byte("d:" + val + ";") |
|
|
|
} else { |
|
return []byte("i:" + val + ";") |
|
} |
|
} |
|
func NumericalValue(value reflect.Value) (float64, bool) { |
|
switch value.Type().Kind() { |
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
|
return float64(value.Int()), true |
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
|
return float64(value.Uint()), true |
|
|
|
case reflect.Float32, reflect.Float64: |
|
return value.Float(), true |
|
|
|
default: |
|
return 0, false |
|
} |
|
} |
|
func MarshalString(value string) []byte { |
|
return []byte(fmt.Sprintf("s:%d:\"%s\";", len(value), value)) |
|
} |
|
func LessValue(a, b reflect.Value) bool { |
|
aValue, aNumerical := NumericalValue(a) |
|
bValue, bNumerical := NumericalValue(b) |
|
|
|
if aNumerical && bNumerical { |
|
return aValue < bValue |
|
} |
|
|
|
if !aNumerical && !bNumerical { |
|
// In theory this should mean they are both strings. In reality |
|
// they could be any other type and the String() representation |
|
// will be something like "<bool>" if it is not a string. Since |
|
// distinct values of non-strings still return the same value |
|
// here that's what makes the ordering undefined. |
|
return strings.Compare(a.String(), b.String()) < 0 |
|
} |
|
|
|
// Numerical values are always treated as less than other types |
|
// (including strings that might represent numbers themselves). The |
|
// inverse is also true. |
|
return aNumerical && !bNumerical |
|
} |
|
|
|
func MarshalMap(value interface{}) ([]byte, error) { |
|
|
|
s := reflect.ValueOf(value) |
|
|
|
mapKeys := s.MapKeys() |
|
sort.Slice(mapKeys, func(i, j int) bool { |
|
return LessValue(mapKeys[i], mapKeys[j]) |
|
}) |
|
|
|
var buffer bytes.Buffer |
|
for _, mapKey := range mapKeys { |
|
m, err := Serialize(mapKey.Interface()) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
buffer.Write(m) |
|
|
|
m, err = Serialize(s.MapIndex(mapKey).Interface()) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
buffer.Write(m) |
|
} |
|
|
|
return []byte(fmt.Sprintf("a:%d:{%s}", s.Len(), buffer.String())), nil |
|
} |
|
|
|
func MarshalSlice(value interface{}) ([]byte, error) { |
|
s := reflect.ValueOf(value) |
|
|
|
var buffer bytes.Buffer |
|
for i := 0; i < s.Len(); i++ { |
|
m, err := Serialize(i) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
buffer.Write(m) |
|
|
|
m, err = Serialize(s.Index(i).Interface()) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
buffer.Write(m) |
|
} |
|
|
|
return []byte(fmt.Sprintf("a:%d:{%s}", s.Len(), buffer.String())), nil |
|
} |
|
|
|
const UNSERIALIZABLE_OBJECT_MAX_LEN = int64(10 * 1024 * 1024 * 1024) |
|
|
|
func UnSerialize(data []byte) (interface{}, error) { |
|
reader := bytes.NewReader(data) |
|
return unMarshalByReader(reader) |
|
} |
|
|
|
func UnSerializeBind(data []byte, post any) error { |
|
reader := bytes.NewReader(data) |
|
b, err := unMarshalByReader(reader) |
|
if err != nil { |
|
return err |
|
} |
|
bb, err := json.Marshal(b) |
|
if err != nil { |
|
return err |
|
} |
|
return json.Unmarshal(bb, post) |
|
} |
|
|
|
func unMarshalByReader(reader *bytes.Reader) (interface{}, error) { |
|
|
|
for { |
|
|
|
if token, _, err := reader.ReadRune(); err == nil { |
|
switch token { |
|
default: |
|
return nil, fmt.Errorf("UnMarshal: Unknown token %#U", token) |
|
case 'N': |
|
return unMarshalNil(reader) |
|
case 'b': |
|
return unMarshalBool(reader) |
|
case 'i': |
|
return unMarshalNumber(reader, false) |
|
case 'd': |
|
return unMarshalNumber(reader, true) |
|
case 's': |
|
return unMarshalString(reader, true) |
|
case 'a': |
|
return unMarshalArray(reader) |
|
// case 'O': |
|
|
|
// case 'C': |
|
|
|
// case 'R', 'r': |
|
|
|
// case 'x': |
|
|
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
func unMarshalNil(reader *bytes.Reader) (interface{}, error) { |
|
_ = expect(reader, ';') |
|
|
|
return nil, nil |
|
} |
|
|
|
func unMarshalBool(reader *bytes.Reader) (interface{}, error) { |
|
var ( |
|
raw rune |
|
err error |
|
) |
|
err = expect(reader, ':') |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if raw, _, err = reader.ReadRune(); err != nil { |
|
return nil, fmt.Errorf("UnMarshal: Error while reading bool value: %v", err) |
|
} |
|
|
|
err = expect(reader, ';') |
|
if err != nil { |
|
return nil, err |
|
} |
|
return raw == '1', nil |
|
} |
|
|
|
func unMarshalNumber(reader *bytes.Reader, isFloat bool) (interface{}, error) { |
|
var ( |
|
raw string |
|
err error |
|
val interface{} |
|
) |
|
err = expect(reader, ':') |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if raw, err = readUntil(reader, ';'); err != nil { |
|
return nil, fmt.Errorf("UnMarshal: Error while reading number value: %v", err) |
|
} else { |
|
if isFloat { |
|
if val, err = strconv.ParseFloat(raw, 64); err != nil { |
|
return nil, fmt.Errorf("UnMarshal: Unable to convert %s to float: %v", raw, err) |
|
} |
|
} else { |
|
if val, err = strconv.Atoi(raw); err != nil { |
|
return nil, fmt.Errorf("UnMarshal: Unable to convert %s to int: %v", raw, err) |
|
} |
|
} |
|
} |
|
|
|
return val, nil |
|
} |
|
|
|
func unMarshalString(reader *bytes.Reader, isFinal bool) (interface{}, error) { |
|
var ( |
|
err error |
|
val interface{} |
|
strLen int |
|
readLen int |
|
) |
|
|
|
strLen, err = readLength(reader) |
|
if err != nil { |
|
return nil, err |
|
} |
|
err = expect(reader, '"') |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if strLen > 0 { |
|
buf := make([]byte, strLen) |
|
if readLen, err = reader.Read(buf); err != nil { |
|
return nil, fmt.Errorf("UnMarshal: Error while reading string value: %v", err) |
|
} else { |
|
if readLen != strLen { |
|
return nil, fmt.Errorf("UnMarshal: Unable to read string. Expected %d but have got %d bytes", strLen, readLen) |
|
} else { |
|
val = string(buf) |
|
} |
|
} |
|
} |
|
|
|
err = expect(reader, '"') |
|
if err != nil { |
|
return nil, err |
|
} |
|
if isFinal { |
|
err = expect(reader, ';') |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
return val, nil |
|
} |
|
|
|
func unMarshalArray(reader *bytes.Reader) (interface{}, error) { |
|
var arrLen int |
|
var err error |
|
val := make(map[string]interface{}) |
|
|
|
arrLen, err = readLength(reader) |
|
|
|
if err != nil { |
|
return nil, err |
|
} |
|
err = expect(reader, '{') |
|
if err != nil { |
|
return nil, err |
|
} |
|
indexLen := 0 |
|
for i := 0; i < arrLen; i++ { |
|
k, err := unMarshalByReader(reader) |
|
if err != nil { |
|
return nil, err |
|
} |
|
v, err := unMarshalByReader(reader) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// if errKey == nil && errVal == nil { |
|
// val[k] = v |
|
switch t := k.(type) { |
|
default: |
|
return nil, fmt.Errorf("UnMarshal: Unexpected key type %T", t) |
|
case string: |
|
stringKey, _ := k.(string) |
|
val[stringKey] = v |
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: |
|
// intKey, _ := k.(int) |
|
// val[strconv.Itoa(intKey)] = v |
|
stringKey, _ := NumericalToString(k) |
|
val[stringKey] = v |
|
|
|
// stringI, _ := utils.NumericalToString(i) |
|
if i == k { |
|
indexLen++ |
|
} |
|
|
|
} |
|
// } else { |
|
// return nil, fmt.Errorf("UnMarshal: Error while reading key or(and) value of array") |
|
// } |
|
} |
|
|
|
err = expect(reader, '}') |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if indexLen == arrLen { |
|
var slice []interface{} |
|
for _, row := range val { |
|
slice = append(slice, row) |
|
} |
|
return slice, nil |
|
} |
|
|
|
return val, nil |
|
} |
|
|
|
func expect(reader *bytes.Reader, expected rune) error { |
|
if token, _, err := reader.ReadRune(); err != nil { |
|
return fmt.Errorf("UnMarshal: Error while reading expected rune %#U: %v", expected, err) |
|
} else if token != expected { |
|
return fmt.Errorf("UnMarshal: Expected %#U but have got %#U", expected, token) |
|
} |
|
return nil |
|
} |
|
|
|
func readUntil(reader *bytes.Reader, stop rune) (string, error) { |
|
var ( |
|
token rune |
|
err error |
|
) |
|
buf := bytes.NewBuffer([]byte{}) |
|
|
|
for { |
|
if token, _, err = reader.ReadRune(); err != nil || token == stop { |
|
break |
|
} else { |
|
buf.WriteRune(token) |
|
} |
|
} |
|
|
|
return buf.String(), err |
|
} |
|
|
|
func readLength(reader *bytes.Reader) (int, error) { |
|
var ( |
|
raw string |
|
err error |
|
val int |
|
) |
|
err = expect(reader, ':') |
|
if err != nil { |
|
return 0, err |
|
} |
|
|
|
if raw, err = readUntil(reader, ':'); err != nil { |
|
return 0, fmt.Errorf("UnMarshal: Error while reading lenght of value: %v", err) |
|
} else { |
|
if val, err = strconv.Atoi(raw); err != nil { |
|
return 0, fmt.Errorf("UnMarshal: Unable to convert %s to int: %v", raw, err) |
|
} else if int64(val) > UNSERIALIZABLE_OBJECT_MAX_LEN { |
|
return 0, fmt.Errorf("UnMarshal: Unserializable object length looks too big(%d). If you are sure you wanna unserialise it, please increase UNSERIALIZABLE_OBJECT_MAX_LEN const", val) |
|
} |
|
} |
|
return val, nil |
|
} |
|
|
|
func NumericalToString(value interface{}) (string, bool) { |
|
var val string |
|
|
|
switch intVal := value.(type) { |
|
default: |
|
return "0", false |
|
case int: |
|
val = strconv.FormatInt(int64(intVal), 10) |
|
case int8: |
|
val = strconv.FormatInt(int64(intVal), 10) |
|
case int16: |
|
val = strconv.FormatInt(int64(intVal), 10) |
|
case int32: |
|
val = strconv.FormatInt(int64(intVal), 10) |
|
case int64: |
|
val = strconv.FormatInt(int64(intVal), 10) |
|
case uint: |
|
val = strconv.FormatUint(uint64(intVal), 10) |
|
case uint8: |
|
val = strconv.FormatUint(uint64(intVal), 10) |
|
case uint16: |
|
val = strconv.FormatUint(uint64(intVal), 10) |
|
case uint32: |
|
val = strconv.FormatUint(uint64(intVal), 10) |
|
case uint64: |
|
val = strconv.FormatUint(intVal, 10) |
|
case float32: |
|
val = strconv.FormatFloat(float64(intVal), 'f', -1, 32) |
|
case float64: |
|
val = strconv.FormatFloat(intVal, 'f', -1, 64) |
|
} |
|
return val, true |
|
} |
|
|
|
//func StrToArr(str string) []string { |
|
// re := regexp.MustCompile(`<(.|\n)*?>`) |
|
// str = re.ReplaceAllString(str, " ") |
|
// str = strings.ReplaceAll(str, `"`, "") |
|
// str = strings.ReplaceAll(str, `'`, "") |
|
// str = strings.ReplaceAll(str, "`", "") |
|
// str = strings.ReplaceAll(str, ",", "") |
|
// str = strings.ReplaceAll(str, " ", "") |
|
// newStr := []string{} |
|
// tmp := "" |
|
// for i := 0; i < len(str); { |
|
// r, n := utf8.DecodeRuneInString(str[i:]) |
|
// if n == 3 { |
|
// tmp = "" |
|
// newStr = append(newStr, string(r)) |
|
// } else { |
|
// |
|
// if string(r) == " " { |
|
// newStr = append(newStr, tmp) |
|
// tmp = "" |
|
// } else if string(r) != "\n" { |
|
// tmp = fmt.Sprintf("%s%c", tmp, r) |
|
// } |
|
// } |
|
// i += n |
|
// } |
|
// if tmp != "" { |
|
// newStr = append(newStr, tmp) |
|
// tmp = "" |
|
// } |
|
// newStr1 := make([]string, 0) |
|
// for _, v := range newStr { |
|
// if strings.TrimSpace(v) == "" { |
|
// continue |
|
// } |
|
// newStr1 = append(newStr1, strings.TrimSpace(v)) |
|
// } |
|
// return newStr1 |
|
//} |
|
|
|
//func StrToUrl(str string) string { |
|
// strs := []string{} |
|
// for _, v := range StrToArr(str) { |
|
// fmt.Println(v) |
|
// } |
|
// |
|
// return strings.Join(strs, "-") |
|
//} |
|
|
|
func Base64Encode(s string) string { |
|
return base64.StdEncoding.EncodeToString([]byte(s)) |
|
} |
|
|
|
func Base64Decode(s string) (string, error) { |
|
b, err := base64.StdEncoding.DecodeString(s) |
|
if err != nil { |
|
return "", errors.Wrap(err, "") |
|
} |
|
return string(b), nil |
|
} |
|
|
|
func HtmlToText(body string) ([]string, error) { |
|
body = strings.ReplaceAll(body, " ", " ") |
|
ss := "<(.|\\n)*?>" |
|
body = regexp.MustCompile(ss).ReplaceAllString(body, " ") |
|
doc, err := goquery.NewDocumentFromReader(strings.NewReader(body)) |
|
if err != nil { |
|
return nil, errors.Wrap(err, "") |
|
} |
|
defer doc.Clone() |
|
v := doc.Text() |
|
var hzRegexp = regexp.MustCompile("^[a-zA-Z0-9\u4e00-\u9fa5]$") |
|
var zwReg = regexp.MustCompile("^[\u4e00-\u9fa5]$") |
|
strn := "" |
|
ars := []string{} |
|
for _, c := range v { |
|
filter := false |
|
if hzRegexp.MatchString(string(c)) { |
|
if zwReg.MatchString(string(c)) { |
|
strn += string(c) |
|
filter = true |
|
} else { |
|
strn += string(c) |
|
} |
|
} else { |
|
filter = true |
|
} |
|
fmt.Println(strn) |
|
if filter && strn != "" { |
|
ars = append(ars, strn) |
|
strn = "" |
|
} |
|
} |
|
if strn != "" { |
|
ars = append(ars, strn) |
|
} |
|
return ars, nil |
|
} |
|
func ContentText(body string) string { |
|
doc, err := goquery.NewDocumentFromReader(strings.NewReader(body)) |
|
if err != nil { |
|
return err.Error() |
|
} |
|
defer doc.Clone() |
|
return doc.Text() |
|
} |
|
func GetPicture(pic string) string { |
|
if pic == "" { |
|
return "/sites/default/files/imagecache/ic50x50/avatar_selection/o2QQW.jpg.png" |
|
} |
|
if !strings.HasPrefix(pic, "/") { |
|
return "/" + pic |
|
} |
|
return pic |
|
} |
|
|
|
func Ucfirst(str string) string { |
|
for i, v := range str { |
|
return string(unicode.ToUpper(v)) + str[i+1:] |
|
} |
|
return "" |
|
} |
|
|
|
func Lcfirst(str string) string { |
|
for i, v := range str { |
|
return string(unicode.ToLower(v)) + str[i+1:] |
|
} |
|
return "" |
|
}
|
|
|