В ECMAScript 6 представлены два новых флага для регулярных выражений:
y
включает режим «липкого» сопоставления.
u
включает различные связанные с Unicode опции.
В данной статье объясняется влияние флага
u
. Эта статья будет Вам полезна, если Вы знакомы с
Unicode-проблемами в Javascript.
N.B.! Поскольку при попытке публикации статьи обнаружились проблемы с отображением некоторых используемых в статье Unicode-символов, часть кода была заменена изображениями со ссылками на JSFiddle.
Влияние на синтаксис
Установка флага
u
в регулярном выражении позволяет использовать
escape-последовательности кодовых точек ES6 Unicode (
\u{...}
) в шаблоне.
При отсутствии флага
u
такие вещи, как
\u{1234}
, технически могут по-прежнему возникать в шаблонах, но они не будут интерпретироваться как escape-последовательности кодовых точек Unicode.
/\u{1234}/
эквивалентно записи
/u{1234}/
, которая соответствует
1234
последовательным символам
u
вместо символа, соответствующего escape-последовательности кодовых точек
U+1234.
Движок Javascript делает так из соображений совместимости. Но с установленным флагом
u
и такие вещи как
\a
(где
a
не является escape-последовательностью) больше не будут эквивалентны
a
. Поэтому, даже если
/\a/
обрабатывается как
/a/
,
/\a/u
выбрасывает ошибку, т.к.
\a
не является зарезервированной escape-последовательностью. Это позволяет расширить функционал флага
u
регулярных выражений в будущей версии ECMAScript. Например,
/\p{Script=Greek}/u
выбрасывает исключение для ES6, но может стать регулярным выражением, соответствующим всем символам греческого алфавита согласно базе данных Unicode, когда соответствующий
синтаксис будет добавлен в спецификацию.
Влияние на оператор ‘.
’
При отсутствии флага
u
,
.
соответствует любому символу BMP (базовая многоязыковая плоскость — Basic Multilingual Plane) за исключением
разделителей строки. Когда установлен флаг ES6
u
,
.
соответствует также астральным символам.
Влияние на квантификаторы
В регулярных выражениях Javascript доступны следующие квантификаторы (а также их вариации):
*
,
+
,
?
, и
{2}
,
{2,}
,
{2,4}
. При отсутствии флага
u
, если квантификатор следует за астральным символом, он применяется только к
низкому суррогату (low surrogate) этого символа.
С флагом ES6
u
квантификаторы применяются к символам целиком, что справедливо даже для астральных символов.
Влияние на символьные классы
При отсутствии флага
u
любой заданный символьный класс может соответствовать только символам BMP. Такие вещи, как
[bcd]
работают как мы того ожидаем:
const regex = /^[bcd]$/;
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('c'), // true
regex.test('d'), // true
regex.test('e') // false
);
Однако, когда в символьном классе используется астральный символ, движок Javascript обрабатывает его как два отдельных «символа»: по одному на каждую из его суррогатных половинок.
Флаг ES6
u
позволяет использовать цельные астральные символы в символьных классах.
Следовательно, цельные астральные символы также могут использоваться в
диапазонах символьных классов, и все будет работать как мы того ожидаем, пока установлен флаг
u
.
Флаг
u
также влияет на
исключающие символьные классы. Например,
/[^a]/
эквивалентно
/[\0-\x60\x62-\uFFFF]/
, что соответствует любому символу BMP, кроме
a
. Но с флагом
u
/[^a]/u
соответствует гораздо большему набору всех символов Unicode, кроме
a
.
Влияние на escape-последовательности
Флаг
u
влияет на значение escape-последовательностей
\D
,
\S
, и
\W
. При отсутствии флага
u
,
\D
,
\S
, и
\W
соответствуют любым символам BMP, которые не соответствуют
\d
,
\s
и
\w
, соответственно.
С флагом
u
,
\D
,
\S
, и
\W
также соответствуют астральным символам.
Флаг
u
не обращается к их обратным аналогам
\d
,
\s
и
\w
. Было предложено сделать
\d
и
\w
(и
\b
) более Unicode-совместимыми, но
это предложение было отклонено.
Влияние на флаг i
Когда установлены флаги
i
и
u
, все символы неявно приводятся к одному регистру с помощью
простого преобразования, предоставляемого стандартом Unicode, непосредственно перед их сопоставлением.
const es5regex = /[a-z]/i;
const es6regex = /[a-z]/iu;
console.log(
es5regex.test('s'), es6regex.test('s'), // true true
es5regex.test('S'), es6regex.test('S'), // true true
// Note: U+017F преобразуется в `S`.
es5regex.test('\u017F'), es6regex.test('\u017F'), // false true
// Note: U+212A преобразуется в `K`.
es5regex.test('\u212A'), es6regex.test('\u212A') // false true
);
Приведение к одному регистру (case-folding) применяется к символам в шаблоне регулярного выражения, а также к символам в сопоставляемой строке.
console.log(
/\u212A/iu.test('K'), // true
/\u212A/iu.test('k'), // true
/\u017F/iu.test('S'), // true
/\u017F/iu.test('s') // true
);
Эта логика приведения к одному регистру применяется и к escape-последовательностям
\w
и
\W
, что также влияет на escape-последовательности
\b
и
\B
.
/\w/iu
соответствует
[0-9A-Z_a-z]
, но также и
U+017F, поскольку U+017F из сопоставляемой строки регулярного выражения преобразуется (canonicalizes) в
S
. То же самое касается
U+212A и
K
. Таким образом,
/\W/iu
эквивалентно
/[^0-9a-zA-Z_\u{017F}\u{212A}]/u
.
console.log(
/\w/iu.test('\u017F'), // true
/\w/iu.test('\u212A'), // true
/\W/iu.test('\u017F'), // false
/\W/iu.test('\u212A'), // false
/\W/iu.test('s'), // false
/\W/iu.test('S'), // false
/\W/iu.test('K'), // false
/\W/iu.test('k'), // false
/\b/iu.test('\u017F'), // true
/\b/iu.test('\u212A'), // true
/\b/iu.test('s'), // true
/\b/iu.test('S'), // true
/\B/iu.test('\u017F'), // false
/\B/iu.test('\u212A'), // false
/\B/iu.test('s'), // false
/\B/iu.test('S'), // false
/\B/iu.test('K'), // false
/\B/iu.test('k') // false
);
Влияние на HTML-документы
Верьте или нет, но флаг
u
также влияет и на документы HTML.
Атрибут pattern для элементов
input
и
textarea
позволяет вам указать регулярное выражение для проверки ввода пользователя. Затем браузер обеспечивает вас стилями и скриптами для создания поведения, основанного на достоверности ввода.
Флаг
u
всегда включен для регулярных выражений, скомпилированных через HTML атрибут
pattern
. Вот
демонстрационный пример.
Поддержка
На данный момент флаг ES6
u
для регулярных выражений доступен в стабильных версиях всех основных браузеров, кроме Safari. Браузеры постепенно начинают использовать этот функционал для HTML атрибута
pattern
.
Рекомендации для разработчиков
- С этого момента используйте флаг
u
для каждого регулярного выражения, которое вы пишете.
- … но не добавляйте флаг
u
слепо в существующие регулярные выражения, поскольку это может неявным образом изменить их смысл.
- Избегайте комбинирования флагов
u
и i
. Лучше явно включать в регулярное выражение символы всех регистров, чем страдать от неявного программного приведения символов к одному регистру.
- Используйте транспилер, чтобы убедиться, что ваш код работает везде, включая устаревшие окружения.
Преобразование (transpiling) Unicode ES6 регулярных выражений в ES5
Мной создан
regexpu, транспилер, который преобразовывает регулярные выражения Unicode ES6 в эквивалентный код ES5, который работает уже сегодня. Это позволит вам играться с новым, развивающимся функционалом.
Полномасштабные транспилеры ES6/ES7, такие как Traceur и Babel, зависят от regexpu при транспиляции
u
.
Дайте мне знать, если вам удастся это сломать.