регулярное выражение соответствует части пути к файлу, если ключ отсутствует

Я пытаюсь сопоставить часть пути к файлу, если он не включает определенное ключевое слово, используя регулярные выражения в python. Например, применение регулярного выражения к «/exclude/this/test/other» не должно совпадать, тогда как «/this/test/other» должно возвращать путь к файлу, исключая «other», то есть «/this/test», и где «другое» — любой каталог. Пока я использую это

In [153]: re.findall("^(((?!exclude).)*(?=test).*)?", "/exclude/this/test/other")
Out[153]: [('', '')]

re.findall("^(((?!exclude).)*(?=test).*)?", "/this/test/other")
Out[152]: [('/this/test/other', '/')]

но я не могу заставить его прекратить сопоставление после «теста», также есть несколько пустых совпадений. Любые идеи?


person user588241    schedule 18.10.2012    source источник
comment
То, что вы ищете, всегда будет фиксированной строкой или это может быть шаблон?   -  person Bryan Oakley    schedule 18.10.2012


Ответы (3)


Вы получаете дополнительный результат, потому что (1) вы используете findall() вместо search() и (2) вы используете группы захвата вместо незахвата

>>> import re
>>> re.search(r'^(?:(?:(?!exclude).)*(?=test)*)$', "/this/test").group(0)
'/this/test'

Это будет работать и с findall(), но это не имеет смысла, когда вы сопоставляете всю строку. Что еще более важно, часть include вашего регулярного выражения не работает. Проверь это:

>>> re.search(r'^(?:(?:(?!exclude).)*(?=test)*)$', "/this/foo").group(0)
'/this/foo'

Это потому, что * в (?=test)* делает просмотр вперед необязательным, что делает его бессмысленным. Но избавление от * на самом деле не является решением, потому что exclude и test могут быть частью более длинных слов, таких как excludexx или yyytest. Вот лучшее регулярное выражение:

r'^(?=.*/test\b)(?!.*/exclude\b)(?:/\w+)+$'

проверено:

>>> re.search(r'^(?=.*/test\b)(?!.*/exclude\b)(?:/\w+)+$', '/this/test').group()
'/this/test'
>>> re.search(r'^(?=.*/test\b)(?!.*/exclude\b)(?:/\w+)+$', '/this/foo').group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'

РЕДАКТИРОВАТЬ: я вижу, вы исправили проблему "необязательный просмотр вперед", но теперь все регулярное выражение является необязательным!

EDIT: Если вы хотите, чтобы совпадения прекратились после /test, попробуйте следующее:

r'^(?:/(?!test\b|exclude\b)\w+)*/test\b'

(?:/(?!test\b|exclude\b)\w+)* соответствует нулю или более компонентам пути, если они не равны /test или /exclude.

person Alan Moore    schedule 18.10.2012
comment
Большое спасибо за ваше объяснение и извините за мои плохие навыки регулярных выражений. Похоже, он делает то, что мне нужно, но как я могу заставить его прекратить сопоставление после теста, чтобы другое не совпадало? - person user588241; 18.10.2012
comment
Это на самом деле делает проблему немного проще. Смотрите мою правку. - person Alan Moore; 18.10.2012

просто используйте in, если вам нужно только проверить наличие ключевого слова:

In [33]: s1="/exclude/this/test"

In [34]: s2="this/test"

In [35]: 'exclude' in s1
Out[35]: True

In [36]: 'exclude' in s2
Out[36]: False

РЕДАКТИРОВАТЬ: или если вам нужен только путь до теста:

if 'exclude' not in s:
    re.findall(r'(.+test)',s)
person root    schedule 18.10.2012
comment
Спасибо, но я понимаю, что неправильно сформулировал свой вопрос, я отредактировал его, чтобы прояснить ситуацию. - person user588241; 18.10.2012
comment
отредактировал ответ, чтобы получить путь только к «тесту», так как это, кажется, то, что вы хотите. еще проще сделать все это с помощью регулярных выражений... - person root; 18.10.2012

Если ваше совпадение более сложное, чем можно было бы сделать с помощью in и простого ключевого слова, оно может быть более понятным, если вы сделали два регулярных выражения:

import re
s1="/exclude/this/test"
s2="this/test"

for s in (s1,s2):
    if re.search(r'exclude',s): 
        print 'excluding:',s
        continue
    print s, re.findall(r'test',s)

Отпечатки:

excluding: /exclude/this/test
this/test ['test']

Вы можете сделать два регулярных выражения компактными, если это ваша цель:

print [(s,re.findall(r'test',s)) for s in s1,s2 if not re.search(r'exclude',s)]

Изменить

Если я понимаю ваше редактирование, это работает:

s1="/exclude/this/test/other"
s2="/this/test/other"

print [(s,re.search(r'(.*?)/[^/]+$',s).group(1)) for s in s1,s2 if not re.search(r'exclude',s)]

Отпечатки:

[('/this/test/other', '/this/test')]
person Community    schedule 18.10.2012
comment
Спасибо, но это не совсем то, что я пытаюсь сделать, я соответствующим образом перефразировал свой вопрос. - person user588241; 18.10.2012