2013-09-12 2 views
1

Я пытаюсь использовать регулярные выражения в python для соответствия компоненту номера кадра файла изображения в последовательности изображений. Я хочу придумать решение, которое охватывает несколько различных соглашений об именах. Если я помещаю это в слова, я пытаюсь сопоставить последний экземпляр одного или нескольких чисел между двумя точками (например, .0100.). Ниже приведен пример того, как моя текущая логика падает:Python Regular Expression - справа налево

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: [email protected]@@@.exr 

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

Я понимаю, что есть и другие способы, в которых я могу решить эту проблему (я уже реализовал решение, в котором я разделив путь на точке и принимая последний элемент, который является числом), но я пользуюсь этой возможностью, чтобы узнать что-то о регулярных выражениях. Кажется, что регулярное выражение создает группы слева направо и не может использовать символы в шаблоне более одного раза. Во-первых, так или иначе, чтобы искать строку справа налево? Во-вторых, почему шаблон не находит два совпадения в eg2 (123 и 0100)?

Приветствия

+0

Это лучше? 'pattern = r '\. (\ d + \.) +'' – hughdbrown

+0

@hughdbrown Это не его проблема ... найдите 'finditer'. Anyways @ Yani, вы должны искать 'finditer' тоже. :) Он возвращает итератор по всем * неперекрывающимся * совпадениям. Его вторая строка - 'xx01_010_animation.123.0100.exr''. Список, называемый 'match', возвращает только 1 элемент, потому что совпадения совпадают. – Shashank

+0

Я не уверен, что понимаю код, который вы даете, но моя первая мысль для поиска строки справа налево - это изменить строку ('example [:: - 1]'). Поскольку ваш шаблон ('\. (\ D +) \.') Является симметричным, похоже, что он сработает. Этот вариант уже исключен? –

ответ

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'] 
+0

Отлично. Это то, чего я хочу. Думаю, мне следовало бы прочитать документы немного внимательнее, но оценить практический пример. Благодаря! – Yani

0

Я считаю, что проблема в том, что finditer возвращает только неперекрывающиеся совпадения. Потому что оба. символы являются частью регулярного выражения, он не рассматривает вторую точку как возможное начало другого совпадения. Вероятно, вы можете использовать конструкцию lookahead? =, Чтобы совместить вторую точку, не потребляя ее с помощью «? =.».

Из-за того, как работают регулярные выражения, я не думаю, что есть простой способ поиска справа налево (хотя, я полагаю, вы могли бы изменить строку и написать шаблон назад).

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: [email protected]@@@.exr 

# Failure 
eg2 = 'xx01_010_animation.123.0100.exr' 
eg2 = sub_frame_number_for_frame_token(eg2) # result: [email protected]@@@.exr 

print(eg1) 
print(eg2) 
0

Если все, что вы заботитесь о является последний\.(\d+)\., а затем прикрепить шаблон с конца строки и сделать простой re.search (_):
\.(\d+)\.(?:.*?)$
где (?:.*?) является не- захватывающий и не жадный, поэтому он будет потреблять как несколько символов, которые возможны между вашей реальной мишенью и концом строки, и эти символы не будут отображаться в matches.
(Caveat 1: Я не тестировал это. Caveat 2: Это одно уродливое регулярное выражение, поэтому добавьте комментарий, объясняющий, что он делает.)
ОБНОВЛЕНИЕ: На самом деле, я думаю, вы могли бы просто сделать ^.*(\.\d\.) и позволить неявно жадным .* соответствовать как можно больше (включая совпадения, которые встречаются ранее в строке), в то же время соответствуя вашей группе. Это упрощает регулярное выражение, но я думаю, что ваши намерения менее ясны.