如何从预定义块构建正则表达式

时间:2013-01-29 11:41:48

标签: python regex

我想基于模板和一组预定义块构建正则表达式,并使用string.Template进行替换。

例如:

  • 模板:/data/${year}_${month}_${day}/${year}${month}${day}_${type}_${id}.dat
  • 块:
    • 天:(?P<day>\d{2})
    • 月:(?P<month>\d{2})
    • 年:(?P<year>\d{4})
    • 输入:(?P<typechar>[BDPCLNIYSQJ])
    • id:(?P<id>\d{8})

>>> string.Template(template).safe_substitute(blocks)

/data/(?P<year>\d{4})_(?P<month>\d{2})_(?P<day>\d{2})/(?P<year>\d{4})(?P<month>\d{2})(?P<day>\d{2})_(?P<typechar>[BDPCLNIYSQJ])_(?P<id>\d{8}).dat

问题在于重复的名称组,正则表达式中不接受这些名称组。

我正在寻找一种方法来纠正模板(在替换之前或之后),一种欺骗重复的方法,或一种全新的问题解决方法。

4 个答案:

答案 0 :(得分:2)

我不确定Python,但PCRE和Perl都支持(?(DEFINE)...)构造。所以你可以使用这样的东西

(?x) 
(?(DEFINE)
    (?<date>        (?&long_date) | (?&short_date))
    (?<long_date>   (?&year) _ (?&month) _ (?&day) _ (?&type) _ (?&id))
    (?<short_date>  (?&year) _ (?&month) _ (?&day))
    (?<day>         \d{2})
    (?<month>       \d{2})
    (?<year>        \d{4})
    (?<type>        [BDPCLNIYSQJ])
    (?<id>          \d{8})
)
(?&date)

我使用了“x”修饰符(?x)只是为了使正则表达式更具可读性(现在正则表达式中的空白区域被忽略)。

  

形式(?(DEFINE)...)的“条件组”可用于定义   从不评估内联的组(命名和编号),但可以   被称为来自其他地方的“子程序”。实际上,DEFINE   条件总是假的。在这样的情况下,可能只有一种替代方案   基。

http://www.pcre.org/changelog.txt

答案 1 :(得分:0)

摆脱?P名称元素。 即

day: (?P<name>\d{2})

变为

day: (\d{2})

我从来没有在tbh之前使用过?P功能

你的正则表达式模板想法很好!

答案 2 :(得分:0)

应用模板两次,一次设置名称然后使用输出来制作最终的正则表达式

day='(?P<$dayname>\d{2})'
d=dict(dayname='day_start')
Template(day).safe_substitute(d)

重复您需要的所有名称,然后将它们全部输入到使用day1 day2等的最终模板

答案 3 :(得分:0)

按照朋友的建议,我找到了达到预期效果的方法。

想法是在替换正则表达式块之前修改模板字符串以消除重复的变量。事实上,它不是删除重复项,而是使用(?P = name)语法对第一个引用替换它们。这样,您可以在使用该块的任何位置强制内容相同。

我将假设正则表达式组名与模板块名相同。在问题示例中并非如此,但可以毫无问题地进行更改。

要转换重复项,我使用以下函数:

>>> def remove_duplicate_blocks(template):
        regex = '\$\{([\w]+)\}'
        def alt_seen(matchobj):
            x = matchobj.group(1)
            if x not in seen and not seen_add(x): return '${%s}' % x
            else: return '(?P=%s)' % x
        seen = set()
        seen_add = seen.add
        return re.sub(regex, alt_seen, template)

返回转换后的模板,没有重复项,并强制所有类似的块具有相同的内容。

之后只需要更换块

>>> unique_blocks_template = remove_duplicate_blocks(template)
>>> print unique_blocks_template
/data/${year}_${month}_${day}/(?P=year)(?P=month)(?P=day)_${type}_${id}.dat

>>> string.Template(unique_blocks_template).safe_substitute(blocks)
'/data/(?P<year>\\d{4})_(?P<month>\\d{2})_(?P<day>\\d{2})/(?P=year)(?P=month)(?P=day)_(?P<type>[BDPCLNIYSQJ])_(?P<id>\\d{8}).dat'

问题中没有提到,但同样的原始模板也可用于重建我们想要与正则表达式匹配的字符串,这是此代码的最初目的。