2

我想基于模板和一组预定义块构建正则表达式,并使用 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

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

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

4

4 回答 4

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

于 2013-01-29T12:30:23.520 回答
0

摆脱 ?P 名称元素。IE

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

变成

day: (\d{2})

在 tbh 之前我从未使用过 ?P 功能

不过,您的正则表达式模板想法很好!

于 2013-01-29T11:49:00.370 回答
0

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

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

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

于 2013-01-29T12:15:31.543 回答
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'

问题中没有提到,但是同样的原始模板也可以用来重构我们想要与正则表达式匹配的字符串,这就是这段代码的初衷。

于 2013-01-29T22:57:33.353 回答