replacer.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package stringx
  2. import (
  3. "sort"
  4. "strings"
  5. )
  6. // replace more than once to avoid overlapped keywords after replace.
  7. // only try 2 times to avoid too many or infinite loops.
  8. const replaceTimes = 2
  9. type (
  10. // Replacer interface wraps the Replace method.
  11. Replacer interface {
  12. Replace(text string) string
  13. }
  14. replacer struct {
  15. *node
  16. mapping map[string]string
  17. }
  18. )
  19. // NewReplacer returns a Replacer.
  20. func NewReplacer(mapping map[string]string) Replacer {
  21. rep := &replacer{
  22. node: new(node),
  23. mapping: mapping,
  24. }
  25. for k := range mapping {
  26. rep.add(k)
  27. }
  28. rep.build()
  29. return rep
  30. }
  31. // Replace replaces text with given substitutes.
  32. func (r *replacer) Replace(text string) string {
  33. for i := 0; i < replaceTimes; i++ {
  34. var replaced bool
  35. if text, replaced = r.doReplace(text); !replaced {
  36. return text
  37. }
  38. }
  39. return text
  40. }
  41. func (r *replacer) doReplace(text string) (string, bool) {
  42. chars := []rune(text)
  43. scopes := r.find(chars)
  44. if len(scopes) == 0 {
  45. return text, false
  46. }
  47. sort.Slice(scopes, func(i, j int) bool {
  48. if scopes[i].start < scopes[j].start {
  49. return true
  50. }
  51. if scopes[i].start == scopes[j].start {
  52. return scopes[i].stop > scopes[j].stop
  53. }
  54. return false
  55. })
  56. var buf strings.Builder
  57. var index int
  58. for i := 0; i < len(scopes); i++ {
  59. scp := &scopes[i]
  60. if scp.start < index {
  61. continue
  62. }
  63. buf.WriteString(string(chars[index:scp.start]))
  64. buf.WriteString(r.mapping[string(chars[scp.start:scp.stop])])
  65. index = scp.stop
  66. }
  67. if index < len(chars) {
  68. buf.WriteString(string(chars[index:]))
  69. }
  70. return buf.String(), true
  71. }