gstr.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. // Package gstr provides functions for string handling.
  7. package gstr
  8. import (
  9. "bytes"
  10. "fmt"
  11. "math"
  12. "strconv"
  13. "strings"
  14. "unicode"
  15. "unicode/utf8"
  16. "github.com/gogf/gf/internal/utils"
  17. "github.com/gogf/gf/util/gconv"
  18. "github.com/gogf/gf/util/grand"
  19. )
  20. const (
  21. // NotFoundIndex is the position index for string not found in searching functions.
  22. NotFoundIndex = -1
  23. )
  24. const (
  25. defaultSuffixForStrLimit = "..."
  26. )
  27. // Count counts the number of `substr` appears in `s`.
  28. // It returns 0 if no `substr` found in `s`.
  29. func Count(s, substr string) int {
  30. return strings.Count(s, substr)
  31. }
  32. // CountI counts the number of `substr` appears in `s`, case-insensitively.
  33. // It returns 0 if no `substr` found in `s`.
  34. func CountI(s, substr string) int {
  35. return strings.Count(ToLower(s), ToLower(substr))
  36. }
  37. // ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
  38. func ToLower(s string) string {
  39. return strings.ToLower(s)
  40. }
  41. // ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.
  42. func ToUpper(s string) string {
  43. return strings.ToUpper(s)
  44. }
  45. // UcFirst returns a copy of the string s with the first letter mapped to its upper case.
  46. func UcFirst(s string) string {
  47. return utils.UcFirst(s)
  48. }
  49. // LcFirst returns a copy of the string s with the first letter mapped to its lower case.
  50. func LcFirst(s string) string {
  51. if len(s) == 0 {
  52. return s
  53. }
  54. if IsLetterUpper(s[0]) {
  55. return string(s[0]+32) + s[1:]
  56. }
  57. return s
  58. }
  59. // UcWords uppercase the first character of each word in a string.
  60. func UcWords(str string) string {
  61. return strings.Title(str)
  62. }
  63. // IsLetterLower tests whether the given byte b is in lower case.
  64. func IsLetterLower(b byte) bool {
  65. return utils.IsLetterLower(b)
  66. }
  67. // IsLetterUpper tests whether the given byte b is in upper case.
  68. func IsLetterUpper(b byte) bool {
  69. return utils.IsLetterUpper(b)
  70. }
  71. // IsNumeric tests whether the given string s is numeric.
  72. func IsNumeric(s string) bool {
  73. return utils.IsNumeric(s)
  74. }
  75. // Reverse returns a string which is the reverse of `str`.
  76. func Reverse(str string) string {
  77. runes := []rune(str)
  78. for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
  79. runes[i], runes[j] = runes[j], runes[i]
  80. }
  81. return string(runes)
  82. }
  83. // NumberFormat formats a number with grouped thousands.
  84. // `decimals`: Sets the number of decimal points.
  85. // `decPoint`: Sets the separator for the decimal point.
  86. // `thousandsSep`: Sets the thousands' separator.
  87. // See http://php.net/manual/en/function.number-format.php.
  88. func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) string {
  89. neg := false
  90. if number < 0 {
  91. number = -number
  92. neg = true
  93. }
  94. // Will round off
  95. str := fmt.Sprintf("%."+strconv.Itoa(decimals)+"F", number)
  96. prefix, suffix := "", ""
  97. if decimals > 0 {
  98. prefix = str[:len(str)-(decimals+1)]
  99. suffix = str[len(str)-decimals:]
  100. } else {
  101. prefix = str
  102. }
  103. sep := []byte(thousandsSep)
  104. n, l1, l2 := 0, len(prefix), len(sep)
  105. // thousands sep num
  106. c := (l1 - 1) / 3
  107. tmp := make([]byte, l2*c+l1)
  108. pos := len(tmp) - 1
  109. for i := l1 - 1; i >= 0; i, n, pos = i-1, n+1, pos-1 {
  110. if l2 > 0 && n > 0 && n%3 == 0 {
  111. for j := range sep {
  112. tmp[pos] = sep[l2-j-1]
  113. pos--
  114. }
  115. }
  116. tmp[pos] = prefix[i]
  117. }
  118. s := string(tmp)
  119. if decimals > 0 {
  120. s += decPoint + suffix
  121. }
  122. if neg {
  123. s = "-" + s
  124. }
  125. return s
  126. }
  127. // ChunkSplit splits a string into smaller chunks.
  128. // Can be used to split a string into smaller chunks which is useful for
  129. // e.g. converting BASE64 string output to match RFC 2045 semantics.
  130. // It inserts end every chunkLen characters.
  131. // It considers parameter `body` and `end` as unicode string.
  132. func ChunkSplit(body string, chunkLen int, end string) string {
  133. if end == "" {
  134. end = "\r\n"
  135. }
  136. runes, endRunes := []rune(body), []rune(end)
  137. l := len(runes)
  138. if l <= 1 || l < chunkLen {
  139. return body + end
  140. }
  141. ns := make([]rune, 0, len(runes)+len(endRunes))
  142. for i := 0; i < l; i += chunkLen {
  143. if i+chunkLen > l {
  144. ns = append(ns, runes[i:]...)
  145. } else {
  146. ns = append(ns, runes[i:i+chunkLen]...)
  147. }
  148. ns = append(ns, endRunes...)
  149. }
  150. return string(ns)
  151. }
  152. // Compare returns an integer comparing two strings lexicographically.
  153. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
  154. func Compare(a, b string) int {
  155. return strings.Compare(a, b)
  156. }
  157. // Equal reports whether `a` and `b`, interpreted as UTF-8 strings,
  158. // are equal under Unicode case-folding, case-insensitively.
  159. func Equal(a, b string) bool {
  160. return strings.EqualFold(a, b)
  161. }
  162. // Fields returns the words used in a string as slice.
  163. func Fields(str string) []string {
  164. return strings.Fields(str)
  165. }
  166. // HasPrefix tests whether the string s begins with prefix.
  167. func HasPrefix(s, prefix string) bool {
  168. return strings.HasPrefix(s, prefix)
  169. }
  170. // HasSuffix tests whether the string s ends with suffix.
  171. func HasSuffix(s, suffix string) bool {
  172. return strings.HasSuffix(s, suffix)
  173. }
  174. // CountWords returns information about words' count used in a string.
  175. // It considers parameter `str` as unicode string.
  176. func CountWords(str string) map[string]int {
  177. m := make(map[string]int)
  178. buffer := bytes.NewBuffer(nil)
  179. for _, r := range []rune(str) {
  180. if unicode.IsSpace(r) {
  181. if buffer.Len() > 0 {
  182. m[buffer.String()]++
  183. buffer.Reset()
  184. }
  185. } else {
  186. buffer.WriteRune(r)
  187. }
  188. }
  189. if buffer.Len() > 0 {
  190. m[buffer.String()]++
  191. }
  192. return m
  193. }
  194. // CountChars returns information about chars' count used in a string.
  195. // It considers parameter `str` as unicode string.
  196. func CountChars(str string, noSpace ...bool) map[string]int {
  197. m := make(map[string]int)
  198. countSpace := true
  199. if len(noSpace) > 0 && noSpace[0] {
  200. countSpace = false
  201. }
  202. for _, r := range []rune(str) {
  203. if !countSpace && unicode.IsSpace(r) {
  204. continue
  205. }
  206. m[string(r)]++
  207. }
  208. return m
  209. }
  210. // WordWrap wraps a string to a given number of characters.
  211. // TODO: Enable cut parameter, see http://php.net/manual/en/function.wordwrap.php.
  212. func WordWrap(str string, width int, br string) string {
  213. if br == "" {
  214. br = "\n"
  215. }
  216. var (
  217. current int
  218. wordBuf, spaceBuf bytes.Buffer
  219. init = make([]byte, 0, len(str))
  220. buf = bytes.NewBuffer(init)
  221. )
  222. for _, char := range []rune(str) {
  223. if char == '\n' {
  224. if wordBuf.Len() == 0 {
  225. if current+spaceBuf.Len() > width {
  226. current = 0
  227. } else {
  228. current += spaceBuf.Len()
  229. spaceBuf.WriteTo(buf)
  230. }
  231. spaceBuf.Reset()
  232. } else {
  233. current += spaceBuf.Len() + wordBuf.Len()
  234. spaceBuf.WriteTo(buf)
  235. spaceBuf.Reset()
  236. wordBuf.WriteTo(buf)
  237. wordBuf.Reset()
  238. }
  239. buf.WriteRune(char)
  240. current = 0
  241. } else if unicode.IsSpace(char) {
  242. if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
  243. current += spaceBuf.Len() + wordBuf.Len()
  244. spaceBuf.WriteTo(buf)
  245. spaceBuf.Reset()
  246. wordBuf.WriteTo(buf)
  247. wordBuf.Reset()
  248. }
  249. spaceBuf.WriteRune(char)
  250. } else {
  251. wordBuf.WriteRune(char)
  252. if current+spaceBuf.Len()+wordBuf.Len() > width && wordBuf.Len() < width {
  253. buf.WriteString(br)
  254. current = 0
  255. spaceBuf.Reset()
  256. }
  257. }
  258. }
  259. if wordBuf.Len() == 0 {
  260. if current+spaceBuf.Len() <= width {
  261. spaceBuf.WriteTo(buf)
  262. }
  263. } else {
  264. spaceBuf.WriteTo(buf)
  265. wordBuf.WriteTo(buf)
  266. }
  267. return buf.String()
  268. }
  269. // RuneLen returns string length of unicode.
  270. // Deprecated, use LenRune instead.
  271. func RuneLen(str string) int {
  272. return LenRune(str)
  273. }
  274. // LenRune returns string length of unicode.
  275. func LenRune(str string) int {
  276. return utf8.RuneCountInString(str)
  277. }
  278. // Repeat returns a new string consisting of multiplier copies of the string input.
  279. func Repeat(input string, multiplier int) string {
  280. return strings.Repeat(input, multiplier)
  281. }
  282. // Shuffle randomly shuffles a string.
  283. // It considers parameter `str` as unicode string.
  284. func Shuffle(str string) string {
  285. runes := []rune(str)
  286. s := make([]rune, len(runes))
  287. for i, v := range grand.Perm(len(runes)) {
  288. s[i] = runes[v]
  289. }
  290. return string(s)
  291. }
  292. // Split splits string `str` by a string `delimiter`, to an array.
  293. func Split(str, delimiter string) []string {
  294. return strings.Split(str, delimiter)
  295. }
  296. // SplitAndTrim splits string `str` by a string `delimiter` to an array,
  297. // and calls Trim to every element of this array. It ignores the elements
  298. // which are empty after Trim.
  299. func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
  300. array := make([]string, 0)
  301. for _, v := range strings.Split(str, delimiter) {
  302. v = Trim(v, characterMask...)
  303. if v != "" {
  304. array = append(array, v)
  305. }
  306. }
  307. return array
  308. }
  309. // SplitAndTrimSpace splits string `str` by a string `delimiter` to an array,
  310. // and calls TrimSpace to every element of this array.
  311. // Deprecated, use SplitAndTrim instead.
  312. func SplitAndTrimSpace(str, delimiter string) []string {
  313. array := make([]string, 0)
  314. for _, v := range strings.Split(str, delimiter) {
  315. v = strings.TrimSpace(v)
  316. if v != "" {
  317. array = append(array, v)
  318. }
  319. }
  320. return array
  321. }
  322. // Join concatenates the elements of `array` to create a single string. The separator string
  323. // `sep` is placed between elements in the resulting string.
  324. func Join(array []string, sep string) string {
  325. return strings.Join(array, sep)
  326. }
  327. // JoinAny concatenates the elements of `array` to create a single string. The separator string
  328. // `sep` is placed between elements in the resulting string.
  329. //
  330. // The parameter `array` can be any type of slice, which be converted to string array.
  331. func JoinAny(array interface{}, sep string) string {
  332. return strings.Join(gconv.Strings(array), sep)
  333. }
  334. // Explode splits string `str` by a string `delimiter`, to an array.
  335. // See http://php.net/manual/en/function.explode.php.
  336. func Explode(delimiter, str string) []string {
  337. return Split(str, delimiter)
  338. }
  339. // Implode joins array elements `pieces` with a string `glue`.
  340. // http://php.net/manual/en/function.implode.php
  341. func Implode(glue string, pieces []string) string {
  342. return strings.Join(pieces, glue)
  343. }
  344. // Chr return the ascii string of a number(0-255).
  345. func Chr(ascii int) string {
  346. return string([]byte{byte(ascii % 256)})
  347. }
  348. // Ord converts the first byte of a string to a value between 0 and 255.
  349. func Ord(char string) int {
  350. return int(char[0])
  351. }
  352. // HideStr replaces part of the string `str` to `hide` by `percentage` from the `middle`.
  353. // It considers parameter `str` as unicode string.
  354. func HideStr(str string, percent int, hide string) string {
  355. array := strings.Split(str, "@")
  356. if len(array) > 1 {
  357. str = array[0]
  358. }
  359. var (
  360. rs = []rune(str)
  361. length = len(rs)
  362. mid = math.Floor(float64(length / 2))
  363. hideLen = int(math.Floor(float64(length) * (float64(percent) / 100)))
  364. start = int(mid - math.Floor(float64(hideLen)/2))
  365. hideStr = []rune("")
  366. hideRune = []rune(hide)
  367. )
  368. for i := 0; i < hideLen; i++ {
  369. hideStr = append(hideStr, hideRune...)
  370. }
  371. buffer := bytes.NewBuffer(nil)
  372. buffer.WriteString(string(rs[0:start]))
  373. buffer.WriteString(string(hideStr))
  374. buffer.WriteString(string(rs[start+hideLen:]))
  375. if len(array) > 1 {
  376. buffer.WriteString("@" + array[1])
  377. }
  378. return buffer.String()
  379. }
  380. // Nl2Br inserts HTML line breaks(<br>|<br />) before all newlines in a string:
  381. // \n\r, \r\n, \r, \n.
  382. // It considers parameter `str` as unicode string.
  383. func Nl2Br(str string, isXhtml ...bool) string {
  384. r, n, runes := '\r', '\n', []rune(str)
  385. var br []byte
  386. if len(isXhtml) > 0 && isXhtml[0] {
  387. br = []byte("<br />")
  388. } else {
  389. br = []byte("<br>")
  390. }
  391. skip := false
  392. length := len(runes)
  393. var buf bytes.Buffer
  394. for i, v := range runes {
  395. if skip {
  396. skip = false
  397. continue
  398. }
  399. switch v {
  400. case n, r:
  401. if (i+1 < length) && (v == r && runes[i+1] == n) || (v == n && runes[i+1] == r) {
  402. buf.Write(br)
  403. skip = true
  404. continue
  405. }
  406. buf.Write(br)
  407. default:
  408. buf.WriteRune(v)
  409. }
  410. }
  411. return buf.String()
  412. }
  413. // AddSlashes quotes chars('"\) with slashes.
  414. func AddSlashes(str string) string {
  415. var buf bytes.Buffer
  416. for _, char := range str {
  417. switch char {
  418. case '\'', '"', '\\':
  419. buf.WriteRune('\\')
  420. }
  421. buf.WriteRune(char)
  422. }
  423. return buf.String()
  424. }
  425. // StripSlashes un-quotes a quoted string by AddSlashes.
  426. func StripSlashes(str string) string {
  427. var buf bytes.Buffer
  428. l, skip := len(str), false
  429. for i, char := range str {
  430. if skip {
  431. skip = false
  432. } else if char == '\\' {
  433. if i+1 < l && str[i+1] == '\\' {
  434. skip = true
  435. }
  436. continue
  437. }
  438. buf.WriteRune(char)
  439. }
  440. return buf.String()
  441. }
  442. // QuoteMeta returns a version of str with a backslash character (\)
  443. // before every character that is among: .\+*?[^]($)
  444. func QuoteMeta(str string, chars ...string) string {
  445. var buf bytes.Buffer
  446. for _, char := range str {
  447. if len(chars) > 0 {
  448. for _, c := range chars[0] {
  449. if c == char {
  450. buf.WriteRune('\\')
  451. break
  452. }
  453. }
  454. } else {
  455. switch char {
  456. case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?':
  457. buf.WriteRune('\\')
  458. }
  459. }
  460. buf.WriteRune(char)
  461. }
  462. return buf.String()
  463. }
  464. // SearchArray searches string `s` in string slice `a` case-sensitively,
  465. // returns its index in `a`.
  466. // If `s` is not found in `a`, it returns -1.
  467. func SearchArray(a []string, s string) int {
  468. for i, v := range a {
  469. if s == v {
  470. return i
  471. }
  472. }
  473. return NotFoundIndex
  474. }
  475. // InArray checks whether string `s` in slice `a`.
  476. func InArray(a []string, s string) bool {
  477. return SearchArray(a, s) != NotFoundIndex
  478. }