Pull to refresh

Comments 47

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

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

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

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

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

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

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

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

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

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

email validation

Шаблон для валидации email согласно стандарту. Вы все еще хотите связываться с регулярками?

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

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 там тоже только с латиницей работают?

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

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

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

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

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

Например, сделаем длинную строку из латинских '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) может иметь смысл впихивать парсеры в прокрустово ложе регулярок.
Sign up to leave a comment.