【问题标题】:Replace whole word without using regex在不使用正则表达式的情况下替换整个单词
【发布时间】:2020-06-12 20:18:59
【问题描述】:

我正在使用string.Replace 替换子字符串A

func removeIP(text string) string {
    text = strings.Replace(text, "someWord", "**NewWord**", -1)
    return text
}

func removeIPUsingRegex(text string) string {
    var re = regexp.MustCompile(`\b` + "someWord" + `\b`) // I want to replace whole word only
    text = re.ReplaceAllString(text, "**NewWord**")
}

我在这里面临的问题是,我只想在字符串替换不支持的情况下替换整个单词。
因为我必须替换非常非常大的字符串,所以可能以 GB 为单位。与字符串替换相比,正则表达式非常慢。
例如:文本:"abcdef defgh /def/ .def/ =def= def xxxy" -> 将 def 替换为 DEF
output: "abcdef defgh /DEF/ .DEF/ =DEF= DEF xxxy" //注意只有整个单词被替换了。

Regex 将时间缩短了近 100 倍 (https://medium.com/codezillas/golang-replace-vs-regexp-de4e48482f53)。任何想法将不胜感激。

【问题讨论】:

  • 正则表达式需要更长时间的原因是它更复杂,这也是它实际工作的原因。简单的字符串替换过于简单,无法处理您正在干燥的事情。您可以编写自己的替换函数,逐个字符地迭代字符串,其性能可能介于strings.Replaceregexp.ReplaceAllString 之间,但也需要在开发和测试方面付出更多努力。
  • 如果您的文本有几 GB 大,并且您使用 io.Reader 访问它,您也可以 look at this question 关于在流式传输时替换字符,但是它不能回答“整个单词”的问题
  • 您可以使用非常快速的字符串搜索和 Index() 来查找子字符串匹配的位置。然后检查匹配前后的字符。如果它们不是字母,则替换出现的子字符串。这将比替换稍慢。
  • 对我之前评论的补充:要查找从字符串内某个索引开始的子字符串的位置,请使用 Slice() 然后 Index():stackoverflow.com/questions/54090467/…
  • 是的,流式阅读是最有可能的答案。 Go 的 re2 已经是最高效的正则表达式引擎之一,但仍然不适合(真正的)大文件。

标签: regex string go


【解决方案1】:

使用的 KMP 算法
// 替换WholeWord ...

 func ReplaceWholeWord(text string, oldWord string, newWord string) string {
        var patternLength = len(oldWord)
        var textLength = len(text) 

        var copyIndex = 0
        var textIndex = 0
        var patternIndex = 0
        var newString strings.Builder
        var lps = computeLPSArray(oldWord)

        for textIndex < textLength {
            if oldWord[patternIndex] == text[textIndex] {
                patternIndex++
                textIndex++
            }
            if patternIndex == patternLength {
                startIndex := textIndex - patternIndex
                endIndex := textIndex - patternIndex + patternLength - 1

                if checkIfWholeWord(text, startIndex, endIndex) {
                    if copyIndex != startIndex {
                        newString.WriteString(text[copyIndex:startIndex])
                    }
                    newString.WriteString(newWord)
                    copyIndex = endIndex + 1
                }

                patternIndex = 0
                textIndex = endIndex + 1

            } else if textIndex < textLength && oldWord[patternIndex] != text[textIndex] {

                if patternIndex != 0 {
                    patternIndex = lps[patternIndex-1]

                } else {
                    textIndex = textIndex + 1
                }

            }
        }
        newString.WriteString(text[copyIndex:])

        return newString.String()
    }

    func computeLPSArray(pattern string) []int {
        var length = 0
        var i = 1
        var patternLength = len(pattern)

        var lps = make([]int, patternLength)

        lps[0] = 0

        for i = 1; i < patternLength; {
            if pattern[i] == pattern[length] {
                length++
                lps[i] = length
                i++

            } else {

                if length != 0 {
                    length = lps[length-1]

                } else {
                    lps[i] = length
                    i++
                }
            }
        }
        return lps
    }

    func checkIfWholeWord(text string, startIndex int, endIndex int) bool {
        startIndex = startIndex - 1
        endIndex = endIndex + 1

        if (startIndex < 0 && endIndex >= len(text)) ||
            (startIndex < 0 && endIndex < len(text) && isNonWord(text[endIndex])) ||
            (startIndex >= 0 && endIndex >= len(text) && isNonWord(text[startIndex])) ||
            (startIndex >= 0 && endIndex < len(text) && isNonWord(text[startIndex]) && isNonWord(text[endIndex])) {
            return true
        }

        return false
    }

    func isNonWord(c byte) bool {
        return !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_'))
    }

【讨论】:

  • 这与一般的字符串替换算法相同。我使用了额外的空间,但空间对我来说不是问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-11
  • 1970-01-01
  • 1970-01-01
  • 2021-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多