我有以下字符串:
'Well, I've tried to say "How Doth the Little Busy Bee," but it all came different!' Alice replied in a very melancholy voice. She continued, 'I'll try again.'
现在,我希望提取以下引用:
1. Well, I've tried to say "How Doth the Little Busy Bee," but it all came different!
2. How Doth the Little Busy Bee,
3. I'll try again.
我尝试了以下代码,但我没有得到我想要的东西。 [^\1]*
未按预期运行。或者其他地方的问题是什么?
import re
s = "'Well, I've tried to say \"How Doth the Little Busy Bee,\" but it all came different!' Alice replied in a very melancholy voice. She continued, 'I'll try again.'"
for i, m in enumerate(re.finditer(r'([\'"])(?!(?:ve|m|re|s|t|d|ll))(?=([^\1]*)\1)', s)):
print("\nGroup {:d}: ".format(i+1))
for g in m.groups():
print(' '+g)
答案 0 :(得分:3)
如果确实需要从仅应用一次的单个正则表达式返回所有结果,则需要使用前瞻((?=findme)
),以便查找位置返回到每场比赛后开始 - 请参阅this answer以获取更详细的说明。
为了防止错误匹配,还需要一些关于增加复杂性的引号的条款,例如: I've
中的撇号不应算作开头或收盘价。没有一种明确的方法可以做到这一点,但我所遵循的规则是:
A"
不会算作开场报价,但会计算,"
。'B
不会算作结束语,但'.
会计算。应用上述规则会产生以下正则表达式:
(?=(?:(?<!\w)'(\w.*?)'(?!\w)|"(\w.*?)"(?!\w)))
对任何可能的候选正则表达式进行良好的快速健全性检查测试是反转引号。这已在此处的演示中完成:https://regex101.com/r/vX4cL9/1
答案 1 :(得分:2)
修改强>
我修改了我的正则表达式,它恰当地匹配了更复杂的情况:
(?=(?<!\w|[!?.])('|\")(?!\s)(?P<content>(?:.(?!(?<=(?=\1).)(?!\w)))*)\1(?!\w))
现在更加复杂,主要的改进是在一些标点字符([!?.]
)和更好的引用案例分离之后不直接匹配。验证多样化的例子。
该句子将在content
捕获组中。当然它有一些限制,依赖于空格的使用等等。但它应该适用于大多数正确的格式化句子 - 或者至少它可以用于示例。
(?=(?<!\w|[!?.])('|\")(?!\s)
- 匹配'
或"
前面没有单词或标点符号((?<!\w|[!?.])
)或不被空格((?!\s)
)拒绝, {1}}或'
部分在第1组中被捕获以进一步使用,"
- 匹配句子,然后是
同一个字符(在组1中捕获的(?P<content>(?:.(?!(?<=(?=\1).)(?!\w)))*)\1(?!\w))
或'
)在启动时忽略其他引号它直接与整个句子不匹配,但是捕捉组嵌套在环绕构造中,所以使用全局匹配修饰符它也会匹配句子内的句子 - 因为它直接匹配句子开始前的位置。
关于你的正则表达式:
我想,通过"
,你的意思是任何字符,但不是第1组中捕获的字符,但字符类不能以这种方式工作,因为它将[^\1]*
视为八进制表示法中的字符(我认为这是一种空白)而不是捕捉群体的参考。看看this example - 阅读说明。同时比较THIS和THIS正则表达式的匹配。
为了达到你想要的效果,你应该使用lookaround,如下所示:(')((?:.(?!\1))*.)
- 捕获开头字符,然后匹配每个未跟随捕获的开头字符的字符,然后再捕获一个字符,这是直接在捕获的字符之前 - 你在被排除的字符之间有完整的内容。
答案 2 :(得分:2)
对于Python正则表达式来说这是一个很好的问题,因为遗憾的是,我认为re
模块是one of the most underpowered of mainstream regex engines。这就是为什么Python的任何严肃的正则表达式工作,我转向Matthew Barnett的恒星regex模块,其中包含了Perl,PCRE和.NET的一些极好的功能。
我向您展示的解决方案可以适用于re
,但regex
更具可读性,因为它是模块化的。另外,请将其视为更复杂的嵌套匹配的起始块,因为regex
允许您编写与{Perl和PCRE中的{{}}类似的recursive regular expressions。
好的,有足够的话题,这里是代码(除了导入和定义之外只有四行)。请不要让长正则表达式吓到你:它很长,因为它的设计是可读的。解释如下。
守则
import regex
quote = regex.compile(r'''(?x)
(?(DEFINE)
(?<qmark>["']) # what we'll consider a quotation mark
(?<not_qmark>[^'"]+) # chunk without quotes
(?<a_quote>(?P<qopen>(?&qmark))(?¬_qmark)(?P=qopen)) # a non-nested quote
) # End DEFINE block
# Start Match block
(?&a_quote)
|
(?P<open>(?&qmark))
(?¬_qmark)?
(?P<quote>(?&a_quote))
(?¬_qmark)?
(?P=open)
''')
str = """'Well, I have tried to say "How Doth the Little Busy Bee," but it all came different!' Alice replied in a very melancholy voice. She continued, 'I will try again.'"""
for match in quote.finditer(str):
print(match.group())
if match.group('quote'):
print(match.group('quote'))
输出
'Well, I have tried to say "How Doth the Little Busy Bee," but it all came different!'
"How Doth the Little Busy Bee,"
'I will try again.'
工作原理
首先,为了简化,请注意我已经冒昧地将I'll
转换为I will
,从而减少了引号的混淆。解决I'll
对于否定前瞻是没有问题的,但我想让正则表达式可读。
在(?(DEFINE)...)
块中,我们定义了三个子表达式qmark
,not_qmark
和a_quote
,这与定义变量或子例程以避免重复的方式非常相似你自己。
在定义块之后,我们继续匹配:
(?&a_quote)
匹配整个报价,|
或...... (?P<open>(?&qmark))
匹配引号并将其捕获到open
组,(?¬_qmark)?
匹配非引号的可选文字(?P<quote>(?&a_quote))
匹配完整报价并将其捕获到quote
组(?¬_qmark)?
匹配非引号的可选文字(?P=open)
匹配在引号开头处捕获的相同引号。然后,Python代码只需打印匹配项和quote
捕获组(如果存在)。
这可以改进吗?你打赌。以这种方式使用(?(DEFINE)...)
,您可以构建漂亮的模式,以后可以重新阅读和理解。
添加递归
如果你想使用纯正则表达式处理更复杂的嵌套,你需要转向递归。
要添加递归,您需要做的就是定义一个组并使用子例程语法引用它。例如,要执行组1中的代码,请使用(?1)
。要在组something
中执行代码,请使用(?&something)
。请记住通过使递归可选(?
)或替换的一侧来留下引擎的出口。
<强>参考强>
答案 3 :(得分:0)
使用juste一个正则表达式传递似乎很难实现,但可以使用相对简单的正则表达式和递归函数来完成:
import re
REGEX = re.compile(r"(['\"])(.*?[!.,])\1", re.S)
S = """'Well, I've tried to say "How Doth the Little Busy Bee," but it all came different!' Alice replied in a very melancholy voice. She continued, 'I'll try again.' 'And we may now add "some more 'random test text'.":' "Yes it seems to be a good idea!" 'ok, let's go.'"""
def extract_quotes(string, quotes_list=None):
list = quotes_list or []
list += [found[1] for found in REGEX.findall(string)]
print("found: {}".format(quotes_list))
index = 0
for quote in list[:]:
index += 1
sub_list = extract_quotes(quote)
list = list[:index] + sub_list + list[index:]
index += len(sub_list)
return list
print extract_quotes(S)
打印:
['Well, I\'ve tried to say "How Doth the Little Busy Bee," but it all came different!', 'How Doth the Little Busy Bee,', "I'll try again.", 'And we may now add "some more \'random test text\'.":\' "Yes it seems to be a good idea!" \'ok, let\'s go.', "some more 'random test text'.", 'Yes it seems to be a good idea!']
请注意,正则表达式使用标点符号来确定引用的文本是否为“实际引用”。为了被提取,报价需要在结束报价之前用标点字符结束。那就是“随机测试文本”不被视为实际报价,而“好吧,我们走吧”。是
正则表达式非常简单,我认为它不需要解释。
Thue extract_quotes
函数查找给定字符串中的所有引号并将它们存储在quotes_list
中。然后,它为每个找到的引用调用自己,寻找内部引用...