Как стать автором
Обновить

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

Если какую-либо задачу можно решить не прибегая к регуляркам, лучше так и поступить.
Тоже касается ситуаций, когда регулярка сложнее чем например #start(.*)end#

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

Собственно, регулярные выражения и есть компактная форма описания таких правил.

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

Да, только лапшой это может быть ещё медленнее сделано.

Я бы сказал скорее всего будет сделано намного медленней.

Странно, что не упомянули про известную уязвимость регулярных выражений — ReDoS.

Это работает далеко не для всех регулярок (в основном речь про вложенные символы повторения наподобие (a+)+ ), но многие движки к этому чувствительны.
Это не значит, что от регекспов стоит отказываться, просто при обработке пользовательского ввода (особенно на бекенде) нужно быть осторожным и иметь в виду такие подводные камни.

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

как то слишком сложно чтоб проверить что есть @ и что точка идет после собачки

blabla@?.com - корректно? Собака есть, точка есть. А как такая строка обрабатываться будет: "bla1@gmail.com,bla2@gmail.com,...,bla1000@gmail.com", сервис отправит 1000 подтверждений?

Есть RFC2822, приведенное выше регулярное выражение написано в соответствии с ним.

blabla@?.com
А разве не корректно? вроде юникод в домене может быть.

А в соответствии ли? Насколько я помню, грамматика RFC2822 в принципе нерегулярна. И, следовательно, не может быть полностью описана никаким регулярным выражением.

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

Хотя расширение chrome regex search иногда бывает полезным.

Знаете, я очень люблю регулярные выражения. И при этом полностью согласен с поговоркой) Просто у каждого инструмента есть свои границы применимости. Когда приходит аналитик, и просит отобрать все идентификаторы, где есть буквы, один разработчик уточняет, русские или английские (забыв про турецкие и весь остальной юникод), после чего пишет какую-то жуть, а другой спрашивает "есть буквы, или есть не-цифры". После чего использует супер-сложную регулярку `\D`

bla+0001@gmail.com тоже корректно (как и любая буквенно-цифровая последовательность после символа +)

А точно фраза "@sidorov вроде писал что-то про эту задачу." является адресом? При этом "Pete(A wonderful ) chap) <pete(his account)@silly.test(his host)>" — адрес
и "Pete(A wonderful ) chap) <pete(his account)@[IPv6:0:0:1]>" тоже (точки после собаки нету)

точка может быть и до собачки

Расскажите об этом упырям из ГМыла. Они не считают учётки, отличающиеся только точками, различными.

Зато считают адрес валидным. То есть можно считать, что не только все адреса с тегами после + ваши, но и все адреса с точками в любом месте тоже ваши.

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

А вот с такими подробностями уже соглашусь с вашей оценкой из предыдущего комментария.

Email сложнее, чем мы привыкли его видеть)

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

const checkEmail = (email) => {
  let input = document.createElement(`INPUT`);
  input.type = `email`
  input.value = email;

  return input.checkValidity();
};

есть вариант без простынки с регуляркой :)

А в той что вы указали начало точно не про email, как будто бессмысленная регулярка для объема, возврат каретки, перевод строки, причем необязательные, за которыми должен обязательно идти один пробел или табулятор и вся скобка тоже необязательна.

Как минимум \r тоже должна быть \r? Далеко не все на винде :)

есть вариант без простынки с регуляркой

Таким образом мы перекладываем проблему валидации на реализацию в какой-то версии какого-то браузера. При этом реализации от версии к версии и от движка к движку могут меняться (могут появляться или исправляться баги).

Необязательно ставить задачу ребром и использовать или регулярку или код. Можно разбить проверку на подзадачи и комбинировать код с регуляркой. И там и там есть преимущества.

Тогда весь код становится и короче и понятнее.

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

А задачей фронтенда в этом случае — уберечь пользователя от банальной ошибки, вроде попытки ввести логин вместо адреса. Проверить @ и наличие после неё точки для этой цели достаточно, регулярка с этой задачей справится легко и элегантно.

следом за ним может быть (а может и не быть) пробельный символ \s*;

Может быть, а может не быть - это квантификатор ?, эквивалентный {0,1}. То, что Вы написали - это возможно пустая последовательность из произвольного количества пробелов, то есть {0,}.

А как насчёт того, что в строке могут быть до и после любые символы?

Задача — валидировать номер паспорта РФ, у которого достаточно понятная структура, состоящая из серии и номера: 99 99 №999999. Кроме серии и номера могут присутствовать пробелы и символ номера. Звёздочка помогает нам поймать любое количество пробельных символов между цифрами, если они есть (может быть, а может не быть). Символ № тоже необязателен, и паттерн будет верным даже при таком наборе: 9999999999. А вот любые знаки до и после уже не совпадают с шаблоном паспорта РФ, поэтому поле будет считаться невалидным.

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

А чем это отличается от пробела, о котором ранее в самой же статье и шла речь?

Вы правы, мы могли бы воспользоваться позитивной ретроспективной проверкой и проверить на наличие пробельного символа (?<=\s) вместо (?<![а-яё]), но тогда выражение не совпало бы в начале строки.

Вообще-то гораздо аккуратнее и правильнее тут использовать \b, он убережет и от других проблем — например, разработчик не учел возможность знаков препинания до и после захватываемой строки.

в JavaScript \b работает только с латинским алфавитом

О как. Это действительно неустранимо в js или там какие-то настройки покрутить надо? А всякие альфанумерики etc там тоже только с латиницей работают?

увы, нет такой настройки, а с альфанумериками всё ок

Слабаки! Я на регулярках разбор HTML делал.

Тогда я не знал, что нельзя. И распарсил. : )

Ну какое-то подмножество можно конечно

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

С регами бывают ещё какие-то странные проблемы, когда выскакивает переполнение стека.

Например, сделаем длинную строку из латинских 'a':

const longStr = 'a'.repeat(1e7);

Вызов /a+/.test(longStr) отрабатывает нормально и возвращает true, а вызов /(a)+/.test(longStr) приводит к ошибке. С более короткими строками отрабатывает без проблем. Конкретно на моем макбуке ошибка проявляется в Хроме и Node 14, если длина строки 3355430 и больше

могу предположить что слишком большое количество сохраненных состояний срабатывает как зацикливание, по крайней мере FireFox ровно об этом и говорит. Если отключить сохранение /(?:a)+/.test(longStr) всё прекрасно работает

Правда ли, что от регулярок у разработчиков одни проблемы

<shameless_plug>

Да

</shameless_plug>

Другое дело, что рабочие альтернативы в Javascript могут иметь существенно более низкую производительность, чем непрозрачные регулярки, реализованные в движке в нативном коде, так что для Javascript (а также Ruby, Python, PHP) может иметь смысл впихивать парсеры в прокрустово ложе регулярок.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий