2015-02-11 73 views
4

我想解析源文件中的代码标签。我写这个正则表达式与PCRE正常工作:如何在Python中使用(?(DEFINE))编写正则表达式?

(?<tag>(?&TAG)):\s* 
(?<message>.*?) 
(
< 
    (?<author>(?:\w{3}\s*,\s*)*\w{3})?\s* 
    (?<date>(?&DATE))? 
    (?<flags>(?&FLAGS))? 
> 
)? 
$ 

(?(DEFINE) 
    (?<TAG>\b(NOTE|LEGACY|HACK|TODO|FIXME|XXX|BUG)) 
    (?<DATE>\d{4}-\d{2}-\d{2}) 
    (?<FLAGS>[pts]:\w+\b) 
) 

不幸的是,似乎Python不理解DEFINE(https://regex101.com/r/qH1uG3/1#pcre

什么是Python中最好的解决方法吗?

+0

鉴于您不曾使用过这些定义中的任何一个,而不仅仅是将它们放入内联中? – jonrsharpe 2015-02-11 09:13:16

+0

@TimPietzcker是的,我做到了。很明显,我明白Python不支持DEFINE语句,但可能有类似写入方式的变通方法(例如'(?P )'而不是'(?)') – nowox 2015-02-11 09:16:32

+4

啊,对不起。我以为你已经知道Python不支持subregex定义。对不起,评论不高。我想唯一的解决方法是使用['regex'模块(PyPI)](https://pypi.python.org/pypi/regex)。 – 2015-02-11 09:18:29

回答

5

与正则表达式模块的方式:

正如评论解释了正则表达式模块允许重复使用命名的子模式。不幸的是,在Perl或PCRE中没有类似于(?(DEFINE)...)的语法。

所以方法是使用相同的解决方法比一个由把一个{0}量词时,要定义一个名为子模式Ruby语言:

import regex 

s = r''' 
// NOTE: A small example 
// HACK: Another example <ABC 2014-02-03> 
// HACK: Another example <ABC,DEF 2014-02-03> 
// HACK: Another example <ABC,DEF p:0> 
''' 

p = r''' 
    # subpattern definitions 
    (?<TAG> \b(?:NOTE|LEGACY|HACK|TODO|FIXME|XXX|BUG)){0} 
    (?<DATE> \d{4}-\d{2}-\d{2}){0} 
    (?<FLAGS> [pts]:\w+){0} 

    # main pattern 
    (?<tag> (?&TAG)) : \s* 
    (?<message> (?>[^\s<]+[^\n\S]+)* [^\s<]+)? \s* # to trim the message 
    < 
    (?<author> (?: \w{3} \s* , \s*)*+ \w{3})? \s* 
    (?<date> (?&DATE))? 
    (?<flags> (?&FLAGS))? 
    > 
    $ 
''' 

rgx = regex.compile(p, regex.VERBOSE | regex.MULTILINE) 

for m in rgx.finditer(s): 
    print (m.group('tag')) 

注:该子模式可以在年底确定的模式也是如此。

+1

我不喜欢这种解决方案的一件事是,它污染'groups/groupdict'与永不匹配的子模式,但是如果你只对特定的命名组感兴趣,这是没有问题的。顺便说一句,'regex'支持直接'm ['tag']',不需要'.groups('tag')'。 – georg 2015-02-11 18:41:55

+0

@georg:是的,但是请注意,如果您在PHP中使用'(?(DEFINE)...)',或者在任何失败或未经测试的分支中捕获任何数据,您将会遇到同样的问题。另一种方法是在占位符中使用一个C形式的字符串,但是当你有太多不同的子模式时,这并不方便,因为在这种情况下,你根本不使用名字。 – 2015-02-11 20:33:08

+0

对于非常复杂的正则表达式,我个人更喜欢“迷你语法”(例如[here](http://stackoverflow.com/a/28043277/989121)) - 这是独立于供应商的,并且没有令人不快的副作用。 – georg 2015-02-12 09:22:24

1
(?P<tag>\b(?:NOTE|LEGACY|HACK|TODO|FIXME|XXX|BUG)):\s* 
(?P<message>.*?) 
(
< 
    (?P<author>(?:\w{3}\s*,\s*)*\w{3})?\s* 
    (?P<date>\d{4}-\d{2}-\d{2})? 
    (?P<flags>[pts]:\w+\b)? 
> 
)? 
$ 

您可以将替换标签定义替换为解决方法。请参阅演示。

https://regex101.com/r/qH1uG3/2

+1

当然,这是一个可行的解决方案,但它并没有真正的帮助。除了内联所有东西外,还有其他DEFINE语句吗? – nowox 2015-02-11 09:17:31

+0

@Coin是的,你可以让你的're'在飞行中。在某些变量中存储'tag'的定义,让你像re.compile(r“aasdsad”+ tag +“asdsad”)'这样的东西 – vks 2015-02-11 09:18:56

+0

我害怕这个答案。这是我不想看到的问题的解决方案。 – nowox 2015-02-11 09:21:56

1

作为速战速决,把你定义在一个字典:

defines = { 
    'TAG': r'\b(NOTE|LEGACY|HACK|TODO|FIXME|XXX|BUG)', 
    'DATE': r'\d{4}-\d{2}-\d{2}', 
    'FLAGS': r'[pts]:\w+\b' 
} 

,并在您的正则表达式替换它们:

regex = re.sub(r'\(\?&(\w+)\)', lambda m: defines[m.group(1)], regex) 

如果你有递归定义的,换行在一个循环中:

define = r'\(\?&(\w+)\)' 
while re.search(define, regex): 
    regex = re.sub(define, lambda m: defines[m.group(1)], regex) 

一个不太快的解决方法是编写你自己的重新解析器 - 编译器 - 但这对于手头的任务来说几乎肯定是一种矫枉过正。

+0

对于不同的标签,re.sub' \(\?&(\ w +)\)里面的函数可能会有所不同。在这种情况下,我会猜想 – vks 2015-02-11 09:44:16

+0

@ vks:不确定你在这里的意思...... care to阐述? – georg 2015-02-11 09:57:39

+0

这个模型会工作re.sub捕获所有标签correclty.So所有标签必须是unifom类型,以便一个通用函数可以捕获并替换。但是其他的可以不同。 – vks 2015-02-11 10:03:14

相关问题