用双引号替换单引号并排除某些元素

Posted

技术标签:

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

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

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

代码1:(@https://***.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://***.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 但每个人都在嘲笑我:) 【参考方案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;!\pL)(?!\pL)(?&lt;=^|[,.?!)\s]) 之类的东西来避免这种情况,这样可以避免出现在句子中的字符的正向环视。但是,列表可能会很长。

【讨论】:

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

你可以使用:

input="I'm one of the persons' *** 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' *** 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' *** 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' *** don't th'em said, "hey what" I'll handle it.

Demo

【讨论】:

【参考方案4】:

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

text="the *** 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 *** don't said, "hey what"

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

【讨论】:

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

这是另一种可能的方法:

import re

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

print re.sub("((?<!s)'(?!\w+)|(\s+'))", '"', text)

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

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

【讨论】:

以上是关于用双引号替换单引号并排除某些元素的主要内容,如果未能解决你的问题,请参考以下文章

sql语句中啥时候用单引号啥时候用双引号?如图中的红为啥用双引号?

双引号与单引号有啥区别

PHP中单引号双引号使用原则

PHP中单引号双引号使用原则

sql语句中有单引号怎么办

用双引号或单引号打印 HTML 和 PHP 代码