用双引号替换单引号并排除某些元素
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"
。
(?<!\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'
:(?<!s)'(?!\w)|(?<=s)'(?!([^']|\w'\w)+'(?!\w)
的情况,有额外的替代方案,其中:
(?<!s)'(?!\w)
- 如果'
之前没有s
,则匹配上面的正则表达式(第一次尝试),
(?<=s)'(?!([^']|\w'\w)+'(?!\w)
- 如果在'
之前有s
,则仅当没有其他'
后跟非单词时才结束此'
的匹配
以下文本中的字符,结束之前或另一个 '
之前(但只有 '
前面有 s
以外的字母,或下一个报价的开头)。 \w'\w
是在这样的匹配中包含一个'
,它位于字母之间,例如i'm
等。
这个正则表达式应该匹配错误,只有在连续有几个 s'
情况下。不过,它远非完美的解决方案。
\w的缺陷
另外,使用\w
总是有可能'
会出现在sybol 或非[a-zA-Z_0-9]
之后,但仍然是字母字符,如某些本地语言字符,然后它将被视为一个四分法的开头。可以通过将(?<!\w)
和(?!\w)
替换为(?<!\pL)
和(?!\pL)
或(?<=^|[,.?!)\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
的用法吗?
太棒了!谢谢。我很少使用环顾四周。添加(?<!s)
这个re.sub(r"'(?!((?:t|ll|m|em)|(?<!s))\b)", '"', input)
正确吗?(为'em 添加了em)
我尝试添加(?<!s)
,但我做错了什么。您可以编辑答案以包含它吗?
非常感谢!有人怀疑您使用 e?m
而不是 em
的任何原因。【参考方案3】:
试试这个:你可以使用这个正则表达式((?<=\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.
【讨论】:
以上是关于用双引号替换单引号并排除某些元素的主要内容,如果未能解决你的问题,请参考以下文章