Проходим челлендж RegexPlay на 100%

    Челлендж от Machine Learning Lab Университета Триеста (Италия) предлагает решить 12 задач причём уровень задач сильно варьируется — от супер-лёгкого до сложного.


    Конечно, решений может быть больше одного и тест-кейсы в задаче покрывают не все варианты. Тем не менее если набрали 100% — то поздравляю! Тест пройден.


    Спойлер: в некоторых задачах (особенно лёгких) подсказка практически и есть решение.


    Задача 1 — найти цифры


    Имеется текст содержащий цифры, требуется найти все цифры.


    Сложность: супер-лёгко.


    Подсказка

    Нужно использовать символ цифры и квантификатор множественности.


    Решение
    \d+

    "[0-9]+" тоже верное решение.


    Разбор решения
    • "\d" или "[0-9]" — любой одиночный символ от 0 до 9.
    • "+" — допускается один или более символов.

    Задача 2 — найти mac-адреса


    Имеется текст содержащий mac-адреса, нужно их найти.


    Сложность: легко.


    Подсказка

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


    Решение
    \w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2}

    "+" вместо "{2}" тоже является верным решением, хоть и не до конца точным.


    Разбор решения
    • "\w" — любой символ алфавита или любая цифра.
    • "{2}" — допускается только 2 символа.

    Лучшее решение

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


    ([\da-f]{2}:){5}[\da-f]{2}

    Разбор решения
    • "\d" — любая одиночная цифра, "a-f" — диапазон от a до f. Итого мы имеем допустимый набор символов 0123456789abcdef — то есть шестнадцатеричный код Hex. "{2}" — любые 2 символа из этого набора символов.
    • "([\da-f]{2}:)"{5} — допустимо повторение двух символов hex + двоеточие ровно 5 раз.
    • и в конце "[\da-f]{2}" — любые 2 имвола из набора hex

    Задача 3 — найти ftp-адреса


    Имеется текст с ftp-адресами, требуется их найти.


    Сложность: легко.


    Подсказка

    Нужно начать с ftp:// а затем перечислить допустимые символы в адресе, не забыв про квантификатор множественности.


    Решение
    ftp:\/\/[\w\d-_.\/]+

    Разбор решения
    • "\/" — обычное экранирование символа "/".
    • внутри квадратных скобок перечисление допустимых символов, а именно — любой символ алфавита или любая цифра, дефис, нижнее подчёркивание, точка или слэш (если в имени есть другие символы — их можно дописать).

    Задача 4 — найти шаблон


    Имеется какой то форматированный файл в котором нужно найти все строки соответствующие шаблону $любые символы$.


    Сложность: легко.


    Подсказка

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


    Решение

    Банальная задача, но требует уже немного лучшего знания регулярок.


    \\$.+?\\$

    Разбор решения
    • "\\$" — экранирование доллара.
    • ".+" — любое количество любых символов, "?" — убираем жадность.

    Задача 5 — найти ip-адреса


    Из текста найти ip-адреса.


    Сложность: легко.


    Подсказка

    Очень похоже на вторую задачу.


    Решение
    \d+\.\d+\.\d+\.\d+

    Если вместо "+" вы написали "{1,3}" то вы молодец.


    Разбор решения
    • "\d" — любая одиночная цифра,
    • "+" — любое количество символов, "{1,3}" — жёсткое ограничение на количество символов (от 1 до 3).

    Лучшее решение

    ip-адрес это не просто цифры, а цифры от 0 до 255.


    \b((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\b

    Разбор решения

    Что бы определить диапазон от 0 до 255 придётся немного постараться:


    • "\d" — число от 0 до 9
    • "[1-9]\d" — число от 10 до 99
    • "1\d{2}" — число от 100 до 199
    • "2[0-4]\d" — число от 200 до 249
    • "25[0-5]" — чилсо от 250 до 255

    Далее разрешаем повторение шаблона цифры от 0 до 255 ровно 3 раза:


    • "(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]).){3}" — внутри точка, потому что она тоже повторяется 3 раза и в конец тот же диапазон чисел, но без точки.
    • в конец и начало добавляем символ границы слова "\b" — нужен для того что бы убрать из вхождения например такой текст "1172.30.42.109" (согласен, маловероятно и наверное излишне).

    Задача 6 — анкор


    Из html-документа вытащить тег href.


    Сложность: средне.


    Подсказка

    Следует использовать "любой символ кроме" и "или" для каждой скобки.


    Решение
    href=("[^"]+"|'[^']+')

    Разбор решения
    • " — начинаем поиск с двойной кавычки.
    • "[^"]+" — далее совершенно любой символ кроме двойной кавычки.
    • Заканчиваем двойной кавычкой.

    Всё это помещаем в круглые скобки, дописываем знак или — "|" и повторяем тоже самое для одинарных кавычек.


    Задача 7 — ссылки


    В произвольном тексте найти все ссылки. Задача усложняется тем что не достаточно просто найти ссылку, нужно выкинуть с конца не допустимые символы.


    Сложность: сложно.


    Подсказка

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


    Решение
    http:\/\/([^>\s]+)[\w\/]

    В задаче нет ссылок https, но можно это учесть добавив "s?" после http.


    Разбор решения
    • "[^>\s]+" ищем любые символы кроме символа ">" и символа пробела. По сути дела читать можно так "идём до тех пор пока не увидим один из этих символов".
    • "[\w\/]" — это ограничитель который разрешает заканчивать ссылку только латинским символом или слэшем.

    И по итогу — начинаем с http://, далее совершенно любой символ кроме символов "<" или пробела, но в конце обязателен символ латинского алфавита или слэш.


    • дополнительные "s?" в "https?" просто разрешают как "http://" так и "https://".

    Задача 8 — заголовки


    В HTML заголовком считается любой тег h + цифра.


    Сложность: средне.


    Подсказка

    Начните с h + цифра + любой символ или его отсутствие.


    Решение

    Довольно просто:


    <h\d.*>.*<\/h\d.*>

    Разбор решения
    • "h\d" — здесь мы говорим что после символа обязательно должна идти "\d" цифра.
    • А далее ".*" может идти любое количество любых символов или может не идти ничего.

    Лучшее решение

    После h идёт цифра, причем в спецификации html указано что допустимыми являются цифры от 1 до 6, а сам тег не может быть закрыт ничем кроме треугольных скобок. Следовательно такое решение будет более точным:


    <h[1-6][^>]*>.+?<\/h[1-6]>

    Правда тут есть ошибка позволяющая открыть тег одной цифрой, а закрыть другой. И поэтому ещё более точным решением будет использовать ссылки: "<h([1-6])[^>]*>.+?<\/h\1>". Это не говоря о том что в тесте не учтен регистр.


    Разбор решения
    • "h[1-6]" — здесь мы говорим что после "h" обязательно должна идти цифра от 1 до 6.
    • "[^>]*" — дальше могут идти совершенно любые символы кроме ">", а могут и не идти.

    Отдельно про дополнение:


    • Обёртывая выражение "(h[1-6])" в скобки мы создаём группу под номером 1.
    • Далее используя ссылку на первую группу "\1" мы говорим о том что хотим видеть в конце тот же самый, h с той же самой цифрой, что и в начале.

    Задача 9 — телефоны


    В произвольном тексте найти в все телефоны в разных форматах.


    Сложность: сложно.


    Подсказка

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


    Решение
    \(?\d{3}[\). \/-]{1,2}\d{3}[-.]\d{4}

    Работать будет, но допускает два символа после первых трех цифр — хорошо если это закрывающая скобка и пробел после неё, но плохо если два дефиса. Поэтому есть чуть более точное решение: "\(?\d{3}(\) ?|[.\/-])\d{3}[-.]\d{4}"


    Разбор решения
    • "\(?" — указываем на возможность наличия символа "(" или его отсутствия, экранируем т. к. это зарезервированный символ.
    • "\d{3}" — цифры с ограничением только 3.
    • Далее возможны символы "[\). \/-]{1,2}" — слэш, закрывающая круглая скобка, точка, пробел, обратный слэш и дефис. {1,2} — говорит о том что этих символов может быть 1 или 2. Например закрывающая скобка и пробел после неё.
    • Далее обязательно "[-.]" — дефис или точка.
    • И "\d{4}" — четыре цифры в конце.

    Задача 10 — авторы


    Есть некий паттерн содержащий элементы, найти всех авторов из поля author.


    Сложность: очень сложно.


    Подсказка

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


    Решение
    [A-Z][A-Za-z-']+, [A-Z](\{\\"\w\})?[A-za-z-']*( [A-Z][A-Za-z-']*)?

    Это решение верно, но имеет минус. Мы обошли "{\"u}" костылем "({\"\w})?" и в принципе это работоспособно если так же засунуть этот костыль в другие части имен авторов.


    В любом случае если вы смогли хоть как то решить эту задачу, это очень круто!


    Разбор решения
    • "[A-Z][A-Za-z-']+" — Ищем слово которое обязательно начинается с большой буквы, затем содержит любой символ алфавита, дефис или апостроф.
    • Далее всё ровно тоже самое что и чуть выше, но здесь между мы имеем "({\"\w})?" — это просто костыль позволяющий обойти экранирование. Означает что кусок "{\"u}" может как присутствовать, так и отсутствовать.
    • И далее опять ровно тоже самое что и в первом случае "( [A-Z][A-Za-z-']*)?", замет исключением что это выражение обвёрнуто в группу и стоит знак "?" означающий не обязательность этого условия.

    P.S перечисление [A-Za-z] здесь вынужденная мера, потому что в тесте отсутствуют модификаторы.


    Лучшее решение

    Есть более компактное и простое решение позволяющее отказаться от костыля выше:


    [A-Z]\S+, [A-Z]\S*( [A-Z][\S]*)?\b

    Разбор решения
    • "[A-Z]\S+, " — сначала, обязательно, заглавная буква "[A-Z]", затем любое количество не пробельных символов "\S+" до точки с запятой ", ".
    • Далее почти тоже самое "[A-Z]\S*" — начало обязательно с заглавной буквы, а потом любой не пробельный символ до границы слова "\b".
    • "( [A-Z][\S]*)?" — а между ними такая же, но не обязательная группа для отчества.

    Задача 11 — содержимое заголовка


    Точно так же как в задаче 8, но нужно найти не сами заголовки, а их содержимое.


    Сложность: сложно.


    Подсказка

    Задача идентична 8 задаче. Это не сложно если знать про заглядывание назад и вперед. Имеет решение с заглядыванием назад и без заглядывания назад.


    Решение

    Без заглядывания назад:


    ([\w ]*<[^h].+|[^<>]+)(?=<\/h\d>)

    Разбор решения
    • "([\w ]*<[^h].+|[^<>]+)" — есть два варианта того как может начинаться содержимое заголовка. Первый — с тегом внутри "[\w ]*<[^h].+" — при этом нужно убедиться что это не тег h и учесть то что перед тегом может быть текст. Второй вариант — текст без тегов "[^<>]+", то-есть любые символы кроме открытия и закрытия тега.
    • "(?=<\/h\d>)" — заглядывание вперед внутри которого должен быть закрывающий тег h.

    С заглядыванием назад:


    (?<=<h[^>]+>).+?(?=<\/h\d>)

    Плюс можно учесть все замечания к решению 8 задачи.


    Разбор решения
    • "(?<=<h[^>]+>)" — заглядывание назад внутри которого должен быть тег h.
    • "(?=<\/h\d>)" — заглядывание вперед внутри которого должен быть закрывающий тег h.

    Задача 12 — список авторов


    Задача похожа на 10 задачу, но на мой взгляд проще.


    Сложность: сложно.


    Подсказка

    Все основные принципы из 10 задачи, но нужно учесть начало строки. Известное мне решение использует заглядывание назад.


    Решение
    (?<=^\d+\. )[A-Z][\w]+, [A-Z][\w\.]+

    Разбор решения
    • "(?<=^\d+. )" — заглядывание назад где обязательно с новой строки должно быть любое количество цифр до точки с пробелом.
    • "[A-Z][\w]+, " — далее любое слово с заглавной буквы до точки с запятой и ещё раз тоже самое "[A-Z][\w.]+".

    Или по-проще, такое:


    (?<=^\d+\. )[^,]+,[^,:]+

    Разбор решения
    • "(?<=^\d+. )" — заглядывание назад где обязательно с новой строки должно быть любое количество цифр до точки с пробелом.
    • "[^,]+," — любое количество любых символов до запятой + запятая.
    • "[^,:]+" — любое количество любых символов до запятой или двоеточия.

    Комментарии 6

      +2

      Полчаса провозился с 11 и 12, думая что их нужно решать без lookbehind. Потом таки заглянул в решения. Видимо авторы используют Хром, так как движок Firefox не поддерживает lookbehind в регулярках :(

        0
        11 вполне решается, кстати
          +1
          Без заглядывания назад можно решить 11 задачу. Вот такая регулярка "(\s*[\w ]*<[^h].+|\b\w+\b)(?=<\/h\d>)" по-идеи должна отработать в Firefox с результатом 100%.
            0

            Спасибо за пример. Хотя такая регулярка и не соответствует духу задания — выбрать всё внутри заголовка. Например, заголовок из двух слов или восклицательный знак в начале заголовка сломает регулярку.

              +1
              Да, у них в задании учтены не все тест-кейсы.
              ([\w ]*<[^h].+|[^<>]+)(?=<\/h\d>) — это покроет варианты про которые вы говорите.
              А вообще, на практике я бы посоветовал решать эту задачу так: /<h\d[^>]*>(.+?)<\/h\d>/i и вынимать значение из группы.
            +2
            Я тот, кто подумал, что нужно обязательно обходиться без lookbehind, и мне больно. Я написал мерзкую, длинную регулярку)

            Третье решил как f.*D\/
            Восьмое — как <h\d.*
            Думал, что умный, раз так минималистично выкрутился.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое