【问题标题】:apostrophe in word not being recognized for string replace字符串替换无法识别单词中的撇号
【发布时间】:2018-05-01 13:47:10
【问题描述】:

我在用正则表达式替换单词 "you're" 时遇到问题。

所有其他单词都在正确更改,只是单词 “you're”。 我认为它不是在撇号之后解析。

我必须将单词 "you" 替换为 "I""you're" 替换为 "I'米”。 它将 "you" 更改为 "I""you're" 变为 "I're"因为它没有超过撇号,并且出于某种原因它认为这是单词的结尾。我必须以某种方式逃避撇号。

请参阅下面的相关代码。

package main

import (
    "fmt"
    "math/rand"
    "regexp"
    "strings"
    "time"
)

//Function ElizaResponse to take in and return a string
func ElizaResponse(str string) string {

    //  replace := "How do you know you are"

    /*Regex MatchString function with isolation of the word "father"
    *with a boundry ignore case regex command.
     */
    if matched, _ := regexp.MatchString(`(?i)\bfather\b`, str);
    //Condition to replace the original string if it has the word "father"
    matched {
        return "Why don’t you tell me more about your father?"
    }
    r1 := regexp.MustCompile(`(?i)\bI'?\s*a?m\b`)

    //Match the words "I am" and capture for replacement
    matched := r1.MatchString(str)

    //condition if "I am" is matched
    if matched {

        capturedString := r1.ReplaceAllString(str, "$1")
        boundaries := regexp.MustCompile(`\b`)
        tokens := boundaries.Split(capturedString, -1)

        // List the reflections.
        reflections := [][]string{
            {`I`, `you`},
            {`you're`, `I'm`},
            {`your`, `my`},
            {`me`, `you`},
            {`you`, `I`},
            {`my`, `your`},
        }

        // Loop through each token, reflecting it if there's a match.
        for i, token := range tokens {
            for _, reflection := range reflections {
                if matched, _ := regexp.MatchString(reflection[0], token); matched {
                    tokens[i] = reflection[1]
                    break
                }
            }
        }

        // Put the tokens back together.
        return strings.Join(tokens, ``)

    }

    //Get random number from the length of the array of random struct
    //an array of strings for the random response
    response := []string{"I’m not sure what you’re trying to say. Could you explain it to me?",
        "How does that make you feel?",
        "Why do you say that?"}
    //Return a random index of the array
    return response[rand.Intn(len(response))]

}

func main() {
    rand.Seed(time.Now().UTC().UnixNano())

    fmt.Println("Im supposed to just take what you're saying at face value?")
    fmt.Println(ElizaResponse("Im supposed to just take what you're saying at face value?"))


}

【问题讨论】:

  • 请注意,您的示例文本包含卷曲撇号,而您的正则表达式仅包含单引号(当您按单引号键时实际得到的结果没有某种“智能引号”功能在文字处理器中查找)。
  • 注意r1 模式不包含任何捕获组,当您替换为$1 时,您只需删除匹配项。所以,capturedString := r1.ReplaceAllString(str, "$1") = capturedString := r1.ReplaceAllString(str, "")。我知道这不是代码中最重要的部分,但请确保您了解它在做什么。
  • 当我完成第一个问题时,我将替换它。它正在替换“我是,我是,我”,所有大小写都被忽略了。在我有你正确改变这个词之后,我有句子要连接到它。
  • 好的,请参阅this demo。抱歉,我很着急,但你会明白要点的。
  • 谢谢 Wiktor Stribiżew 这是一个很好的解决方案。我发现我只需要将正则表达式更改为边界变量。我会在下面发布我的解决方案。再次感谢!!

标签: regex go regex-group


【解决方案1】:

请注意,撇号字符会创建单词边界,因此您在正则表达式中使用\b 可能会让您感到困惑。也就是说,字符串"I'm" 有四个字边界,每个字符前后各一个。

┏━┳━┳━┓
┃I┃'┃m┃
┗━┻━┻━┛
│ │ │ └─ end of line creates a word boundary
│ │ └─── after punctuation character creates a word boundary
│ └───── before punctuation character creates a word boundary
└─────── start of line creates a word boundary

无法更改单词边界元字符的行为,因此您最好将包含带有标点符号的完整单词的正则表达式映射到所需的替换,例如:

type Replacement struct {
  rgx *regexp.Regexp
  rpl string
}

replacements := []Replacement{
  {regexp.MustCompile("\\bI\\b"), "you"},
  {regexp.MustCompile("\\byou're\\b"), "I'm"},
  // etc...
}

另请注意,您的一个示例包含一个 UTF-8“右单引号”(U+2019, 0xe28099),不要与 UTF-8/ASCII 撇号(U+0027, 0x27)混淆!

fmt.Sprintf("% x", []byte("'’")) // => "27 e2 80 99"

【讨论】:

  • 感谢您的回复 maerics 我可以看到您在说什么,但我只需要将您替换为 I'm。
  • 抱歉发布得太早了 :) 有没有办法摆脱撇号并强制正则表达式将其视为一个词?
  • 我认为 split 函数也将其拆分为单独的单词,因为边界:= regexp.MustCompile(\b)。
  • fmt.Println(tokens) 的结果是:[应该只接受你所说的面值?]应该只接受我所说的面值?
  • 是否有一个条件我可以在那里忽略在 u 和 ' 之后放置边界
【解决方案2】:

你想要在这里实现的是用特定的替换来替换特定的字符串。使用字符串键和值的映射更容易实现这一点,其中每个唯一键都是要搜索的文字短语,值是要替换的文本。

您可以这样定义反射

reflections := map[string]string{
    `you're`: `I'm`,
    `your`: `my`,
    `me`: `you`,
    `you`: `I`,
    `my`: `your`,
    `I` : `you`,
}

接下来,需要按长度降序获取键(这里是a sample code):

type ByLenDesc []string
func (a ByLenDesc) Len() int {
   return len(a)
}
func (a ByLenDesc) Less(i, j int) bool {
   return len(a[i]) > len(a[j])
}
func (a ByLenDesc) Swap(i, j int) {
   a[i], a[j] = a[j], a[i]
}

然后在函数中:

var keys []string
for key, _ := range reflections {
    keys = append(keys, key)
}
sort.Sort(ByLenDesc(keys))

然后构建模式:

pat := "\\b(" + strings.Join(keys, `|`) + ")\\b"
// fmt.Println(pat) // => \b(you're|your|you|me|my|I)\b

该模式匹配 you'reyouryoumemyI 作为整个单词。

res := regexp.MustCompile(pat).ReplaceAllStringFunc(capturedString, func(m string) string {
    return reflections[m]
})

以上代码创建了一个正则表达式对象,并将所有匹配项替换为对应的reflections 值。

请参阅Go demo

【讨论】:

    【解决方案3】:

    我发现我只需要更改这两行代码。

    boundaries := regexp.MustCompile(`(\b[^\w']|$)`)
    return strings.Join(tokens, ` `)
    

    它会阻止 split 函数在 ' 字符处进行拆分。 那么返回的tokens需要一个空格来放出字符串,否则就是一个连续的字符串。

    【讨论】:

      猜你喜欢
      • 2021-08-21
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-08
      • 1970-01-01
      相关资源
      最近更新 更多