replacer.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package stringx
  2. import "strings"
  3. type (
  4. // Replacer interface wraps the Replace method.
  5. Replacer interface {
  6. Replace(text string) string
  7. }
  8. replacer struct {
  9. *node
  10. mapping map[string]string
  11. }
  12. )
  13. // NewReplacer returns a Replacer.
  14. func NewReplacer(mapping map[string]string) Replacer {
  15. rep := &replacer{
  16. node: new(node),
  17. mapping: mapping,
  18. }
  19. for k := range mapping {
  20. rep.add(k)
  21. }
  22. rep.build()
  23. return rep
  24. }
  25. // Replace replaces text with given substitutes.
  26. func (r *replacer) Replace(text string) string {
  27. var builder strings.Builder
  28. var start int
  29. chars := []rune(text)
  30. size := len(chars)
  31. for start < size {
  32. cur := r.node
  33. if start > 0 {
  34. builder.WriteString(string(chars[:start]))
  35. }
  36. for i := start; i < size; i++ {
  37. child, ok := cur.children[chars[i]]
  38. if ok {
  39. cur = child
  40. } else if cur == r.node {
  41. builder.WriteRune(chars[i])
  42. // cur already points to root, set start only
  43. start = i + 1
  44. continue
  45. } else {
  46. curDepth := cur.depth
  47. cur = cur.fail
  48. child, ok = cur.children[chars[i]]
  49. if !ok {
  50. // write this path
  51. builder.WriteString(string(chars[i-curDepth : i+1]))
  52. // go to root
  53. cur = r.node
  54. start = i + 1
  55. continue
  56. }
  57. failDepth := cur.depth
  58. // write path before jump
  59. builder.WriteString(string(chars[start : start+curDepth-failDepth]))
  60. start += curDepth - failDepth
  61. cur = child
  62. }
  63. if cur.end {
  64. val := string(chars[i+1-cur.depth : i+1])
  65. builder.WriteString(r.mapping[val])
  66. builder.WriteString(string(chars[i+1:]))
  67. // only matching this path, all previous paths are done
  68. if start >= i+1-cur.depth && i+1 >= size {
  69. return builder.String()
  70. }
  71. chars = []rune(builder.String())
  72. size = len(chars)
  73. builder.Reset()
  74. break
  75. }
  76. }
  77. if !cur.end {
  78. builder.WriteString(string(chars[start:]))
  79. return builder.String()
  80. }
  81. }
  82. return string(chars)
  83. }