fix: replace shoud replace the longest match

This commit is contained in:
hudahai
2023-02-04 15:31:16 +08:00
committed by kevin
parent aaa974e1ad
commit 687a1d15da
3 changed files with 73 additions and 61 deletions

View File

@@ -1,6 +1,8 @@
package stringx
import "strings"
import (
"bytes"
)
type (
// Replacer interface wraps the Replace method.
@@ -30,68 +32,27 @@ func NewReplacer(mapping map[string]string) Replacer {
// Replace replaces text with given substitutes.
func (r *replacer) Replace(text string) string {
var builder strings.Builder
var start int
chars := []rune(text)
size := len(chars)
for start < size {
cur := r.node
if start > 0 {
builder.WriteString(string(chars[:start]))
}
for i := start; i < size; i++ {
child, ok := cur.children[chars[i]]
if ok {
cur = child
} else if cur == r.node {
builder.WriteRune(chars[i])
// cur already points to root, set start only
start = i + 1
continue
var buf bytes.Buffer
target := []rune(text)
cur := r.node
nextStart := 0
for len(target) != 0 {
used, jump, matched := cur.longestMatch(target, nextStart)
if matched {
replaced := r.mapping[string(target[:used])]
target = append([]rune(replaced), target[used:]...)
cur = r.node
} else {
buf.WriteString(string(target[:used]))
target = target[used:]
if jump != nil {
cur = jump
nextStart = jump.depth
} else {
curDepth := cur.depth
cur = cur.fail
child, ok = cur.children[chars[i]]
if !ok {
// write this path
builder.WriteString(string(chars[i-curDepth : i+1]))
// go to root
cur = r.node
start = i + 1
continue
}
failDepth := cur.depth
// write path before jump
builder.WriteString(string(chars[start : start+curDepth-failDepth]))
start += curDepth - failDepth
cur = child
cur = r.node
nextStart = 0
}
if cur.end {
val := string(chars[i+1-cur.depth : i+1])
builder.WriteString(r.mapping[val])
builder.WriteString(string(chars[i+1:]))
// only matching this path, all previous paths are done
if start >= i+1-cur.depth && i+1 >= size {
return builder.String()
}
chars = []rune(builder.String())
size = len(chars)
builder.Reset()
break
}
}
if !cur.end {
builder.WriteString(string(chars[start:]))
return builder.String()
}
}
return string(chars)
return buf.String()
}