gstr.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. // Copyright 2018 gf Author(https://github.com/gogf/gf). 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. // Replace returns a copy of the string <origin>
  21. // in which string <search> replaced by <replace> case-sensitively.
  22. func Replace(origin, search, replace string, count ...int) string {
  23. n := -1
  24. if len(count) > 0 {
  25. n = count[0]
  26. }
  27. return strings.Replace(origin, search, replace, n)
  28. }
  29. // Replace returns a copy of the string <origin>
  30. // in which string <search> replaced by <replace> case-insensitively.
  31. func ReplaceI(origin, search, replace string, count ...int) string {
  32. n := -1
  33. if len(count) > 0 {
  34. n = count[0]
  35. }
  36. if n == 0 {
  37. return origin
  38. }
  39. length := len(search)
  40. searchLower := strings.ToLower(search)
  41. for {
  42. originLower := strings.ToLower(origin)
  43. if pos := strings.Index(originLower, searchLower); pos != -1 {
  44. origin = origin[:pos] + replace + origin[pos+length:]
  45. if n--; n == 0 {
  46. break
  47. }
  48. } else {
  49. break
  50. }
  51. }
  52. return origin
  53. }
  54. // Count counts the number of <substr> appears in <s>.
  55. // It returns 0 if no <substr> found in <s>.
  56. func Count(s, substr string) int {
  57. return strings.Count(s, substr)
  58. }
  59. // CountI counts the number of <substr> appears in <s>, case-insensitively.
  60. // It returns 0 if no <substr> found in <s>.
  61. func CountI(s, substr string) int {
  62. return strings.Count(ToLower(s), ToLower(substr))
  63. }
  64. // ReplaceByArray returns a copy of <origin>,
  65. // which is replaced by a slice in order, case-sensitively.
  66. func ReplaceByArray(origin string, array []string) string {
  67. for i := 0; i < len(array); i += 2 {
  68. if i+1 >= len(array) {
  69. break
  70. }
  71. origin = Replace(origin, array[i], array[i+1])
  72. }
  73. return origin
  74. }
  75. // ReplaceIByArray returns a copy of <origin>,
  76. // which is replaced by a slice in order, case-insensitively.
  77. func ReplaceIByArray(origin string, array []string) string {
  78. for i := 0; i < len(array); i += 2 {
  79. if i+1 >= len(array) {
  80. break
  81. }
  82. origin = ReplaceI(origin, array[i], array[i+1])
  83. }
  84. return origin
  85. }
  86. // ReplaceByMap returns a copy of <origin>,
  87. // which is replaced by a map in unordered way, case-sensitively.
  88. func ReplaceByMap(origin string, replaces map[string]string) string {
  89. return utils.ReplaceByMap(origin, replaces)
  90. }
  91. // ReplaceIByMap returns a copy of <origin>,
  92. // which is replaced by a map in unordered way, case-insensitively.
  93. func ReplaceIByMap(origin string, replaces map[string]string) string {
  94. for k, v := range replaces {
  95. origin = ReplaceI(origin, k, v)
  96. }
  97. return origin
  98. }
  99. // ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
  100. func ToLower(s string) string {
  101. return strings.ToLower(s)
  102. }
  103. // ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.
  104. func ToUpper(s string) string {
  105. return strings.ToUpper(s)
  106. }
  107. // UcFirst returns a copy of the string s with the first letter mapped to its upper case.
  108. func UcFirst(s string) string {
  109. return utils.UcFirst(s)
  110. }
  111. // LcFirst returns a copy of the string s with the first letter mapped to its lower case.
  112. func LcFirst(s string) string {
  113. if len(s) == 0 {
  114. return s
  115. }
  116. if IsLetterUpper(s[0]) {
  117. return string(s[0]+32) + s[1:]
  118. }
  119. return s
  120. }
  121. // UcWords uppercase the first character of each word in a string.
  122. func UcWords(str string) string {
  123. return strings.Title(str)
  124. }
  125. // IsLetterLower tests whether the given byte b is in lower case.
  126. func IsLetterLower(b byte) bool {
  127. return utils.IsLetterLower(b)
  128. }
  129. // IsLetterUpper tests whether the given byte b is in upper case.
  130. func IsLetterUpper(b byte) bool {
  131. return utils.IsLetterUpper(b)
  132. }
  133. // IsNumeric tests whether the given string s is numeric.
  134. func IsNumeric(s string) bool {
  135. return utils.IsNumeric(s)
  136. }
  137. // SubStr returns a portion of string <str> specified by the <start> and <length> parameters.
  138. func SubStr(str string, start int, length ...int) (substr string) {
  139. lth := len(str)
  140. // Simple border checks.
  141. if start < 0 {
  142. start = 0
  143. }
  144. if start >= lth {
  145. start = lth
  146. }
  147. end := lth
  148. if len(length) > 0 {
  149. end = start + length[0]
  150. if end < start {
  151. end = lth
  152. }
  153. }
  154. if end > lth {
  155. end = lth
  156. }
  157. return str[start:end]
  158. }
  159. // SubStrRune returns a portion of string <str> specified by the <start> and <length> parameters.
  160. // SubStrRune considers parameter <str> as unicode string.
  161. func SubStrRune(str string, start int, length ...int) (substr string) {
  162. // Converting to []rune to support unicode.
  163. rs := []rune(str)
  164. lth := len(rs)
  165. // Simple border checks.
  166. if start < 0 {
  167. start = 0
  168. }
  169. if start >= lth {
  170. start = lth
  171. }
  172. end := lth
  173. if len(length) > 0 {
  174. end = start + length[0]
  175. if end < start {
  176. end = lth
  177. }
  178. }
  179. if end > lth {
  180. end = lth
  181. }
  182. return string(rs[start:end])
  183. }
  184. // StrLimit returns a portion of string <str> specified by <length> parameters, if the length
  185. // of <str> is greater than <length>, then the <suffix> will be appended to the result string.
  186. func StrLimit(str string, length int, suffix ...string) string {
  187. if len(str) < length {
  188. return str
  189. }
  190. addStr := "..."
  191. if len(suffix) > 0 {
  192. addStr = suffix[0]
  193. }
  194. return str[0:length] + addStr
  195. }
  196. // StrLimitRune returns a portion of string <str> specified by <length> parameters, if the length
  197. // of <str> is greater than <length>, then the <suffix> will be appended to the result string.
  198. // StrLimitRune considers parameter <str> as unicode string.
  199. func StrLimitRune(str string, length int, suffix ...string) string {
  200. rs := []rune(str)
  201. if len(rs) < length {
  202. return str
  203. }
  204. addStr := "..."
  205. if len(suffix) > 0 {
  206. addStr = suffix[0]
  207. }
  208. return string(rs[0:length]) + addStr
  209. }
  210. // Reverse returns a string which is the reverse of <str>.
  211. func Reverse(str string) string {
  212. runes := []rune(str)
  213. for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
  214. runes[i], runes[j] = runes[j], runes[i]
  215. }
  216. return string(runes)
  217. }
  218. // NumberFormat formats a number with grouped thousands.
  219. // <decimals>: Sets the number of decimal points.
  220. // <decPoint>: Sets the separator for the decimal point.
  221. // <thousandsSep>: Sets the thousands separator.
  222. // See http://php.net/manual/en/function.number-format.php.
  223. func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) string {
  224. neg := false
  225. if number < 0 {
  226. number = -number
  227. neg = true
  228. }
  229. // Will round off
  230. str := fmt.Sprintf("%."+strconv.Itoa(decimals)+"F", number)
  231. prefix, suffix := "", ""
  232. if decimals > 0 {
  233. prefix = str[:len(str)-(decimals+1)]
  234. suffix = str[len(str)-decimals:]
  235. } else {
  236. prefix = str
  237. }
  238. sep := []byte(thousandsSep)
  239. n, l1, l2 := 0, len(prefix), len(sep)
  240. // thousands sep num
  241. c := (l1 - 1) / 3
  242. tmp := make([]byte, l2*c+l1)
  243. pos := len(tmp) - 1
  244. for i := l1 - 1; i >= 0; i, n, pos = i-1, n+1, pos-1 {
  245. if l2 > 0 && n > 0 && n%3 == 0 {
  246. for j := range sep {
  247. tmp[pos] = sep[l2-j-1]
  248. pos--
  249. }
  250. }
  251. tmp[pos] = prefix[i]
  252. }
  253. s := string(tmp)
  254. if decimals > 0 {
  255. s += decPoint + suffix
  256. }
  257. if neg {
  258. s = "-" + s
  259. }
  260. return s
  261. }
  262. // ChunkSplit splits a string into smaller chunks.
  263. // Can be used to split a string into smaller chunks which is useful for
  264. // e.g. converting BASE64 string output to match RFC 2045 semantics.
  265. // It inserts end every chunkLen characters.
  266. // It considers parameter <body> and <end> as unicode string.
  267. func ChunkSplit(body string, chunkLen int, end string) string {
  268. if end == "" {
  269. end = "\r\n"
  270. }
  271. runes, endRunes := []rune(body), []rune(end)
  272. l := len(runes)
  273. if l <= 1 || l < chunkLen {
  274. return body + end
  275. }
  276. ns := make([]rune, 0, len(runes)+len(endRunes))
  277. for i := 0; i < l; i += chunkLen {
  278. if i+chunkLen > l {
  279. ns = append(ns, runes[i:]...)
  280. } else {
  281. ns = append(ns, runes[i:i+chunkLen]...)
  282. }
  283. ns = append(ns, endRunes...)
  284. }
  285. return string(ns)
  286. }
  287. // Compare returns an integer comparing two strings lexicographically.
  288. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
  289. func Compare(a, b string) int {
  290. return strings.Compare(a, b)
  291. }
  292. // Equal reports whether <a> and <b>, interpreted as UTF-8 strings,
  293. // are equal under Unicode case-folding, case-insensitively.
  294. func Equal(a, b string) bool {
  295. return strings.EqualFold(a, b)
  296. }
  297. // Fields returns the words used in a string as slice.
  298. func Fields(str string) []string {
  299. return strings.Fields(str)
  300. }
  301. // HasPrefix tests whether the string s begins with prefix.
  302. func HasPrefix(s, prefix string) bool {
  303. return strings.HasPrefix(s, prefix)
  304. }
  305. // HasSuffix tests whether the string s ends with suffix.
  306. func HasSuffix(s, suffix string) bool {
  307. return strings.HasSuffix(s, suffix)
  308. }
  309. // CountWords returns information about words' count used in a string.
  310. // It considers parameter <str> as unicode string.
  311. func CountWords(str string) map[string]int {
  312. m := make(map[string]int)
  313. buffer := bytes.NewBuffer(nil)
  314. for _, r := range []rune(str) {
  315. if unicode.IsSpace(r) {
  316. if buffer.Len() > 0 {
  317. m[buffer.String()]++
  318. buffer.Reset()
  319. }
  320. } else {
  321. buffer.WriteRune(r)
  322. }
  323. }
  324. if buffer.Len() > 0 {
  325. m[buffer.String()]++
  326. }
  327. return m
  328. }
  329. // CountChars returns information about chars' count used in a string.
  330. // It considers parameter <str> as unicode string.
  331. func CountChars(str string, noSpace ...bool) map[string]int {
  332. m := make(map[string]int)
  333. countSpace := true
  334. if len(noSpace) > 0 && noSpace[0] {
  335. countSpace = false
  336. }
  337. for _, r := range []rune(str) {
  338. if !countSpace && unicode.IsSpace(r) {
  339. continue
  340. }
  341. m[string(r)]++
  342. }
  343. return m
  344. }
  345. // WordWrap wraps a string to a given number of characters.
  346. // TODO: Enable cut parameter, see http://php.net/manual/en/function.wordwrap.php.
  347. func WordWrap(str string, width int, br string) string {
  348. if br == "" {
  349. br = "\n"
  350. }
  351. var (
  352. current int
  353. wordBuf, spaceBuf bytes.Buffer
  354. init = make([]byte, 0, len(str))
  355. buf = bytes.NewBuffer(init)
  356. )
  357. for _, char := range []rune(str) {
  358. if char == '\n' {
  359. if wordBuf.Len() == 0 {
  360. if current+spaceBuf.Len() > width {
  361. current = 0
  362. } else {
  363. current += spaceBuf.Len()
  364. spaceBuf.WriteTo(buf)
  365. }
  366. spaceBuf.Reset()
  367. } else {
  368. current += spaceBuf.Len() + wordBuf.Len()
  369. spaceBuf.WriteTo(buf)
  370. spaceBuf.Reset()
  371. wordBuf.WriteTo(buf)
  372. wordBuf.Reset()
  373. }
  374. buf.WriteRune(char)
  375. current = 0
  376. } else if unicode.IsSpace(char) {
  377. if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
  378. current += spaceBuf.Len() + wordBuf.Len()
  379. spaceBuf.WriteTo(buf)
  380. spaceBuf.Reset()
  381. wordBuf.WriteTo(buf)
  382. wordBuf.Reset()
  383. }
  384. spaceBuf.WriteRune(char)
  385. } else {
  386. wordBuf.WriteRune(char)
  387. if current+spaceBuf.Len()+wordBuf.Len() > width && wordBuf.Len() < width {
  388. buf.WriteString(br)
  389. current = 0
  390. spaceBuf.Reset()
  391. }
  392. }
  393. }
  394. if wordBuf.Len() == 0 {
  395. if current+spaceBuf.Len() <= width {
  396. spaceBuf.WriteTo(buf)
  397. }
  398. } else {
  399. spaceBuf.WriteTo(buf)
  400. wordBuf.WriteTo(buf)
  401. }
  402. return buf.String()
  403. }
  404. // RuneLen returns string length of unicode.
  405. // Deprecated, use LenRune instead.
  406. func RuneLen(str string) int {
  407. return LenRune(str)
  408. }
  409. // LenRune returns string length of unicode.
  410. func LenRune(str string) int {
  411. return utf8.RuneCountInString(str)
  412. }
  413. // Repeat returns a new string consisting of multiplier copies of the string input.
  414. func Repeat(input string, multiplier int) string {
  415. return strings.Repeat(input, multiplier)
  416. }
  417. // Str returns part of <haystack> string starting from and including
  418. // the first occurrence of <needle> to the end of <haystack>.
  419. // See http://php.net/manual/en/function.strstr.php.
  420. func Str(haystack string, needle string) string {
  421. if needle == "" {
  422. return ""
  423. }
  424. idx := strings.Index(haystack, needle)
  425. if idx == -1 {
  426. return ""
  427. }
  428. return haystack[idx+len([]byte(needle))-1:]
  429. }
  430. // Shuffle randomly shuffles a string.
  431. // It considers parameter <str> as unicode string.
  432. func Shuffle(str string) string {
  433. runes := []rune(str)
  434. s := make([]rune, len(runes))
  435. for i, v := range grand.Perm(len(runes)) {
  436. s[i] = runes[v]
  437. }
  438. return string(s)
  439. }
  440. // Split splits string <str> by a string <delimiter>, to an array.
  441. func Split(str, delimiter string) []string {
  442. return strings.Split(str, delimiter)
  443. }
  444. // SplitAndTrim splits string <str> by a string <delimiter> to an array,
  445. // and calls Trim to every element of this array. It ignores the elements
  446. // which are empty after Trim.
  447. func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
  448. array := make([]string, 0)
  449. for _, v := range strings.Split(str, delimiter) {
  450. v = Trim(v, characterMask...)
  451. if v != "" {
  452. array = append(array, v)
  453. }
  454. }
  455. return array
  456. }
  457. // SplitAndTrimSpace splits string <str> by a string <delimiter> to an array,
  458. // and calls TrimSpace to every element of this array.
  459. // Deprecated.
  460. func SplitAndTrimSpace(str, delimiter string) []string {
  461. array := make([]string, 0)
  462. for _, v := range strings.Split(str, delimiter) {
  463. v = strings.TrimSpace(v)
  464. if v != "" {
  465. array = append(array, v)
  466. }
  467. }
  468. return array
  469. }
  470. // Join concatenates the elements of <array> to create a single string. The separator string
  471. // <sep> is placed between elements in the resulting string.
  472. func Join(array []string, sep string) string {
  473. return strings.Join(array, sep)
  474. }
  475. // JoinAny concatenates the elements of <array> to create a single string. The separator string
  476. // <sep> is placed between elements in the resulting string.
  477. //
  478. // The parameter <array> can be any type of slice, which be converted to string array.
  479. func JoinAny(array interface{}, sep string) string {
  480. return strings.Join(gconv.Strings(array), sep)
  481. }
  482. // Explode splits string <str> by a string <delimiter>, to an array.
  483. // See http://php.net/manual/en/function.explode.php.
  484. func Explode(delimiter, str string) []string {
  485. return Split(str, delimiter)
  486. }
  487. // Implode joins array elements <pieces> with a string <glue>.
  488. // http://php.net/manual/en/function.implode.php
  489. func Implode(glue string, pieces []string) string {
  490. return strings.Join(pieces, glue)
  491. }
  492. // Chr return the ascii string of a number(0-255).
  493. func Chr(ascii int) string {
  494. return string([]byte{byte(ascii % 256)})
  495. }
  496. // Ord converts the first byte of a string to a value between 0 and 255.
  497. func Ord(char string) int {
  498. return int(char[0])
  499. }
  500. // HideStr replaces part of the the string <str> to <hide> by <percentage> from the <middle>.
  501. // It considers parameter <str> as unicode string.
  502. func HideStr(str string, percent int, hide string) string {
  503. array := strings.Split(str, "@")
  504. if len(array) > 1 {
  505. str = array[0]
  506. }
  507. var (
  508. rs = []rune(str)
  509. length = len(rs)
  510. mid = math.Floor(float64(length / 2))
  511. hideLen = int(math.Floor(float64(length) * (float64(percent) / 100)))
  512. start = int(mid - math.Floor(float64(hideLen)/2))
  513. hideStr = []rune("")
  514. hideRune = []rune(hide)
  515. )
  516. for i := 0; i < hideLen; i++ {
  517. hideStr = append(hideStr, hideRune...)
  518. }
  519. buffer := bytes.NewBuffer(nil)
  520. buffer.WriteString(string(rs[0:start]))
  521. buffer.WriteString(string(hideStr))
  522. buffer.WriteString(string(rs[start+hideLen:]))
  523. if len(array) > 1 {
  524. buffer.WriteString("@" + array[1])
  525. }
  526. return buffer.String()
  527. }
  528. // Nl2Br inserts HTML line breaks(<br>|<br />) before all newlines in a string:
  529. // \n\r, \r\n, \r, \n.
  530. // It considers parameter <str> as unicode string.
  531. func Nl2Br(str string, isXhtml ...bool) string {
  532. r, n, runes := '\r', '\n', []rune(str)
  533. var br []byte
  534. if len(isXhtml) > 0 && isXhtml[0] {
  535. br = []byte("<br />")
  536. } else {
  537. br = []byte("<br>")
  538. }
  539. skip := false
  540. length := len(runes)
  541. var buf bytes.Buffer
  542. for i, v := range runes {
  543. if skip {
  544. skip = false
  545. continue
  546. }
  547. switch v {
  548. case n, r:
  549. if (i+1 < length) && (v == r && runes[i+1] == n) || (v == n && runes[i+1] == r) {
  550. buf.Write(br)
  551. skip = true
  552. continue
  553. }
  554. buf.Write(br)
  555. default:
  556. buf.WriteRune(v)
  557. }
  558. }
  559. return buf.String()
  560. }
  561. // AddSlashes quotes chars('"\) with slashes.
  562. func AddSlashes(str string) string {
  563. var buf bytes.Buffer
  564. for _, char := range str {
  565. switch char {
  566. case '\'', '"', '\\':
  567. buf.WriteRune('\\')
  568. }
  569. buf.WriteRune(char)
  570. }
  571. return buf.String()
  572. }
  573. // StripSlashes un-quotes a quoted string by AddSlashes.
  574. func StripSlashes(str string) string {
  575. var buf bytes.Buffer
  576. l, skip := len(str), false
  577. for i, char := range str {
  578. if skip {
  579. skip = false
  580. } else if char == '\\' {
  581. if i+1 < l && str[i+1] == '\\' {
  582. skip = true
  583. }
  584. continue
  585. }
  586. buf.WriteRune(char)
  587. }
  588. return buf.String()
  589. }
  590. // QuoteMeta returns a version of str with a backslash character (\)
  591. // before every character that is among: .\+*?[^]($)
  592. func QuoteMeta(str string, chars ...string) string {
  593. var buf bytes.Buffer
  594. for _, char := range str {
  595. if len(chars) > 0 {
  596. for _, c := range chars[0] {
  597. if c == char {
  598. buf.WriteRune('\\')
  599. break
  600. }
  601. }
  602. } else {
  603. switch char {
  604. case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?':
  605. buf.WriteRune('\\')
  606. }
  607. }
  608. buf.WriteRune(char)
  609. }
  610. return buf.String()
  611. }
  612. // SearchArray searches string <s> in string slice <a> case-sensitively,
  613. // returns its index in <a>.
  614. // If <s> is not found in <a>, it returns -1.
  615. func SearchArray(a []string, s string) int {
  616. for i, v := range a {
  617. if s == v {
  618. return i
  619. }
  620. }
  621. return -1
  622. }
  623. // InArray checks whether string <s> in slice <a>.
  624. func InArray(a []string, s string) bool {
  625. return SearchArray(a, s) != -1
  626. }