Нужен ли флаг Ungreedy для сопоставления до конца строки $?

Вопрос или проблема

Это мой регулярное выражение: ^([\p{L}]+\.?)(?:[-\s']?[\p{L}]+)*$
Я хотел бы использовать его для поиска названий городов.

С тестовыми строками:

Тревеллерс Рест
Сан-Франциско
Трёхсловный город
abc-bbc
Сент-Катаринс
город xx
Сан-Франциско
Вал-д'Ор
Преск'иль
Ниагара на озере
Ниагара-на-озере
Мюнхен
торонто
тоРонТо
города Квебека
Провенс-Алпы-Лазурный Берег
Иль-де-Франс
Копавогу
Гардабяр
Саударкурокур
Торлаксхофн
上海
東京

Он находит только два соответствия без флага Ungreedy.

Может кто-нибудь объяснить, почему это происходит?

введите описание изображения здесь

Проблема в последней части вашего выражения

^([\p{L}]+\.?)(?:[-\s']?[\p{L}]+)*$
                        ^^^^^^^^

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

Чтобы избежать этого, вы можете явно указать регулярному выражению избегать новых строк перед тем, как этот сегмент выражения будет сопоставлен, используя негативный обратный просмотр, вот так:

^([\p{L}]+\.?)(?:[-\s']?(?<!\n)[\p{L}]+)*$

Обратите внимание на добавленный (?<!\n). Это говорит движку, что перед этой частью совпадения не должно быть новой строки

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

Рабочий пример

Ответ или решение

Нужно ли использовать флаг Ungreedy для соответствия до конца строки $?

Введение

Регулярные выражения (regex) — мощный инструмент для поиска и манипуляции текстовой информацией. Однако их эффективность во многом зависит от правильности составления самих выражений. В вашем случае вы сталкиваетесь с проблемой сопоставления названий городов на основе регулярного выражения ^([\p{L}]+\.?)(?:[-\s']?[\p{L}]+)*$. Появление неожиданного поведения в виде недостаточного числа совпадений может возникать из-за особенностей работы с флагами, в частности, с флагом Ungreedy.

Понимание флагов Greedy и Ungreedy

Greedy (жадный) — флаг, который по умолчанию активирован в регулярных выражениях, означает, что регулярное выражение будет пытаться захватить как можно больше символов, прежде чем проверить соответствие условию. Это может привести к ситуации, когда, например, часть строки (такая как новая строка) будет включена в матч.

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

Проблема с регулярным выражением

Ваше регулярное выражение ^([\p{L}]+\.?)(?:[-\s']?[\p{L}]+)*$ доходит до конца строки $, что может повлиять на сопоставление, если присутствуют новые строки в тестируемых строках. Одной из ключевых причин, по которой железобетонно не срабатывает единичное тестирование этих названий, является то, что часть вашего выражения, ответственный за захват дополнительных слов, потенциально захватывает символы новой строки, особенно при использовании флага многострочного gm.

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

Решение проблемы

Для оптимизации вашего регулярного выражения я бы рекомендовал использовать негативную проверку смотрящего за поиком (negative lookbehind). Это позволит убедиться в отсутствии символа новой строки перед захватом:

^([\p{L}]+\.?)(?:[-\s']?(?<!\n)[\p{L}]+)*$

Здесь конструкция (?<!\n) указывает, что перед символом не должно быть новой строки, таким образом исключается влияние символов новой строки на сопоставление.

Заключение

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

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

Оцените материал
Добавить комментарий

Капча загружается...