如何将带命名组的Perl正则表达式转换为Python?

3 投票
4 回答
1022 浏览
提问于 2025-04-15 12:31

我正在尝试把我在Video::Filename这个Perl模块中找到的正则表达式,转换成Python 2.5.4的正则表达式,用来解析文件名。

# Perl > v5.10
re => '^(?:(?<name>.*?)[\/\s._-]*)?(?<openb>\[)?(?<season>\d{1,2})[x\/](?<episode>\d{1,2})(?:-(?:\k<season>x)?(?<endep>\d{1,2}))?(?(<openb>)\])(?:[\s._-]*(?<epname>[^\/]+?))?$',

我也想使用命名组,我知道在Python中,命名组的正则表达式写法和Perl不一样,但我对具体的语法不是特别确定。

这是我尝试过的:

# Python (not working)
r = re.compile(r'^(?:(?P<name>.*?)[\/\s._-]*)?(?P<openb>\[)?(?P<season>\d{1,2})[x\/](?P<episode>\d{1,2})(?:-(?:\kP<season>x)?(?P<endep>\d{1,2}))?(?(P<openb>)\])(?:[\s._-]*(?P<epname>[^\/]+?))?$')

我遇到的错误是:

   raise error, v # invalid expression
sre_constants.error: bad character in group name

举个例子,我有一个正则表达式成功转换了,并且可以正常工作。但上面的那个我总是搞不对。在Python中出现了编译错误。

# Perl:
re => '^(?:(?<name>.*?)[\/\s._-]+)?(?:s|se|season|series)[\s._-]?(?<season>\d{1,2})[x\/\s._-]*(?:e|ep|episode|[\/\s._-]+)[\s._-]?(?<episode>\d{1,2})(?:-?(?:(?:e|ep)[\s._]*)?(?<endep>\d{1,2}))?(?:[\s._]?(?:p|part)[\s._]?(?<part>\d+))?(?<subep>[a-z])?(?:[\/\s._-]*(?<epname>[^\/]+?))?$',

# Python (working):
r = re.compile(r'^(?:(?P<name>.*?)[\/\s._-]+)?(?:s|se|season|series)[\s._-]?(?P<season>\d{1,2})[x\/\s._-]*(?:e|ep|episode|[\/\s._-]+)[\s._-]?(?P<episode>\d{1,2})(?:-?(?:(?:e|ep)[\s._]*)?(?P<endep>\d{1,2}))?(?:[\s._]?(?:p|part)[\s._]?(?P<part>\d+))?(?P<subep>[a-z])?(?:[\/\s._-]*(?P<epname>[^\/]+?))?$')

我不知道该从哪里开始查找问题。

4 个回答

0

这些正则表达式真是让人觉得脑洞大开... :-)

不过,(?()) 在 Python 和 Perl 里都是用来表示条件的,而上面的 Perl 语法看起来应该和 Python 的语法一样,也就是说,它会判断名为 exists 的组是否存在。

那么,我们该从哪里开始查找呢?这些模块的文档在这里:

http://docs.python.org/library/re.html http://www.perl.com/doc/manual/html/pod/perlre.html

2

我找到了出问题的地方,但我搞不清楚到底哪里出了错,因为我还没完全理解整个情况。

r = re.compile(r'^(?:(?P<name>.*?)[\/\s._-]*)?(?P<openb>\[)?(?P<season>\d{1,2})[x\/](?P<episode>\d{1,2})(?:-(?:\kP<season>x)?(?P<endep>\d{1,2}))?

(?(P<openb>)\]) // this part here causes the error message

(?:[\s._-]*(?P<epname>[^\/]+?))?$')

问题似乎出在Python中的组名必须是有效的Python标识符(可以查看文档)。看起来括号是个问题。去掉括号后,结果是

(?(P<openb>)\]) //with parentheses
(?P<openb>\])   //without parentheses

redefinition of group name 'openb' as group 6; was group 2
6

你的翻译有两个问题。首先,第二次提到的 openb 周围多了括号,这让它变成了一个 条件表达式,而不是一个命名表达式。

接下来是你没有翻译 \k<season> 这个 反向引用,在Python中用 (P=season) 来匹配同样的东西。下面的代码对我来说是可以编译的:

r = re.compile(r'^(?:(?P<name>.*?)[\/\s._-]*)?(?P<openb>\[)?(?P<season>\d{1,2})[x\/](?P<episode>\d{1,2})(?:-(?:(?P=season)x)?(?P<endep>\d{1,2}))?(?(openb)\])(?:[\s._-]*(?P<epname>[^\/]+?))?$')

如果我是你,我会使用 re.VERBOSE 将这个表达式分成多行,并添加大量的注释,这样将来如果需要维护的话,你能更好地理解这个表达式。

(在意识到第二个 openb 引用是一个条件表达式后进行了编辑,并且正确翻译了反向引用)。

撰写回答