Я пытаюсь получить регулярное выражение, которое будет соответствовать:
somefile_1.txt
somefile_2.txt
somefile_{anything}.txt
но не совпадать:
somefile_16.txt
Я старался
somefile_[^(16)].txt
не повезло (включает даже запись "16")





Некоторые библиотеки регулярных выражений позволяют смотреть вперед:
somefile(?!16\.txt$).*?\.txt
В противном случае вы все равно можете использовать несколько классов символов:
somefile([^1].|1[^6]|.|.{3,})\.txt
или, чтобы добиться максимальной переносимости:
somefile([^1].|1[^6]|.|....*)\.txt
[^(16)] означает: соответствует любому символу, кроме фигурных скобок, 1 и 6.
@ Мартин Браун: Почему? . *? афаик недействителен для большинства диалектов. . означает любой символ, * ноль или более вхождений. Что должен делать знак вопроса?
@phihag:. *? означает сделать. * нежадным. Это особое использование вопросительного знака.
@Ben Doom, @Martin Brown: Спасибо за разъяснения, добавил
Ваши «несколько классов символов» («somefile ([^ 1] [^ 6] |. |. {3,}) \. Txt») и «максимальная переносимость» («somefile ([^ 1] [^ 6] | . | .... *) \. txt ") регулярные выражения очень неправильные. Попробуйте сопоставить такие строки, как «somefile19.txt», «somefile46.txt» и «somefile1654.txt».
@ Маттиас Андерссон: Спасибо, что указали. Вы правы, они были неправы.
somefile_(?!16).*\.txt
(?! 16) означает: Утверждение, что невозможно сопоставить регулярное выражение "16", начиная с этой позиции.
Это сломается, если {что-нибудь} содержит точку: somefile_19700101.archive.txt не будет соответствовать.
Уже упоминалось лучшее решение:
somefile_(?!16\.txt$).*\.txt
Это работает, и достаточно жадно, чтобы отреагировать на все, что идет к нему по той же линии. Однако, если вы знаете, что вам нужно допустимое имя файла, я бы предложил также ограничить недопустимые символы:
somefile_(?!16)[^?%*:|"<>]*\.txt
Если вы работаете с механизмом регулярных выражений, который не поддерживает просмотр вперед, вам нужно подумать, как это сделать! 16. Вы можете разделить файлы на две группы: те, которые начинаются с 1 и за которыми не следует 6, и те, которые начинаются с чего-либо еще:
somefile_(1[^6]|[^1]).*\.txt
Если вы хотите разрешить somefile_16_stuff.txt, но НЕ somefile_16.txt, этих регулярных выражений будет недостаточно. Вам нужно будет установить свой лимит по-другому:
somefile_(16.|1[^6]|[^1]).*\.txt
Объедините все это, и вы получите две возможности: одна блокирует единственный экземпляр (somefile_16.txt), а другая блокирует все семейства (somefile_16 * .txt). Я лично думаю, что вы предпочитаете первый:
somefile_((16[^?%*:|"<>]|1[^6?%*:|"<>]|[^1?%*:|"<>])[^?%*:|"<>]*|1)\.txt
somefile_((1[^6?%*:|"<>]|[^1?%*:|"<>])[^?%*:|"<>]*|1)\.txt
В версии без удаления спецсимволов, чтобы было легче читать:
somefile_((16.|1[^6]|[^1).*|1)\.txt
somefile_((1[^6]|[^1]).*|1)\.txt
Чтобы строго подчиняться вашей спецификации и быть разборчивым, вам лучше использовать:
^somefile_(?!16\.txt$).*\.txt$
так что somefile_1666.txt, который {что угодно} может быть сопоставлен;)
но иногда его просто удобнее использовать ...:
ls | grep -e 'somefile_.*\.txt' | grep -v -e 'somefile_16\.txt'
Иногда проще использовать два регулярных выражения. Сначала ищите все, что хотите, а затем игнорируйте все, что вам не нужно. Я делаю это все время в командной строке, где я передаю регулярное выражение, которое получает надмножество, в другое регулярное выражение, которое игнорирует вещи, которые мне не нужны.
Если цель состоит в том, чтобы выполнить работу, а не найти идеальное регулярное выражение, рассмотрите этот подход. Часто намного проще писать и понимать, чем регулярное выражение, использующее экзотические функции.
Без использования опережающего просмотра
somefile_(|.|[^1].+|10|11|12|13|14|15|17|18|19|.{3,}).txt
Прочтите это так: somefile_, за которым следует:
1, за которым следуют любые другие символы.10 .. 19, обратите внимание, что 16 был исключен.и, наконец, .txt.
Вы, наверное, хотите? там вот так: somefile (?! 16). *? \. txt