【问题标题】:Convert command line arguments to regular expression将命令行参数转换为正则表达式
【发布时间】:2013-07-23 16:53:38
【问题描述】:

比如说,我想知道模式“\section”是否在文本“abcd\sectiondefghi”中。当然,我可以这样做:

import re

motif = r"\\section"
txt = r"abcd\sectiondefghi"
pattern = re.compile(motif)
print pattern.findall(txt)

这会给我我想要的。但是,每次我想在新文本中找到新模式时,我都必须更改代码,这很痛苦。因此,我想写一些更灵活的东西,像这样(test.py):

import re
import sys

motif = sys.argv[1]
txt = sys.argv[2]
pattern = re.compile(motif)
print pattern.findall(txt)

然后,我想像这样在终端中运行它:

python test.py \\section abcd\sectiondefghi

但是,这行不通(我讨厌使用\\\\section)。

那么,有什么方法可以将我的用户输入(来自终端或来自文件)转换为 python 原始字符串?或者有没有更好的方法来根据用户输入进行正则表达式模式编译?

非常感谢。

【问题讨论】:

  • 请注意,这个问题实际上并不是关于将字符串转换为原始字符串。这里的问题很简单,在终端中运行的 shell 要求您转义命令行参数。大多数shell 会将cd\sec 中的\s 变成一个普通的旧s 字符。无论您在 python 代码中做什么,您都无法判断 s 前面是否有反斜杠。有关将字符串中的特殊字符转换为转义序列的问题,请参阅here

标签: python regex shell


【解决方案1】:

所以为了清楚起见,您搜索的内容(在您的示例中为“\section”)应该是正则表达式还是文字字符串?如果是后者,re 模块并不是真正适合该任务的工具;给定一个搜索字符串needle 和一个目标字符串haystack,你可以这样做:

# is it in there
needle in haystack

# how many copies are there
n = haystack.count(needle)
python test.py \\section abcd\sectiondefghi
# where is it
ix = haystack.find(needle)

所有这些都比基于正则表达式的版本更有效。

如果您需要在运行时将文字片段插入到较大的正则表达式中,re.escape 仍然很有用,但如果您最终执行re.compile(re.escape(needle)),则在大多数情况下有更好的工具来完成这项任务。

编辑:我开始怀疑这里的真正问题是 shell 的转义规则,这与 Python 或原始字符串无关。也就是说,如果你输入:

python test.py \\section abcd\sectiondefghi

在 Unix 风格的 shell 中,"\section" 部分在 Python 看到之前被 shell 转换为 "\section"。解决这个问题的最简单方法是告诉 shell 跳过转义,您可以通过将参数放在单引号内来做到这一点:

python test.py '\\section' 'abcd\sectiondefghi'

比较和对比:

$ python -c "import sys; print ','.join(sys.argv)" test.py \\section abcd\sectiondefghi
-c,test.py,\section,abcdsectiondefghi

$ python -c "import sys; print ','.join(sys.argv)" test.py '\\section' 'abcd\sectiondefghi'
-c,test.py,\\section,abcd\sectiondefghi

(在此处显式在连接字符串上使用 print 以避免 repr 增加更多混乱...)

【讨论】:

    【解决方案2】:

    一种方法是使用参数解析器,例如optparseargparse

    您的代码将如下所示:

    import re
    from optparse import OptionParser
    
    parser = OptionParser()
    parser.add_option("-s", "--string", dest="string",
                      help="The string to parse")
    parser.add_option("-r", "--regexp", dest="regexp",
                      help="The regular expression")
    parser.add_option("-a", "--action", dest="action", default='findall',
                      help="The action to perform with the regexp")
    
    (options, args) = parser.parse_args()
    
    print getattr(re, options.action)(re.escape(options.regexp), options.string)
    

    我使用它的一个例子:

    > code.py -s "this is a string" -r "this is a (\S+)"
    ['string']
    

    用你的例子:

    > code.py -s "abcd\sectiondefghi" -r "\section"
    ['\\section'] 
    # remember, this is a python list containing a string, the extra \ is okay.
    

    【讨论】:

      【解决方案3】:

      使用re.escape() 确保输入文本在正则表达式中被视为文字文本:

      pattern = re.compile(re.escape(motif))
      

      演示:

      >>> import re
      >>> motif = r"\section"
      >>> txt = r"abcd\sectiondefghi"
      >>> pattern = re.compile(re.escape(motif))
      >>> txt = r"abcd\sectiondefghi"
      >>> print pattern.findall(txt)
      ['\\section']
      

      re.escape() 转义所有非字母数字;在每个这样的字符前面添加一个反斜杠:

      >>> re.escape(motif)
      '\\\\section'
      >>> re.escape('\n [hello world!]')
      '\\\n\\ \\[hello\\ world\\!\\]'
      

      【讨论】:

      • 另一方面,如果您正在搜索文字字符串,那么 re 是错误的工具。
      • @Fredrik:我假设这将成为更大模式的一部分,而 OP 只是简化了。
      • @MartijnPieters 谢谢,re.escape 真的很有帮助!
      • @Fredrik:我对更退化的搜索模式感兴趣并想使用 re。我提供的代码只是一个简化的例子。
      • 原始字符串是一种在Python代码中关闭字符串文字转义规则的方法(就像单引号是一种在shell中关闭shell的转义规则的方法一样),与此无关使用正则表达式机制,在编写 Python 代码时不仅方便。但如果你从 Python 外部获取正则表达式,它就不是 Python 代码......
      猜你喜欢
      • 2012-03-03
      • 2016-07-14
      • 2015-09-09
      • 1970-01-01
      • 2011-10-02
      • 2016-04-02
      • 1970-01-01
      • 2019-05-11
      • 2014-11-08
      相关资源
      最近更新 更多