А вы знали, что если вставлять скопированный текст из буфера сюда на Капибару, то по Ctrl+Shift+V он вставляется с автоматическими переносами строк?
И не надо потом весь текст сканить и вставлять переводы строк в нужных местах.
Плюс к этому: в винде это стандартная комбинация "Вставить без форматирования". Например скопировали в ворде или на сайте сайт со ссылками, заголовками и другими финтифлюшками; Ctrl+Shift+V - все финтифлюшки исчезают.
Записка адресована тем, кто приходит в ужас при виде регулярных выражений или понятия не имеет что это и зачем. Содержит упрощенное описание и не претендует на полноту.
Каждое приложение реализует поддержку масок и регулярных выражений так, как ему захочется, и несмотря на то, что стандарт, общие принципы и низкоуровневая реализация практически всегда одинаковые, в случае неожиданного поведения стоит ознакомиться с документацией или хотя бы попросить ChatGPT привести примеры валидного использования.
Маски пути (wildcards) используются для описания файлов на диске для которых невозможно задать полное имя. Например, если мы хотим найти все исполняемые файлы в Windows, маской будет *.exe.
Классические маски позволяют использовать символы ? и * вместо реальных символов. ? - обозначает любой символ (один!), а * - любое число любых символов (0 символов тоже подходит).
????.* - любые файлы из 4 символов с любым расширением: abcd.jpeg
a* - любые файлы, которые начинаются на 'a': abc, arthas.jpg
*b* - любые файлы, которые содержат 'b': abc, mask.black
Большинство приложений учитывают только имя файла при проверке на маску, но не путь к нему. Поэтому маски вида C:\*virus.exe могут не находить ничего, даже если на диске C:\ есть файл с именем virus.exe. Однако каждое приложение может иметь собственные правила, описанные в документации. Например, GIT предоставляет расширенный синтаксис, который позволяет работать с каталогами: src/**/*.cs - все файлы .cs в любых каталогах в папке src; а файловые менеджер FAR позволяет указать несколько масок: *.mkv;*.avi.
Windows Explorer по умолчанию скрывает расширения файлов. Для их отображения нужно поставить галочку в меню Вид -> Расширения имён файлов
Регулярные выражения (regular expressions) - являются более мощным инструментом работы с текстовыми данными и позволяет искать произвольные последовательности символов, разбивать их на группы и использовать эти группы в дальнейшем. Для поиска текста с использованием регулярных выражений вам понадобится редактор, который их поддеживает. Например, Notepad++ и VSCode под Windows.
Маски чаще всего работают только с файлами, регулярные выражения чаще всего работают только с текстом, большинство программ, которые умеют работать с масками не готовы к тому, что вы скормите им регулярные выражения и наоборот. Но вы всегда можете почитать документацию или проверить это. Чаще всего, если приложение поддерживает работу и с тем и с другим, вначале и конце регулярного выражения должен стоять символ /
Возможности регулярных выражений огромны, но мы поговорим о самых базовых из них, которые и необходимы и достаточны в 95% случаев. Для тестирования и отладки ваших регулярок, настоятельно рекомендую сайт regex101 - это мощный визульный редактор, который отображает - как ваши регулярки мапятся на примеры вашего же текста.
Начнём с простого регулярного выражения: alo.*
' alo ' - это просто символы, которые должны быть в строке
' . ' - точка указывает на любой символ, кроме переноса строки
' * ' - указывает, что должно быть любое (в т.ч. 0) число таких символов
Таким образом наше выражение, в отличие от маски, наше выражение будет совпадать и со строкой "alo" и со строкой "alo.jpg", и со строкой "alongtimeagoinagalaxyfarfaraway", так как ' . ' не является точкой, а вместе со ' * ' указывает на любое число символов до конца строки. Чтобы указать именно точку, нам понадобится символ ' \ ' который меняет режим интерпретации следующего символа:
' \ ' - сам по себе не является символом, а меняет восприятие последующего
' \. ' - просто точка
' \\ ' - просто слеш
' \r ' - возврат курсора, часто стоит в конце строки
' \n ' - конец стркои
' \t ' - табуляция
Таким образом, если мы хотим, чтобы наше выражение alo.* работало, как файловая маска alo.*, его нужно переписать: alo\..*
' alo ' - всё ещё просто символы
' \. ' - просто точка
' .* ' - любое число любых символов до конца строки
Теперь к более полезным вещам: помимо ' . ' существуют другие способы описать последовательность символов:
' \s ' - любые прбелы и переносы строк
' \S ' - иневертирует \s - то есть НЕ пробелы и НЕ переносы строк
' \w ' - часть слова (буква или цифра, без дефисов и пробелов)
' \W ' - НЕ часть слова, то есть не буква и не цифра
' \b ' - граница слова, то есть \w\b будет указывать на последнюю букву в слове
' \B ' - НЕ граница слова, то есть \B\w\B будет указывать на букву, которая не является первой или последней в слове
' \A ' или ' ^ ' - начало строки
' \Z ' или ' $ ' - конец строки
' [abc] ' - любой символ из числа ' a ' , ' b ' и ' c '
' [^abc] ' - любой символ КРОМЕ ' a ' , ' b ' и ' c '.
' [а-яА-ЯёЁ] ' - любой символ в диапазоне от ' а ' до ' я '.
Диапазоны значений, такие как ' а-я ' используют порядковые номера символов в таблицах кодировок. Поэтому буква ' ё ' указана в наборе отдельно - на заре времён её не стали включать в стандартную таблицу кодировки, где и без того было тесно (всего 256 символов), а с приходом ANSI воткнули туда, где было место. Так и продолжилось в UTF8 и Unicode.
Все вышеописанные выражения описывают ровно один символ из набора. Для того чтобы указать, что символов может быть больше, необходимо сделать это явно:
[abc]* - любое число символов a, b или c
[abc]? - 0 или 1 символ
[abc]+ - один или более символ
[abc]{2,} - два и более символа
[abc]{4,8} - от 4 до 8 символов
По умолчанию все операторы, указывающие число символов - жадные. Это означает, что они будут пытаться найти как можно больше текста, который подпадает под условие. Например, .+k захватит всю строку "xkabak". Поведение можно изменить, поставив после числа символов знак вопрсоа ' ? ' , это заставит регулярку выбирать минимально возможное число символов из набора, так выражение .+?k найдёт вначале "xk", а затем "abak"
В ситуации, когда вы ищите слово, которое может включать в себя "aab" или "baa", но не "bab", может быть удобно указать несколько опциональных наборов, вместо того чтобы пытаться описать один, для этого служит разделитель ' | '. Выражение aab|baa будет искать либо одно вхождение, либо другое. Если дать ему на вход строку "aabbaa", то первым вхождением вы найдёте "aab", а следующим "baa".
Поведение регулярных выражений зависит от выставленных флагов. Некоторые приложения дают вам возможность применять флаги, другие позволяют использовать их как часть регулярного выражения, третьи не позволяют менять вообще. Самый главный флаг для вас - ' u ' - поддержка Unicode. Если флаг не выставлен, то \w и \b не будут работать с кириллицией. Как часть регулярного выражения флаги перечисляются слитно после выражения: /.*/gmuis
' g ' - global, поиск продолжается после первого совпадения
' m ' - multi-line, воспринимает текст, как набор строчек, позволяет использовать ' ^ ' и '$', как символы начала каждой строчки в вашем тексте
' u ' - поддержка Unicode, без этого флага дефолтные наборы работают только с ASCII таблицей кодировки
' i ' - insensitive, игнорирует регистр букв, выражения которые срабатывают для ' я ', также работают и для ' Я ', и наоборот
' s ' - single-line, воспринимает текст, как одну строку, ' . ' начинает работать для символа новой строки ' \n '
Флагов больше, но это те, что могут вам понадобиться в первую очередь.
Наконец, давайте разберём группировки - то, что делает регулярки по настоящему мощным инструментом для замены и значительно упрощает анализ.
(.)(.) - нет, это не сисиьки, это две группы символов по 1 в каждой. :)
По умолчанию каждая группа имеет порядковый номер: $1 и $2 соответственно.
Это позволяет вам ссылаться на результаты поиска и выполнять преобразования.
Например ^(.*)\.([^\.]+)$ такое выражение позволит разбить полное имя файла на имя и расширение:
' ^ ' - начало строки
' (.*) ' - группа $1, в неё попадает любое число любых символов
' \. ' - далее следует точка
' ([^\.]+) ' - группа $2, в неё попадает хотя бы 1 символ, который не является (^) точкой (\.).
' $ ' - конец строки
Теперь мы можем заменить одну группу, и оставить без изменений другую. Например у нас есть список файлов с расширениями, а мы хотим оставить только имена:
Заменяем строку, оставляя только группу $1
Группам можно давать имена: (?'left'.)(?<right>.) и ссылаться на них: $+{left} и $+{right} соответственно. Как вы можете заметить, группы всё ещё содержат по 1 произвольному символу, но уже стали очень многословны. Смешивание управляющих последовательностей и наборов символов является одной из проблем, которая мешает легко и просто начать пользоваться регулярками - в них нет ничего сложного, у них просто упоротый синтаксис, который тяжело читать и понимать неподготовленному человеку.
Наконец, если вам понадобится "инвертировать" выражение, описав то, что не должно попадать в текст, вам могут помочь два костыля, которые проверяют, что впереди или позади от текущей позиции нет определенного регулярного выражения:
Здравствуй (?!мир) - ищет "Здравствуй " за которым не следует "мир".
(?<!Здравствуй) мир - ищет "мир" перед которым нет "Здравствуй".
В обоих случаях вы можете указывать любые регулярные выражения внутри скобок, но во втором выражение должно иметь константную длину, то есть вы не можете заменить "Здравствуй" на "З.+", так как подобный поиск будет работать чудовищно долго и не поддерживается стандартом регулряных выражений.
Помимо создания именованных групп, ( ) используются для любого рода группировок, начиная с возможности сделать текст регулярного выражения чуть чище, заканчивая ситуацией, когда вам необходимо указать, что вся группа выражений может встречаться не один раз. Например, вот так можно провалидировать список email-адресов:
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} - затем идёт шаблон нашего email-адреса. Несмотря на кажущуюся сложность, фактичеси он состоит только из перечислениям допустимых символов и выглядит почти как \w+@\w+\.\w+ (лучше не стало, да? :D ничего, привыкните :))
(; )? - это опциональный разделитель между адресами - точка с запятой и пробел
Затем мы берём всё предыдущее выражение в скобки и говорим, что у нас должно быть хотя бы одно такое вхождение ( ... )+
Ну и заканчивается это концом строки: $
Таким образом вы можете группировать выражения, делать выбор между разными группами, делать вложенные выражения внутри других выражений и т.д.
Единицы добравшиеся досюда спросят - и зачем это мне?
Прежде всего - для автоматизации рутинных процессов по поиску, замене и анализу текста. Например, у меня есть 661 .txt файл с текстом из игры, и мне необходимо найти все ссылки на игровую энциклопедию вида:
<link="Encyclopedia:TechPriest">техножрец</link>
Я использую Notepad++ для поиска файлов по маске .txt и регуляркой
<link="Encyclopedia:\w+">([^<]+)<\/link>
выгребаю нужные строки:
Notepad++ (Ctrl+F -> Найти в файлах), внизу: "Регуляр. выражен."
После чего копирую найденные строки в отдельный файл и сортирую, чтобы убрать мусор:
Notepad++ (Для сортировки: Правка -> Операции со строками -> Сортирова)
Заменяю <link на \r\n<link, чтобы в каждой строки было не больше одного вхождения и снова сотритрую, убирая мусор:
Notepad++ (Для замены: Ctrl+H)
Теперь мне остаётся заменить всю строку на значение первой группы регуляки, удалить дубикаты и отсортировать строки:
<link="Encyclopedia:\w+">([^<]+).*
Notepad++ (Для удаления дубликатов: Правка -> Операции со строками -> Удалить дубликаты)
Таким образом регулярные выражения сэкономили кучу времени, которое в противном случае пришлось бы потратить на ручную вычистку документа.
Надеюсь, теперь вам понятны общие принципы работы регулярок, и для чего они могут пригодиться. Всем спасибо за внимание и до новых встреч.