【问题标题】:Replace single quotes with double with exclusion of some elements用双引号替换单引号并排除某些元素
【发布时间】:2015-11-08 23:11:48
【问题描述】:

我想用双引号替换字符串中的所有单引号,但“n't”、“'ll”、“'m”等出现的情况除外。

input="the stackoverflow don\'t said, \'hey what\'"
output="the stackoverflow don\'t said, \"hey what\""

代码1:(@https://stackoverflow.com/users/918959/antti-haapala)

def convert_regex(text): 
     return re.sub(r"(?<!\w)'(?!\w)|(?<!\w)'(?=\w)|(?<=\w)'(?!\w)", '"', text)

有 3 种情况: ' 前面没有,后面也没有字母数字字符; or 前面没有,但后面跟一个字母数字字符; or 前面是字母数字字符,后面不跟一个字母数字字符。

问题:这不适用于以撇号结尾的单词,即 最所有格复数,它也不适用于非正式的 以撇号开头的缩写。

代码2:(@https://stackoverflow.com/users/953482/kevin)

def convert_text_func(s):
    c = "_" #placeholder character. Must NOT appear in the string.
    assert c not in s
    protected = {word: word.replace("'", c) for word in ["don't", "it'll", "I'm"]}
    for k,v in protected.iteritems():
        s = s.replace(k,v)
    s = s.replace("'", '"')
    for k,v in protected.iteritems():
        s = s.replace(v,k)
    return s

要指定的词集太大,如如何指定人等。 请帮忙。

编辑 1: 我正在使用@anubhava 的绝妙答案。我正面临这个问题。有时,该方法会出现语言翻译失败。 代码=

text=re.sub(r"(?<!s)'(?!(?:t|ll|e?m|s|d|ve|re|clock)\b)", '"', text)

问题:

在文本中,'Kumbh melas' melas 是印地语到英语的翻译,而不是复数所有格名词。

Input="Similar to the 'Kumbh melas', celebrated by the banks of the holy rivers of India,"
Output=Similar to the "Kumbh melas', celebrated by the banks of the holy rivers of India,
Expected Output=Similar to the "Kumbh melas", celebrated by the banks of the holy rivers of India,

我正在寻找可能以某种方式修复它的条件。人工干预是最后的选择。

编辑 2: 天真而漫长的修复方法:

def replace_translations(text):
    d = enchant.Dict("en_US")
    words=tokenize_words(text)
    punctuations=[x for x in string.punctuation]
    for i,word in enumerate(words):
        print i,word
        if(i!=len(words) and word not in punctuations and d.check(word)==False and words[i+1]=="'"):
            text=text.replace(words[i]+words[i+1],words[i]+"\"")
    return text

是否有我遗漏的极端案例或有更好的方法?

【问题讨论】:

  • 寻找角色的机制与语言无关。因此,您可能会误以为可以使用正则表达式来做到这一点。
  • @sln 我放了一个non-regex answer 但每个人都在嘲笑我:)

标签: python regex replace nlp


【解决方案1】:

第一次尝试

你也可以使用这个正则表达式:

(?:(?<!\w)'((?:.|\n)+?'?)'(?!\w))

DEMO IN REGEX101

这个正则表达式匹配整个句子/单词与两个引号,从开头和结尾,但也将引用的内容包含在第 nr 1 组内,因此您可以将匹配的部分替换为"\1"

  • (?&lt;!\w) - 对非单词字符进行否定查找,以排除诸如“you'll”之类的单词,但允许正则表达式匹配\n:;、@ 等字符之后的 quatations 987654330@或-等假设在引用之前总是有一个空格是有风险的。
  • ' - 单引号,
  • (?:.|\n)+?'?) - 非捕获组:一个或多个任意字符或 带有惰性 quantifire 的新行(匹配多行句子)(避免 从第一个到最后一个单引号匹配),然后是 可选的单引号 sing,如果连续有两个
  • '(?!\w) - 单引号,后跟非单词字符,排除 像“i'm”、“you're”等文字,其中引号是 beetwen 词,

s'案例

但是,在以 s 结尾的单词之后出现带有撇号的句子匹配仍然存在问题,例如:'the classes' hours'。我认为当s 后跟' 应该被视为引号的结尾,或者带有撇号的s 时,用正则表达式来区分是不可能的。但我想出了一种解决这个问题的有限方法,使用正则表达式:

(?:(?<!\w)'((?:.|\n)+?'?)(?:(?<!s)'(?!\w)|(?<=s)'(?!([^']|\w'\w)+'(?!\w))))

DEMO IN REGEX101

PYTHON IMPLEMENTATION

对于s':(?&lt;!s)'(?!\w)|(?&lt;=s)'(?!([^']|\w'\w)+'(?!\w) 的情况,有额外的替代方案,其中:

  • (?&lt;!s)'(?!\w) - 如果' 之前没有s,则匹配上面的正则表达式(第一次尝试),
  • (?&lt;=s)'(?!([^']|\w'\w)+'(?!\w) - 如果在' 之前有s,则仅当没有其他' 后跟非单词时才结束此' 的匹配 以下文本中的字符,结束之前或另一个 ' 之前(但只有 ' 前面有 s 以外的字母,或下一个报价的开头)。 \w'\w 是在这样的匹配中包含一个',它位于字母之间,例如i'm 等。

这个正则表达式应该匹配错误,只有在连续有几个 s' 情况下。不过,它远非完美的解决方案。

\w的缺陷

另外,使用\w 总是有可能' 会出现在sybol 或非[a-zA-Z_0-9] 之后,但仍然是字母字符,如某些本地语言字符,然后它将被视为一个四分法的开头。可以通过将(?&lt;!\w)(?!\w) 替换为(?&lt;!\p{L})(?!\p{L})(?&lt;=^|[,.?!)\s]) 之类的东西来避免这种情况,这样可以避免出现在句子中的字符的正向环视。但是,列表可能会很长。

【讨论】:

  • 绝对是我想要的方式。请考虑将[^']+ 替换为(?:[^']+|'\w)+,以使其匹配“'The Joneses' car won't start'”,假设此处添加的回溯步骤不是问题。
  • @Mariano 感谢您的精彩提示!但是我决定把\w'\w而不是'\w,在匹配中包含',当它在字母之间时,但避免匹配'后跟字母,这应该是下一个引号的开头。这是一个真正的回溯怪物,但问题相当复杂。再次感谢您!
【解决方案2】:

你可以使用:

input="I'm one of the persons' stackoverflow don't th'em said, 'hey what' I'll handle it."
print re.sub(r"(?<!s)'(?!(?:t|ll|e?m)\b)", '"', input)

输出:

I'm one of the persons' stackoverflow don't th'em said, "hey what" I'll handle it.

RegEx Demo

【讨论】:

  • @anubhava谢谢!字符串所有格复数名词如person'等呢?
  • 这里能解释一下\b的用法吗?
  • 太棒了!谢谢。我很少使用环顾四周。添加(?&lt;!s) 这个re.sub(r"'(?!((?:t|ll|m|em)|(?&lt;!s))\b)", '"', input) 正确吗?(为'em 添加了em)
  • 我尝试添加(?&lt;!s),但我做错了什么。您可以编辑答案以包含它吗?
  • 非常感谢!有人怀疑您使用 e?m 而不是 em 的任何原因。
【解决方案3】:

试试这个:你可以使用这个正则表达式((?&lt;=\s)'([^']+)'(?=\s))并替换为"\2"

import re
p = re.compile(ur'((?<=\s)\'([^\']+)\'(?=\s))')
test_str = u"I'm one of the persons' stackoverflow don't th'em said, 'hey what' I'll handle it."
subst = u"\"\2\""

result = re.sub(p, subst, test_str)

输出

I'm one of the persons' stackoverflow don't th'em said, "hey what" I'll handle it.

Demo

【讨论】:

    【解决方案4】:

    这是一种非正则表达式的方法

    text="the stackoverflow don't said, 'hey what'"
    
    out = []
    for i, j in enumerate(text):
        if j == '\'':
            if text[i-1:i+2] == "n't" or text[i:i+3] == "'ll" or text[i:i+3] == "'m":
                out.append(j)
            else:
                out.append('"')
        else:
            out.append(j)
    
    print ''.join(out)
    

    作为输出给出

    the stackoverflow don't said, "hey what"
    

    当然,您可以改进排除列表,不必使用手动检查每个排除...

    【讨论】:

    • 问题的第二个标签名为regex :))
    • 我知道这可能是一个有效的解决方案,只是因为讽刺而有趣。
    • 是的。我有一个不用正则表达式来解决问题的爱好:)
    • 有趣,我有一个用正则表达式解决问题的爱好:D
    【解决方案5】:

    这是另一种可能的方法:

    import re
    
    text = "I'm one of the persons' stackoverflow don't th'em said, 'hey what' I'll handle it."
    
    print re.sub("((?<!s)'(?!\w+)|(\s+'))", '"', text)
    

    我试图避免对特殊情况的需要,它给出了:

    I'm one of the persons' stackoverflow don't th'em said,"hey what" I'll handle it.
    

    【讨论】:

      猜你喜欢
      • 2021-08-12
      • 1970-01-01
      • 2018-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-18
      • 2018-09-23
      • 2011-01-26
      相关资源
      最近更新 更多