Python正则表达式 - 从右到左

时间:2013-09-12 01:26:42

标签: python regex

我试图在python中使用正则表达式来匹配图像序列中图像文件的帧编号组件。我想提出一个涵盖许多不同命名约定的解决方案。如果我把它写成单词,我试图匹配两个点之间的一个或多个数字的最后一个实例(例如.0100。)。下面是我当前逻辑如何下降的一个例子:

import os
import re    

def sub_frame_number_for_frame_token(path, token='@'):
    folder = os.path.dirname(path)
    name = os.path.basename(path)
    pattern = r'\.(\d+)\.'
    matches = list(re.finditer(pattern, name) or [])
    if not matches:
        return path

    # Get last match.
    match = matches[-1]
    frame_token = token * len(match.group(1))
    start, end = match.span()
    apetail_name = '%s.%s.%s' % (name[:start], frame_token, name[end:])
    return os.path.join(folder, apetail_name)

# Success
eg1 = 'xx01_010_animation.0100.exr'
eg1 = sub_frame_number_for_frame_token(eg1) # result: xx01_010_animation.@@@@.exr

# Failure
eg2 = 'xx01_010_animation.123.0100.exr'
eg2 = sub_frame_number_for_frame_token(eg2) # result: xx01_010_animation.@@@.0100.exr

我意识到还有其他方法可以解决这个问题(我已经实现了一个解决方案,我在点上分割路径并取最后一项是一个数字)但我借此机会学习关于正则表达式的东西正则表达式似乎从左到右创建组,并且不能多次使用模式中的字符。无论如何,首先是从右到左搜索字符串?其次,为什么模式在eg2(123和0100)中找不到两个匹配?

干杯

4 个答案:

答案 0 :(得分:2)

finditer将在字符串中的所有非重叠匹配上返回一个迭代器“。

在您的示例中,第一个匹配的最后一个.将“消耗”第二个匹配的第一个.。基本上,在进行第一次匹配后,eg2示例的剩余字符串为0100.exr,但不匹配。

为避免这种情况,您可以使用lookahead assertion?=),它不会消耗第一个匹配项:

>>> pattern = re.compile(r'\.(\d+)(?=\.)')

>>> pattern.findall(eg1)
['0100']

>>> pattern.findall(eg2)
['123', '0100']

>>> eg3 = 'xx01_010_animation.123.0100.500.9000.1234.exr'
>>> pattern.findall(eg3)
['123', '0100', '500', '9000', '1234']
# and "right to left"
>>> pattern.findall(eg3)[::-1]
['1234', '9000', '500', '0100', '123']

答案 1 :(得分:1)

我的解决方案使用了一种非常简单的修复方法。它会在函数开头反转字符串path,并在其结尾处反转返回值。它基本上使用正则表达式来搜索给定字符串的向后版本。哈金,但它的工作原理。我使用this question中显示的语法来反转字符串。

import os
import re    

def sub_frame_number_for_frame_token(path, token='@'):
    path = path[::-1]
    folder = os.path.dirname(path)
    name = os.path.basename(path)
    pattern = r'\.(\d+)\.'
    matches = list(re.finditer(pattern, name) or [])
    if not matches:
        return path

    # Get last match.
    match = matches[-1]
    frame_token = token * len(match.group(1))
    start, end = match.span()
    apetail_name = '%s.%s.%s' % (name[:start], frame_token, name[end:])
    return os.path.join(folder, apetail_name)[::-1]

# Success
eg1 = 'xx01_010_animation.0100.exr'
eg1 = sub_frame_number_for_frame_token(eg1) # result: xx01_010_animation.@@@@.exr

# Failure
eg2 = 'xx01_010_animation.123.0100.exr'
eg2 = sub_frame_number_for_frame_token(eg2) # result: xx01_010_animation.123.@@@@.exr

print(eg1)
print(eg2)

答案 2 :(得分:0)

我认为问题是finditer只返回非重叠匹配。因为两个'。'字符是正则表达式的一部分,它不会将第二个点视为另一个匹配的可能开始。您可以使用前瞻构造?=匹配第二个点而不使用“?=。”。

由于正则表达式的工作方式,我认为没有一种简单的方法可以从右向左搜索(尽管我认为你可以反转字符串并向后编写模式......)。

答案 3 :(得分:0)

如果你关心的只是 last \.(\d+)\.,那么从字符串的末尾锚定你的模式并做一个简单的re.search(_):
  \.(\d+)\.(?:.*?)$
其中(?:.*?)是非捕获和非贪婪的,因此它将在您的真实目标和字符串结尾之间尽可能少地使用几个字符,并且这些字符不会显示在matches
(警告1:我没有测试过这个。警告2:这是一个丑陋的正则表达式,所以添加一个评论来解释它正在做什么。)
更新:实际上我想你可以做一个^.*(\.\d\.)并让隐含贪婪的.*尽可能地匹配(包括字符串中较早出现的匹配),同时仍然匹配你的组。这使得一个更简单的正则表达式,但我认为它使你的意图不那么明确。