Комментарии 26
Очередная попытка убить регулярные выражения очередным велосипедом, который и 1% от их возможностей не покрывает и ничуть не делает код проще.
Не проще ли разобраться с регулярными выражениями, чтобы они вас больше не пугали?
Все мы писали свои велосипеды, это часть обучения.
Главное не тащить их потом в прод.
Согласен, посыл статьи не в том, что "гляньте, какого я убийцу фейсбука запилил", а в том, чтобы не бояться быть опозоренным, показывая свои начинания.
В движках типа Nitra используется PEG для лексического анализа, что позволяет писать с заданием имён конкретным элементам выражения, комментариями, и возможностей в сумме таки больше, чем у самых выдающихся движков вроде того же перлового.
То, что именно АС сделал движок с меньшими возможностями, показывает, что он не в курсе достижений индустрии — факт, но и само по себе намерение что-то сделать в этом — похвально.
Меня пугают регулярные выражения. Не смотря на то, что я их знаю и использую. Основная причина - регэксп - это код для state machine, подобие ассемблера. Там ничего не для человека. Это write only код, который можно глазами реверс-инженерить и только. Там нет ни имён, ни мест для синтакисческого ритма (отступы, семантические блоки).
Человек, который написал длинный и суровый регэксп - справился. А как насчёт того, что будет в этом регэкспе багу ловить?
Если есть в языке возможность эффективно компилировать из строковых выражений (в большинстве промышленных языков это как-то обеспечивается), то такое можно обеспечить самому.
Я вот по другой причине (собственно иерархия синтаксиса), но строил в питоновском коде:
re_token="[A-Za-z0-9.!%*_\\+`'~-]+"
re_quoted_string="\"([^\r\n\"\\\\]|\\\\[^\r\n])*\""
re_display_name = "\\s*((?:%s(?:\\s+%s){0,})|%s)\\s*" % \
(re_token, re_token, re_quoted_string)
re_gen_value = "(?:%s|%s)" % (re_token, re_quoted_string)
re_gen_param = "(?:%s(?:\\s*=\\s*%s)?)" % (re_token, re_gen_value)
ну и так далее. Для парсера они предкомпилируется, и то, что у конкретного выражения может быть в итоге 1467 символов (исходник регэкспа для name-address) — уже неважно, структурно всё понятно, тестируется по кускам и так далее.
PS: Ну да, дальше предлагают механизмы, которые всё это оформляют уже в виде функций/методов — что-то вроде классического parser combinators. Если они могут потом скомпилировать результат во что-то эффективное (один автомат, как у классических регулярок) — отлично. Просто на одну зависимость больше :)
Мне кажется, написать свои регулярки — хороший способ разобраться с "классическими".
Просто сравните удобство использования: https://github.com/sprache/Sprache
"Хм, regex такой сложный. НУ ПОЧЕМУ?
Ну почему он всем кажется сложный?
Регексп ПРОСТОЙ! Нужно просто сесть, прочитать одну книжку по регекспам (1-2 вечера) и посидеть немного на стековерфлоу или unixexchange посматривая задачи по регекспам и парся их. ВСЕ. Все регекспы становятся достаточно простыми.
Можно не стать супергуру, но как минимум 90% регекспов вы сможете читать сходу, и 60% из них писать сами. Как минимум если говорить по PCRE.
У меня это было: каждый раз на практике разбираешь новое выражение, читаешь доки по мере надобности, пишешь свои выражения для конкретных кейсов. Проходит несколько лет и ты незаметно для себя владеешь регулярками и не представляешь как жил раньше.
По поводу статьи: автор просто учится, думаю пет-проект просто поможет разобраться, но а ещё полезно все таки реальные примеры из жизни.
Ведь по сути, то что автору кажется сложным, надо просто порезать на куски, и все станет понятным:
^(?!-)[A-Za-z0-9-]+([\\-\\.]{1}[a-z0-9]+)*\\.[A-Za-z]{2,6}$
^...$ — начало и конец строки, значит мы ищем совпадение не внутри строки, а со строкой целиком. Теперь про это забываем, и рассматриваем то что осталось по частям.
[A-Za-z0-9-] — латинские большие и маленькие буквы и цифры, и минус
[a-z0-9] — тоже самое маленькие буквы и цифры
[A-Za-z] — большие и маленькие буквы, без цифр на этот раз
Во всех случаях — одна штука
[]+ — одно или более повторений того что в скобках
{2,6} — от 2 до 6 повторений
{1} — ровно одно повторение
([\\-\\.]{1}[a-z0-9]+)* — а это комбинация из уже изученных вещей, как и вся регулярка в целом. Тут из нового круглые скобки, чтобы + или * применялись к группе как целому
Ну и что тут сложного, если совсем немного подумать? После этого от регулярки остались рожки да ножки — можно посмотреть в справочник, и понять, что означают остальные конструкции. А по сути, мы изучили всего несколько базовых вещей: *, +, {} как повторение, [] как диапазон символов, () как группировка, ^ И $. Ну и \\ еще.
И что я забыл, из нужного все время?
И что я забыл, из нужного все время?
Я регулярно использую lookahead и look behind, а также группы, чтобы потом можно было использовать back referenсe на сматченную группу.
Ну и главное — пока я уложился в одну страницу. То есть правил-то совсем немного.
Просто речь о разных типах сложности.
Правила регекспа просты и емки.
Это и их преимущество и недостаток.
Недостаток в том, что за счет высокой плотности правила получаются write only - их крайне тяжело читать и менять впоследствии, особенно если не делаешь это регулярно.
В моем случае это приводит к том, что правила то Я знаю, но сталкиваюсь так не часто и разнообразно (то в пайтоне, то в баше, то в шарповом коде), что каждый раз приходится мучительно вспоминать, как там начать строку, какой значок для букв и цифр и т.д.
Для моего случая verbal expression могут быть крайне удобен (да и на выходе он дает те же регекспы)
Он не сложный, он read only. Я не понимаю, кому вообще могла прийти идея в половине случаев экранировать обычные символы, а в половине - управляющие. Но даже если и пришла, для этого нужен был ещё один человек, который сказал "Мне норм. В продакшен".
Не поймите неправильно, в случае работы и регэспы парсятся и по логам дебажится, но всё же.
Не абсолютно. Но достаточно часто в реале приходится расставлять какие-то метки (в редакторе, на бумаге), чтобы нарисовать для себя структуру выражения.
О, идея: научить этому IDE. Пусть даже в отдельном окошке, но чтобы рисовал структуру, пояснял и делал фолдинг, где надо. Или уже есть плагин? Или все уже продумали и откинули эту идею?
Про сборку из частей (своими литералами, PEG, ещё как-то) я уже писал.
> Я не понимаю, кому вообще могла прийти идея в половине случаев экранировать обычные символы, а в половине — управляющие.
Исторически. Экранировка управляющих это специфика basic syntax, а не extended syntax, который сейчас в основном развивается всеми движками. А в basic с самого начала выделили небольшой набор символов для меты (.*^$ — и всё?), а дальше развивали через \.
На самом деле, если бы в базовых сделали _все_ меты через \, было бы системнее (и проще развивать). Но уже как получилось, так получилось — ломать миллионы мест уже никто не будет.
> Но даже если и пришла, для этого нужен был ещё один человек, который сказал «Мне норм. В продакшен».
Если посмотрите историю Unix, там таких промежуточных «мне норм, в продакшен», а через месяц «ой не то, надо править» было несколько штук в неделю, какие-то мелочи могли шлифоваться с десятками переделок. В то время была поговорка «типичный пользователь Unix не знает, как на этой неделе вызывается вывод на принтер».
Через несколько лет это всё обтесалось до приемлемого состояния.
У Alex Aiken есть курс Compilers. В самом начале курса так фундаментально разбираются возможности конечных автоматов и соответствующих им регулярных выражений, что после изучения их уже нельзя забыть/не понимать/сторониться. Очень рекомендую.
Погодите, но регулярные выражения были созданы чтоб простыню кода по парсингу скомкать в компактную строку. Её нет никакого смысла раскрывать обратно, вместо этого вы могли бы взять аналогичную простыню кода и попробовать её упростить до, например, упомянутого выше, https://github.com/sprache/Sprache.
Мой первый Pet-проект — Regexoop