Разработчики делятся на два типа: тех, кто уже понимает регулярные выражения и порой решает сложные задачи одной строкой, и тех, кто все еще боится и всячески их избегает. Эта статья специально для вторых, чтобы им было проще стать первыми. Она либо поможет преодолеть «регекспофобию», либо усугубит ее. В любом случае, добро пожаловать под кат.

Используйте навигацию, если не хотите читать текст полностью:
Введение
Инструменты
Hello, world
Спецсимволы
Заглядываем в бездну

Введение


Регулярное выражение описывает некоторый образец (на английском — pattern), которому текстовые строки могут или соответствовать, или нет. Основные области применения: поиск, валидация, парсинг и устрашение.
  • Поиск. Найти все email-адреса в тексте, чтобы отправить им письма счастья.
  • Валидация. Проверить, что введенный в форме email-адрес хотя бы отдаленно похож на настоящий.
  • Парсинг. Разбить email-адрес на имя пользователя и домен.
  • Устрашение. Наиболее полное регулярное выражение для валидации email-адресов можно посмотреть на этой странице.
Важно помнить, что хотя регулярные выражения и являются мощным инструментом, все-таки это не серебряная пуля и полнотой по Тьюрингу они не обладают. Следовательно, не все задачи можно решить с их помощью. Например, на Stack Overflow объясняется, почему ни в коем случае нельзя парсить HTML с помощью регулярных выражений.

С другой стороны, иногда регулярные выражения позволяют решать задачи, для которых они вообще не предназначены. Например, на этой странице описан крайне неэффективный, но работающий способ проверять число на простоту.

Каждый раз писать «регулярное выражение» утомляет. В англоязычном сленге прижились термины regex и regexp. Однако оба звучат как имена злодеев из аниме, поэтому в этой статье я буду время от времени использовать слово «регулярки».

Согласно легенде, «regular expressions» означает «обычные выражения», а регулярными их назвали, потому что поленились при переводе.

Кстати, есть мнение, что термин regex не всегда может быть синонимом regular expression. Но иногда может. Подробности можно прочитать в короткой статье.



Инструменты


Безусловно, из всех своих задач регулярки лучше всего справляются именно с устрашением. Однако благодаря работе ведущих египтологов появились сервисы, помогающие расшифровать эти загадочные письмена. Я чаще всего пользуюсь двумя: один красивый, другой — полезный. На самом деле оба красивые и полезные.

Regexper позволяет превратить регулярку почти любой степени запутанности в красиво оформленный граф. Например, есть такая регулярка для вещественных чисел, изобретенная древними шумерами:

[+-]?(\d*\.)?\d+

И вдруг она обретает смысл в виде интуитивно-понятной инфографики:


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

Правда, для особо запутанных случаев и граф будет непростым. Например, попробуйте отправить в сервис вышеупомянутую регулярку для email (правда, сперва нужно будет записать ее в одну строчку). Я не стал прикреплять картинку здесь, потому что она имеет 24 621 пикселей в ширину.

Второй крайне полезный ресурс — regex101. Помогает увидеть, как работает регулярка. А если она не работает или работает не так, то можно понять, в чем дело. В сервисе есть даже пошаговый дебаггер!


Hello, world


Хоть язык регулярных выражений и не является языком программирования, все-таки будем соблюдать традицию. Итак, открываем regex101 и вводим регулярное выражение:

hello, world

Да-да, регулярка может выглядеть как человеческий текст, а не только как клингонский некролог. Регулярному выражению «hello, world» соответствует одна-единственная строка — «hello, world».


(В некоторых|Во многих) языках программирования регулярному выражению можно задавать опции. Если ей будет Case Insensitive (обычно обозначается как i), то подойдут все варианты регистра: «Hello world», «Hello World», «HELLO WORLD», «hElLo wOrLd» и т. д.

В regex101 для этого достаточно кликнуть по буквам с опциями справа от строки ввода.


Ни для кого не секрет, что в слове «hello» две буквы «l». Давайте этот занимательный факт запишем с помощью языка регулярных выражений:

hel{2}o, world

Это то же самое выражение, что и предыдущее, но теперь уже слегка пробуждает Ктулху. Обратите внимание, что {2} работает только на букву «l», а не на все буквы, стоящее ранее.

Зачем нам это нужно? Не проще ли было оставить просто «ll»? Безусловно, в данном случае проще. Однако в реальной практике могут быть более сложные ситуации. Во-первых, символ может повторяться не два раза, а, например, тысячу. Во-вторых, есть возможность применить операцию не на один символ, а на группу, но об этом в другой раз.

Кроме того, можно указать не только конкретное число повторений, но и пределы: минимум и максимум. Например:

hel{2,5}o, world

Такая запись означает, что буква «L» может повторяться от двух до пяти раз.


А если второе число опустить, но запятую оставить, это уже будет означать «до бесконечности».
Например:

hel{2,}o, world

Такая запись уже означает, что в слове может быть любое количество букв «l», но не менее двух.


Оставим пока букву «l» в покое. В нашем примере вариативность по-настоящему пригодится, когда мы будем работать с пробелом. Допустим, нам нужно обрабатывать и те случаи, когда между словами не один пробел, а несколько:

hello, {1,}world



Срочная правка от заказчика: в конце фразы могут стоять восклицательные знаки, может быть даже сразу три. А может быть бесконечность, а может быть ни одного. Так и запишем:

hello, {1,}world!{0,}



Обратите внимание на последние две строчки. Если начинаются символы, не соответветствующие образцу, это не отбраковывает всю строку целиком, а только ту часть, которая не подходит. Иногда это здорово, а иногда это совсем не то, что нам нужно. Что с этим делать, обсудим позже.

Раз уж зашла речь про знаки препинания: а что, если кто-то забудет поставить запятую, а мы все равно хотим найти такие варианты? Значит, запятая у нас должна встречаться или ноль, или один раз:

hello,{0,1} {1,}world!{0,}

Вот теперь это похоже на друидскую бухгалтерию, как и положено хорошей регулярке!{3,}
Она уже достойна того, чтобы посмотреть ее в Regexper:


  1. Берем слово «hello».
  2. Запятую или берем, или обходим стороной.
  3. Пробел один берем точно, затем крутимся по нему любое число раз.
  4. Берем слово «world».
  5. Восклицательный знак или обходим стороной, или крутимся по нему.


Обратите внимание, что строка, где вообще нет пробела, не соответствует образцу. Дело в том, что мы указали, что хотя бы один пробел должен быть. Разберем этот случай чуть позже.

Спецсимволы


Чтобы не переполнять регулярку цифрами, ее можно переполнить спецсимволами. Для некоторых наиболее распространенных вариантов есть сокращенный способ записи:
{0,1}
?
Символ или есть, или нет
{0,}
*
Символ встречается любое количество раз, включая 0
{1,}
+
Символ встречается не менее одного раза
Перепишем выражение короче:

hello,? +world!*

Теперь выражение выглядит почти как изначальное «hello, world», но насколько функциональнее оно стало!
Кстати, регулярное выражение, которому соответствуют оба имени злодеев англоязычных термина — regexp?
А что, если нужно воспринимать спецсимволы как обычные: плюс как плюс, вопросик как вопросик? Мы можем их экранировать с помощью escape-символов (в англоязычной литературе это называется escape character). Косая черта перед спецсимволом означает, что он теряет сверхспособности и становится обычным. Такой криптонит в мире регулярок.

Пример:

hello\?

Соответствует строке «hello?», а вовсе не «hello\» и «hello».


Косая черта может экранировать и саму себя. Чтобы это сработало, ее нужно записать дважды: \\

В некоторых языках программирования косая черта уже является escape-символом в строке, поэтому, чтобы она стала escape-символом регулярного выражения, нужно написать \\. А чтобы она стала просто косой чертой, нужно написать \\\\. Да, чтобы косая черта осталась самой собой, ее нужно повторить четыре раза. Расскажите это людям, которые не понимают, за что айтишникам столько платят.

Возвращаемся к «hello, world». Кто-то может случайно написать не «hello», а «hallo». Давайте этот случай тоже обработаем.

Чтобы выбрать один символ из нескольких, нужно перечислить их внутри квадратных скобок. Выражению h[ea]llo соответствуют и «hello», и «hallo».


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

Если написать [helloпривет], это выражение будет соответствовать просто одному символу из набора h,e,l,o, п, р, и, в, е, т. То есть одна буква подходит, а все слово — уже нет. Как быть?

Для этого есть спецсимвол |. Запишем hello|привет.


Стоп, а в регулярках можно использовать кириллицу? Конечно! Хоть емоджи. Хотя, конечно, это зависит от приложения или языка программирования, в которым вы будете их использовать, но прямого запрета на этот счет нет.

Если написать hello|привет, world, то слово «world» останется в правой части. То есть мы получим или «hello», или «привет, world».


А можно «world» как-то оставить общим (хоть это и странно будет смотреться в сочетании с приветом)? Чтобы ограничить действие вертикальной черты, можно использовать круглые скобки. Вообще, это далеко не единственная, и более того — не главная задача круглых скобок, но об этом позже. А пока:

(hello|привет), world

Можно писать сколько угодно вариантов:

(hello|привет|bonjour), world



Обратите внимание, что можно поставить пробелы вокруг прямой черты, чтобы было красиво:

(hello | привет | bonjour), world

Но в таком случае изменится и выражение, поскольку пробелы не просто для красоты, а являются частью выражения. Поэтому не вставляйте пробелы, если не уверены, что они нужны.



Кстати, по поводу пробелов. Есть люди, которые не ставят пробел после запятой, а есть те, кто ставят перед ней. Как сказал бы Шелдон Купер, в мире не хватит ромашкового чая, чтобы унять ярость в моей груди. Но что поделать? Такие случаи тоже возможно нужно обрабатывать.

Попробуем такой вариант:

hello *,? *world



Работает.


Но есть две проблемы. Первая — строчка «helloworld». Так как у нас пробелы со звездочкой и запятая под вопросом, то подходят даже те варианты, в которых вообще нет разделителей. В тренировочном примере ничего страшного, но в реальной практике это распространенная проблема: как сделать, чтобы среди множества опциональных вариантов хотя бы один все-таки присутствовал? Это можно сделать разными способами: квадратным и круглым.

Квадратный способ


hello *[, ] *world


После «hello» могут идти пробелы (возможно, ни одного), затем точно должен быть какой-то разделитель (запятая или пробел), затем — еще пачка пробелов, возможно пустая.

Круглый способ


hello( *, *| +)world


Здесь у нас есть два варианта на выбор:
  • запятая, с которой могут быть пробелы (а могут и не быть);
  • пробелы, не менее одного.
Какой из способов более предпочтительный, зависит от конкретного случая.

Первую проблему решили, но есть вторая: в самом выражении пробел недостаточно ярко выражен. Не сразу очевидно, что звездочка относится именно к нему.

Помните, мы говорили, что косая черта превращает спецсимволы в простых смертных? Так вот, наоборот тоже работает. Если поставить косую черту перед простым магловским символом, он обретет сверхспособности. Конечно, не с каждым это работает, только с теми, у кого достаточно мидихлориан.

Например, буква s прекрасна тем, что с нее начинается слово space, поэтому \s — это спецсимвол, обозначающий пробел. И не только пробел, а вообще все пробелообразное: Tab и при определенных опциях перенос строк.

Перепишем наше выражение:

hello(\s*,\s*|\s+)world

Уже начинает походить на древнеегипетскую глаголицу, как и положено достойному регулярному выражению. Но при этом мы все еще примерно понимаем, что оно делает! В этом главный секрет регулярок — кто их пишет, тот их и понимает.

Заглядываем в бездну


Теперь соберем все вместе:

(h[ea]llo|привет|bonjour)(\s*,\s*|\s+)world!*


Уровень оккультизма достиг приемлемого.

В статье присутствует от нуля до бесконечности неточностей и упрощений. Некоторые из них допущены умышленно, чтобы сохранить развлекательный дух статьи и не отпугнуть вас излишней педантичностью и академичностью.

В следующей публикации рассмотрим различные возможности регулярных выражений более подробно.

Комментарии (46)


  1. andkartsev
    07.10.2024 16:49
    +11

    Пойду попью ромашкового чаю.


  1. DSSilver
    07.10.2024 16:49
    +13

    (Автор|Дани{2}л)(\s*,\s*|\s+)пиши ещё!*


  1. lost_embedder
    07.10.2024 16:49
    +11

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

    За мануал спасибо, было наглядно. Увижусь с ним, по моим расчетам, месяца через четыре :)


    1. DandyDan Автор
      07.10.2024 16:49
      +5

      Ну, прямо ембедить наверно редко надо, но наверняка возникают задачи, связанные с процессом разработки: логи распарсить, найти и заменить что-то в коде и т.д. В конечном итоге время, потраченное на изучение, окупается.


  1. pelepelin
    07.10.2024 16:49
    +4

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

    А разработчики, которые используют регулярные выражения, делятся на тех, кто уже решал проблемы с их производительностью, и тех, кто ещё нет ;)

    hello *,? *world

    оно




    1. DandyDan Автор
      07.10.2024 16:49

      Да, производительность — это отдельная тема )


      1. Sly_tom_cat
        07.10.2024 16:49

        Я бы сказал больная тема....


    1. Spearance
      07.10.2024 16:49

      и с ReDoS


  1. duke_alba
    07.10.2024 16:49
    +1

    Жду продолжения! Пока, вроде, понял... :-)


  1. Grey83
    07.10.2024 16:49
    +3

    Вообще регулярных выражений существует больше одного стандарта: https://ru.wikipedia.org/wiki/Регулярные_выражения#Разновидности_регулярных_выражений

    И про регулярки раз сто на хабре писали: https://habr.com/ru/search/?target_type=posts&order=relevance&q=[regexp]


    1. DandyDan Автор
      07.10.2024 16:49
      +2

      Главная цель и отличительная черта этой серии статей — максимальная простота. Нюансов очень много, и если бросать их на читателя все разом, получится просто перегруз информацией.

      В том числе и про различные "наречия" планировал упомянуть в следующей главе. Без конкретных примеров, чем именно отличаются, просто как сам факт.


      1. Grey83
        07.10.2024 16:49
        +1

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


    1. unreal_undead2
      07.10.2024 16:49
      +1

      Угу - писать про регулярки, не уточняя диалект, всё равно что играть в преферанс, не договорившись о системе.


      1. DandyDan Автор
        07.10.2024 16:49

        В любом преферансе 32 карты в колоде, даже в "гусарике". В регулярках тоже есть общая база, а диалекты в основном меняют/добавляют advanced фичи.


        1. unreal_undead2
          07.10.2024 16:49
          +1

          Да какие advanced, периодически путаюсь, где какие скобки надо экранировать.


  1. SadOcean
    07.10.2024 16:49
    +6

    Я признаю мощность и красоту регулярок, но вместе с тем есть и пару проблем:
    - Они очень информационно плотные, их тяжело читать и разбирать, получается writeonly подход.
    - Мне они нужны довольно редко, в лучшем случае раз в пару месяцев. Соответственно каждый раз приходится гуглить, особенно если задача нетипичная. Python, С# и прочие Java имеют какие то маленькие нюансики в синтаксисе стандартного средства регулярок, поэтому без гуглежа можно не понять, что конкретно не завелось.

    А так хорошая штука конечно.

    Для слабых духом есть библиотеки, собирающие регулярки из человекочитаемых конструкций наподобие LINQ, к примеру вот такая:
    https://github.com/ricoapon/readable-regex


    1. DandyDan Автор
      07.10.2024 16:49
      +1

      Спасибо за ссылку на библиотеку!

      Тоже когда-то задумывался о создании чего-то подобного, чтобы вместо иероглифов были простые человеческие конструкции. Однако, когда натолкнулся на regexper, понял, что графический язык лучше помогает читать регулярки, чем даже самый многословный текст. По крайней мере, для меня так.


      1. SadOcean
        07.10.2024 16:49

        Я, если честно, тоже не уверен, что это оптимально в общем случае (когда пишешь код для других)


  1. eyeDM
    07.10.2024 16:49
    +4

    ИМХО, самое страшное в регулярках то, что в разном софте слегка разный синтаксис. grep, sed, nginx, apache, php, javascript... Маски и "полноценные regex", PCRE и POSIX...

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


  1. Fedorkov
    07.10.2024 16:49
    +3

    В прошлом году сдавал ЕГЭ по информатике (чтобы поступить в вуз), где-то 4 или 5 задач решил одной строчкой регекса, потратив на каждую пару минут.


    1. sibel
      07.10.2024 16:49
      +2

      В реальной разработке, если вижу регулярку, там где можно обойтись парой-тройкой ифчиков, то мне хочется убивать. Как правило регулярки сильно ухудшают читаемость кода, и крайне плохо модифицируются кем либо, кроме автора регулярки. Поэтому, если можно не писать регулярку, то ни в коем случае ее писать не следует


      1. alamat42
        07.10.2024 16:49

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


      1. eulampius
        07.10.2024 16:49

        У регулярных выражений есть огромный плюс - они могут быть параметром, который выводится в настройки.

        И мне, например, наоборот не нравится, когда потенциально изменяющуюся логику жёстко прописывают ифчиками или городят стратегии и фабрики валидаторов, вместо регулярки. Естественно, при условии, что использование регулярных выражений не спровоцирует кризис производительности. А то с другой стороны мне доводилось видеть, как регулярки использовали для добывания значений параметров из файла, каждый раз скармливая весь файл для каждого искомого параметра )


        1. unreal_undead2
          07.10.2024 16:49
          +2

          Главное не переборщить, а то получится что-то типа конфигов sendmail .


  1. genodessa
    07.10.2024 16:49

    Недумал я, что блоксхемы могут быть даже тут. С блоксхемами на много понятнее. Спасибо. Очень интересно.


    1. redfox0
      07.10.2024 16:49

      Это не блок-схемы, а синтаксические диаграммы. Да, они полезные.


  1. venanen
    07.10.2024 16:49

    Для меня регулярки бывают двух видов - распространённые (валидация email, телефона, адреса и т.д.), и локальные - массово что-то в проекте заменить.
    Вторые пишутся обычно за 2 минуты по шпаргалке, а первые лучше всего взять с гитхаба или СО.

    P.S. Если вы используете сложную регулярку, и ее нельзя заменить - оберните ее хотя бы в читаемый вид - или в переменную типа phoneValidationRegExp, или в функцию с читаемым названием. Иначе потом будет больно.


    1. iroln
      07.10.2024 16:49

      валидация email

      Всегда вспоминается это, когда-то кто говорит про валидацию email в контексте регулярных выражений. :)
      https://pdw.ex-parrot.com/Mail-RFC822-Address.html

      Регулярные выражения - это типичный write only код, отлично подходит для одноразовых скриптов, но в продакшен коде сложные регулярки - это почти всегда боль, особенно если их нужно поддерживать и дописывать/переписывать под новые требования. Без тулов, раскладывающих регулярки на понятные читаемые блоки, не обойтись.


      1. unreal_undead2
        07.10.2024 16:49

        Кстати, нет ли (проверенных в промышленных условиях) библиотек, оперирующих синтаксисом регулярных выражений в виде объектов? Что-нибудь типа one_or_more(one_of("abc"))+zero_or_more(whitespace)+...


      1. WayMax
        07.10.2024 16:49

        Зачем? Зачем эти два с половиной листа в Ворде?

        Разве валидный email это не: "строка не нулевой длины" + "собачка" + "строка не нулевой длины" + "точка" + "строка не нулевой длины" ?


        1. unreal_undead2
          07.10.2024 16:49

          Читайте мануал . Конкретно формат адреса расписывается в п.6, но остальные части тоже релевантны. Например, в e-mail адресе допускаются комментарии в определённом формате.


          1. WayMax
            07.10.2024 16:49

            Ок, допустим "точек" может быть несколько, но это полностью подпадает под правило "строка не нулевой длины ". Зачем городить для этого что-то монструозное?


            1. unreal_undead2
              07.10.2024 16:49

              Не все строки ненулевой длины являются частью валидного e-mail.


  1. kolper
    07.10.2024 16:49
    +2

    Лучшее, что читал про регулярные выражения. Точнее - лучшее изложение.


  1. JuPk
    07.10.2024 16:49
    +6


    1. POPSuL
      07.10.2024 16:49

      Прекрасная книга!


  1. adante
    07.10.2024 16:49
    +1

    Это очень интересно, но вот ИМХО любую регулярку уже можно смело отдавать чатгпт и забыть про это.

    Все равно если ты их не пишешь каждый день, ты их пишешь с гуглом и справочником. При этом практическая польза от скилла «умею читать/писать регулярки» сомнительная.


    1. avaava
      07.10.2024 16:49

      Умею читать от умею писать отличается ОЧЕНЬ сильно.

      Мне они практически не нужны, использую ОЧЕНЬ редко, и, как мне кажется, для ситуативных случаев они хороши. Но вот разбираться с чужой (или старой своей, которой более полугода) - боль. Проще новую накидать.

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


      1. unreal_undead2
        07.10.2024 16:49
        +2

        Очень удобны просто для разовой замены в редакторе. Для кода, который надо поддерживать, часто прав jwz:

        Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.


  1. Spearance
    07.10.2024 16:49

    Сам автор курса по регуляркам, что могу сказать на последний комментарий, если в вашей работе нет задач в которых они нужны — вы просто мало и не глубоко работаете с текстом. Регулярки не панацея, но некоторые задачи позволяют решать в разы быстрее, чем то же самое описать обычными условиями. Сам ими пользуюсь везде, при написании кода, в терминальной строке, в корректуре приходящих извне объемных данных...

    По сути статьи, лучше примеры брать более жизненные. И раз уж примеры синтетические, то не нужна очень большая точность совпадения, а потому: `hello[ ,]*world` вполне достаточно


    1. DandyDan Автор
      07.10.2024 16:49

      hello,,,,,world

      helloworld

      hello , , , , ,world


      1. Spearance
        07.10.2024 16:49

        Нормально для простого примера, аналогично !*, иначе получается где-то мы точно ищем, а где-то допускаем вольности. Иначе:

        (h[ea]llo|привет|bonjour)(\s*,\s*|\s+)world!*

        Лучше писать без вторых круглых скобок, нам не нужно сохраняющее состояние, не нужен вариант \s+, его покрывает \s* только нужно исключить запятую ,?

        (h[ea]llo|привет|bonjour)\s*,?\s*world!*


        1. DandyDan Автор
          07.10.2024 16:49

          Это чтобы не допустить вариант helloworld.

          Да, я понимаю, что примеры надуманные и нереалистичные. Но для меня главное была простота и последовательность изложения. Любой реалистичный пример ломает последовательность.


          1. Spearance
            07.10.2024 16:49

            проходил это, когда курс писал, примеры приходилось из пальца высасывать, но, после объяснения скобок и квантификаторов, пффф... и тебе разбор CSV, и плавающие переносы в тексте, и триады в числах... желаю не перегореть ;)


    1. unreal_undead2
      07.10.2024 16:49

      при написании кода, в терминальной строке, в корректуре приходящих извне объемных данных...

      Хорошие примеры, но всё это скорее разовые задачи, чем часть production кода - и подходы могут быть разные. Скажем, если надо быстренько посмотреть на какие-то паттерны в бинарном коде - наверное, пройдусь регулярками по выхлопу objdump -d. Если надо добавить такую фичу в продукт - скорее возьму для парсинга готовую библиотеку (скажем, из llvm), даже если на входе именно дизассемблированный текст.


  1. radioxoma
    07.10.2024 16:49

    Регулярки приятны тем, что не нужно уметь программировать, чтобы пользоваться sed/grep и обрабатывать массивы текста. Ну и поиск в Sublime, Libreoffice.