отрицательное утверждение lookahead не работает в python

задачи:
- дано: список изображений filenames
- todo: создайте новый список с именами файлов, не содержащими слово "thumb" -т. е. только целевые изображения без миниатюр (с библиотекой изображений Pil - Python).

Я пробовал r".*(?!thumb).*" но это не удалось.

я нашел решение (здесь, в stackoverflow), чтобы добавить ^ в regex и поставить .* в отрицательный lookahead:r"^(?!.*thumb).*" и теперь это работает.

дело в том, что я хотелось бы понять, почему мое первое решение не сработало, но я этого не делаю. Поскольку regexes достаточно сложны, я бы очень хотел их понять.

что я понимаю, так это то, что ^ сообщает синтаксическому анализатору, что следующее условие должно совпадать в начале строки. Но разве .* в первом примере (не работает) также начинается в начале строки? Я думал, что он начнется в начале строки и будет искать столько символов как это может до достижения "большого пальца". Если это так, он вернет несоответствие.

может кто-нибудь объяснить, почему r".*(?!thumb).*" не работает, но r"^(?!.*thumb).*" делает?

спасибо!

3 ответов


(черт, Джон избил меня. Ну что ж, вы все равно можете посмотреть на примеры)

как говорили другие ребята, регулярное выражение-не лучший инструмент для этой работы. Если вы работаете с filepaths, взгляните на os.путь.

что касается фильтрации файлов, которые вы не хотите, вы можете сделать if 'thumb' not in filename: ... после того, как вы рассекали путь (где filename - это str).

и для потомков, вот мои мысли на эти выражения. r".*(?!thumb).*" не работает, потому что .* жадный, и lookahead получает очень низкий приоритет. Взгляните на это:

>>> re.search('(.*)((?!thumb))(.*)', '/tmp/somewhere/thumb').groups()
('/tmp/somewhere/thumb', '', '')
>>> re.search('(.*?)((?!thumb))(.*)', '/tmp/somewhere/thumb').groups()
('', '', '/tmp/somewhere/thumb')
>>> re.search('(.*?)((?!thumb))(.*?)', '/tmp/somewhere/thumb').groups()
('', '', '')

последний довольно странный...

другое выражение (r"^(?!.*thumb).*") работает, потому что .* находится внутри lookahead, поэтому у вас нет никаких проблем с украденными персонажами. На самом деле вам даже не нужно ^ в зависимости от того, если вы используете re.match или re.search:

>>> re.search('((?!.*thumb))(.*)', '/tmp/somewhere/thumb').groups()
('', 'humb')
>>> re.search('^((?!.*thumb))(.*)', '/tmp/somewhere/thumb').groups()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'groups'
>>> re.match('((?!.*thumb))(.*)', '/tmp/somewhere/thumb').groups()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'groups'

может кто-нибудь объяснить, почему r".*(?!thumb).*" не работает, но r"^(?!.*thumb).*" делает?

первый всегда будет соответствовать как .* будет потреблять всю строку (поэтому за ней не может последовать ничего, чтобы отрицательный lookahead потерпел неудачу). Второй немного запутан и будет соответствовать с самого начала строки, наибольшее количество символов, пока он не встретит "большой палец", и если это присутствует, то весь матч терпит неудачу, так как строка начинается с чего-то за ним следует "палец".

номер два-легче написать так:

  • 'thumb' not in string
  • not re.search('thumb', string) (вместо матча)

также, как я упоминал в комментариях, ваш вопрос говорит:

имена, не содержащие слово "большой палец"

таким образом, вы можете рассмотреть вопрос о том,thumbs up должен быть исключен или нет.


игнорируя все биты о регулярных выражениях, ваша задача кажется довольно простой:

  • дано: список имен файлов изображений
  • todo: создайте новый список с именами файлов, не содержащими слово "thumb" - т. е. только целевые изображения без миниатюр (с Pil-Python Библиотека Изображений).

Если у вас есть список файлов, который выглядит примерно так:

filenames = [ 'file1.jpg', 'file1-thumb.jpg', 'file2.jpg', 'file2-thumb.jpg' ]

тогда вы можете получить список файлов не содержащий слово thumb, как это:

not_thumb_filenames = [ filename for filename in filenames if not 'thumb' in filename ]

Это то, что мы называем понимание, и является, по сути, сокращенный вариант:

not_thumb_filenames = []
for filename in filenames:
  if not 'thumb' in filename:
    not_thumb_filenames.append(filename)

регулярные выражения на самом деле не нужны для этой простой задачи.