【问题标题】:Using Python regex matches in eval()在 eval() 中使用 Python 正则表达式匹配
【发布时间】:2018-03-21 19:37:26
【问题描述】:

我想利用用户输入来匹配和重新排列字符串。 在 Perl 中,一个简单的示例如下所示:

use strict;

my $str = 'abc123def456ghi';
my $user_input1 = '(\d+).+?(\d+)';
my $user_input2 = '$2.$1';
if ($str =~ /$user_input1/) {
  my $newstr = eval($user_input2);
  print $newstr;
}
else {print "No match..."};

原则上同样适用于 Python:

import re

mystr = 'abc123def456ghi'
user_input1 = '(\d+).+?(\d+)'
user_input2 = 'm.group(2) + m.group(1)'
m = re.search(user_input1,mystr)
if m:
    newstr = eval(user_input2)
    print (newstr)
else: print ("No match...")

结果:456123

不过,'m.group(1)''m.group(2)' 表达式对于用户来说不是很友好,如果您必须在输入字段中输入多次。

因此,我想知道在 Python 中是否有类似的紧凑表达式,如 Perl 中的 '$1' '$2'? 我无法使用'\1''\2' 在Python 中工作。 有什么想法吗?

编辑:

对不起,我在下面的一些 cmets 之后尝试解释: 我正在尝试eval(),因为它似乎可以与m.group(1) 等一起使用。 但显然由于某种原因r'\1' 等在eval() 中不被接受

import re
mystr = 'abc123def456ghi'
user_input1 = r'(\d+).+?(\d+)'
user_input2 = r'\2\1'
newstr = eval(user_input2)
print (newstr)

结果

SyntaxError: unexpected character after line continuation character

关于使用 re.sub() 的建议

它不应该是简单的字符串替换,而是重新排列+匹配的添加。 如果我修改原来的正则表达式

user_input1 = r'(.+?)(\d+).+?(\d+)(.+)'

我可以使用user_input2 = r'\3\2' 但是,例如如果我想在匹配之间添加“999”(当然,r'\3999\2'r'\3+"999"+\2' 都不会这样做),那么我可能会回来使用 eval()m.group(1) 等,尽管我想保持用户输入为尽可能简短。也许,我可以使用一些建议的替换。

【问题讨论】:

  • 你为什么使用eval
  • 你总是可以写例如g = m.group 然后为组执行g(1)g(2)。然后,您甚至可以在 f 字符串中执行 {g(1)} 等。但真的没有理由这样做。您只需键入一次,但会阅读数百次,因此可读性远比节省几次击键更重要。
  • 你不应该使用eval。你应该使用this
  • “我无法正常工作”是什么意思?向我们展示您调用 re.subm.expand 或其他任何名称的代码,我们可能会告诉您您做错了什么,但不知道您尝试了什么或发生了什么,任何人都只能说您一定做了什么哪里出错了。
  • 我要冒昧地说这是stackoverflow.com/questions/20765265/…的复制品

标签: python regex eval


【解决方案1】:

您不需要eval。其实you want to avoid eval like the plague

您可以使用match.expand 实现相同的输出:

mystr = 'abc123def456ghi'
user_input1 = r'(\d+).+?(\d+)'
user_input2 = r'\2\1'
match = re.search(user_input1, mystr)
result = match.expand(user_input2)
# result: 456123

在匹配之间插入999的例子很容易使用\g<group_number>语法解决:

mystr = 'abc123def456ghi'
user_input1 = r'(.+?)(\d+).+?(\d+)(.+)'
user_input2 = r'\g<3>999\2'
match = re.search(user_input1, mystr)
result = match.expand(user_input2)
# result: 456999123

如您所见,如果您需要做的只是移动捕获的文本并插入新文本,那么正则表达式模块可以满足您的需求。仅当您真的必须执行代码时才使用eval

【讨论】:

  • 好的,如果可能,我应该避免使用eval()。但是,为了使 user_input1 尽可能简单,我不想强​​迫他总是在 user_input1 的开头和结尾分别输入 (.+?)(.+)。但是使用re.sub() 将需要这个,因为它是我想要的替换而不是重新排列匹配。我仍然看不到使用re.sub() 实现这一目标的方法。如果有人可以告诉我没有eval()的方法...很好!
  • 您是否测试过您的第一个示例?结果是:abc456123ghi,这不是我想要的结果。所以,re.sub() 开头需要(.+?)(.+) 结尾需要user_input1。请解释一下为什么你认为你不需要它。使用eval(),我可以根据自己的喜好重新组合匹配\1\2、...。请让我知道如何使用re.sub() 做到这一点。
  • @theozh 抱歉造成误会。答案已更新。
  • 在最初的问题中我写了我所期望的:Result: 456123。我假设你知道什么是正则表达式。首先,我匹配表达式r'(\d+)(.+?)(\d+)',用文字表示:几个数字,中间有一些字符,然后是几个数字。其次,如果mystr 中有匹配项,我只想重用和组合第二组数字和第一组数字,仅此而已。如果您测试了建议的代码,这将是一个反例,因为结果是abc456123ghi。抱歉,耽误您的时间了。
  • aaah,expand() 成功了! @abarnert 上面提到过。我在我的 Python 教程中找不到它,因为您坚持可以使用 re.sub() 完成它,所以我认为我没有看到对其他人来说显而易见的东西。好的,现在我终于可以跳过邪恶eval(),每个人都很高兴。感谢您提供更新的答案和您的耐心。
【解决方案2】:

受到上述cmets的“启发”,我认为以下似乎是对我问题的回答。 (假设您只有\1\9)。至少,这个解决方案对我来说并不直观和明显(就像 Python 喜欢的那样)。欢迎使用更优雅的构造。

import re

mystr = 'abc123def456ghi'
user_input1 = r'(\d+).+?(\d+)'
user_input2 = r'\2+"999"+\1'
user_input2 = re.sub(r'\\(\d)',r'm.group(\1)',user_input2,flags=re.S)
m = re.search(user_input1,mystr)
if m:
     newstr = eval(user_input2)
     print (newstr)

【讨论】:

  • 虽然我被告知不要使用eval(),但到目前为止,这是第一个满足我要求的解决方案。
  • @Aran-Fey,你用re.sub() 测试过你的代码吗?使用我的 Python3.6,结果是 abc456999123ghi。你的结果是什么?
  • 哦,我现在看到了问题所在。您代码中的re.sub 欺骗了我,让我认为您想要替换文本,而实际上您想要extract 文本。我的错。我会更新我的答案。
猜你喜欢
  • 2014-03-30
  • 1970-01-01
  • 2016-07-01
  • 2013-09-22
  • 2022-12-11
  • 1970-01-01
  • 1970-01-01
  • 2015-09-14
  • 2021-12-31
相关资源
最近更新 更多