- PVSM.RU - https://www.pvsm.ru -

ECMAScript 6. Регулярные выражения с поддержкой Unicode

В ECMAScript 6 представлены два новых флага для регулярных выражений:

  1. y включает режим «липкого» сопоставления [1].
  2. u включает различные связанные с Unicode опции.

В данной статье объясняется влияние флага u. Эта статья будет Вам полезна, если Вы знакомы с Unicode-проблемами в Javascript [2].
ECMAScript 6. Регулярные выражения с поддержкой Unicode - 1

N.B.! Поскольку при попытке публикации статьи обнаружились проблемы с отображением некоторых используемых в статье Unicode-символов, часть кода была заменена изображениями со ссылками на JSFiddle.

Влияние на синтаксис

Установка флага u в регулярном выражении позволяет использовать escape-последовательности кодовых точек ES6 Unicode [3] (u{...}) в шаблоне.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 2 [4]

При отсутствии флага u такие вещи, как u{1234}, технически могут по-прежнему возникать в шаблонах, но они не будут интерпретироваться как escape-последовательности кодовых точек Unicode. /u{1234}/ эквивалентно записи /u{1234}/, которая соответствует 1234 последовательным символам u вместо символа, соответствующего escape-последовательности кодовых точек U+1234 [5].

Движок Javascript делает так из соображений совместимости. Но с установленным флагом u и такие вещи как a (где a не является escape-последовательностью) больше не будут эквивалентны a. Поэтому, даже если /a/ обрабатывается как /a/, /a/u выбрасывает ошибку, т.к. a не является зарезервированной escape-последовательностью. Это позволяет расширить функционал флага u регулярных выражений в будущей версии ECMAScript. Например, /p{Script=Greek}/u выбрасывает исключение для ES6, но может стать регулярным выражением, соответствующим всем символам греческого алфавита согласно базе данных Unicode, когда соответствующий синтаксис [6] будет добавлен в спецификацию.

Влияние на оператор ‘.

При отсутствии флага u, . соответствует любому символу BMP (базовая многоязыковая плоскость — Basic Multilingual Plane) за исключением разделителей строки [7]. Когда установлен флаг ES6 u, . соответствует также астральным символам.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 3 [8]

Влияние на квантификаторы

В регулярных выражениях Javascript доступны следующие квантификаторы (а также их вариации): *, +, ?, и {2}, {2,}, {2,4}. При отсутствии флага u, если квантификатор следует за астральным символом, он применяется только к низкому суррогату (low surrogate) этого символа.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 4 [9]

С флагом ES6 u квантификаторы применяются к символам целиком, что справедливо даже для астральных символов.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 5 [10]

Влияние на символьные классы

При отсутствии флага 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 обрабатывает его как два отдельных «символа»: по одному на каждую из его суррогатных половинок.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 6 [11]

Флаг ES6 u позволяет использовать цельные астральные символы в символьных классах.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 7 [12]

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

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 8 [13]

Флаг u также влияет на исключающие символьные классы. Например, /[^a]/ эквивалентно /[-x60x62-uFFFF]/, что соответствует любому символу BMP, кроме a. Но с флагом u /[^a]/u соответствует гораздо большему набору всех символов Unicode, кроме a.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 9 [14]

Влияние на escape-последовательности

Флаг u влияет на значение escape-последовательностей D, S, и W. При отсутствии флага u, D, S, и W соответствуют любым символам BMP, которые не соответствуют d, s и w, соответственно.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 10 [15]

С флагом u, D, S, и W также соответствуют астральным символам.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 11 [16]

Флаг u не обращается к их обратным аналогам d, s и w. Было предложено сделать d и wb) более Unicode-совместимыми, но это предложение [17] было отклонено.

Влияние на флаг i

Когда установлены флаги i и u, все символы неявно приводятся к одному регистру с помощью простого преобразования [18], предоставляемого стандартом 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 [19], поскольку U+017F из сопоставляемой строки регулярного выражения преобразуется (canonicalizes) в S. То же самое касается U+212A [20] и 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 [21] для элементов input и textarea позволяет вам указать регулярное выражение для проверки ввода пользователя. Затем браузер обеспечивает вас стилями и скриптами для создания поведения, основанного на достоверности ввода.

ECMAScript 6. Регулярные выражения с поддержкой Unicode - 12 [22]

Флаг u всегда включен для регулярных выражений, скомпилированных через HTML атрибут pattern. Вот демонстрационный пример [23].

Поддержка

На данный момент флаг ES6 u для регулярных выражений доступен в стабильных версиях всех основных браузеров, кроме Safari. Браузеры постепенно начинают использовать этот функционал для HTML атрибута pattern.

Browser(s) JavaScript engine u flag u flag for pattern attribute
Edge Chakra issue #1102227 [24] + issue #517 [25] + issue #1181 [26] issue #7113940 [27]
Firefox Spidermonkey bug #1135377 [28] + bug #1281739 [29] bug #1227906 [30]
Chrome/Opera V8 V8 issue #2952 [31] + issue #5080 [32] issue #535441 [33]
WebKit JavaScriptCore bug #154842 [34] + bug #151597 [35] + bug #158505 [36] bug #151598 [37]

Рекомендации для разработчиков

  • С этого момента используйте флаг u для каждого регулярного выражения, которое вы пишете.
  • … но не добавляйте флаг u слепо в существующие регулярные выражения, поскольку это может неявным образом изменить их смысл.
  • Избегайте комбинирования флагов u и i. Лучше явно включать в регулярное выражение символы всех регистров, чем страдать от неявного программного приведения символов к одному регистру.
  • Используйте транспилер, чтобы убедиться, что ваш код работает везде, включая устаревшие окружения.

Преобразование (transpiling) Unicode ES6 регулярных выражений в ES5

Мной создан regexpu [38], транспилер, который преобразовывает регулярные выражения Unicode ES6 в эквивалентный код ES5, который работает уже сегодня. Это позволит вам играться с новым, развивающимся функционалом.

Автор: Олег Лазарев

Источник [39]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/264235

Ссылки в тексте:

[1] «липкого» сопоставления: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky

[2] Unicode-проблемами в Javascript: https://mathiasbynens.be/notes/javascript-unicode

[3] escape-последовательности кодовых точек ES6 Unicode: https://mathiasbynens.be/notes/javascript-escapes#unicode-code-point

[4] Image: https://jsfiddle.net/ollazarev/012g9w5p/

[5] U+1234: https://codepoints.net/U+1234

[6] синтаксис: https://github.com/mathiasbynens/es-regexp-unicode-property-escapes

[7] разделителей строки: http://ecma-international.org/ecma-262/6.0/#sec-line-terminators

[8] Image: https://jsfiddle.net/ollazarev/wojd7tzo/

[9] Image: https://jsfiddle.net/ollazarev/6uzdzauk/

[10] Image: https://jsfiddle.net/ollazarev/72dL8n4c/

[11] Image: https://jsfiddle.net/ollazarev/qjmftk4z/

[12] Image: https://jsfiddle.net/ollazarev/b2cdLd8n/

[13] Image: https://jsfiddle.net/ollazarev/zw1p6pmq/

[14] Image: https://jsfiddle.net/ollazarev/qznudq5h/

[15] Image: https://jsfiddle.net/ollazarev/xr9fbtu5/

[16] Image: https://jsfiddle.net/ollazarev/e04t7rvx/

[17] это предложение: https://github.com/mathiasbynens/es-regexp-unicode-character-class-escapes#readme

[18] простого преобразования: http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt

[19] U+017F: https://codepoints.net/U+017F

[20] U+212A: https://codepoints.net/U+212A

[21] Атрибут pattern: https://html.spec.whatwg.org/multipage/forms.html#the-pattern-attribute

[22] Image: https://jsfiddle.net/ollazarev/5xbfd4cv/

[23] демонстрационный пример: https://mathiasbynens.be/demo/pattern-u

[24] issue #1102227: https://connect.microsoft.com/IE/feedback/details/1102227/regexp-u-flag-support-is-flaky

[25] issue #517: https://github.com/Microsoft/ChakraCore/issues/517#issuecomment-203707149

[26] issue #1181: https://github.com/Microsoft/ChakraCore/issues/1181

[27] issue #7113940: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7113940/

[28] bug #1135377: https://bugzilla.mozilla.org/show_bug.cgi?id=1135377

[29] bug #1281739: https://bugzilla.mozilla.org/show_bug.cgi?id=1281739

[30] bug #1227906: https://bugzilla.mozilla.org/show_bug.cgi?id=1227906

[31] V8 issue #2952: https://code.google.com/p/v8/issues/detail?id=2952

[32] issue #5080: https://bugs.chromium.org/p/v8/issues/detail?id=5080

[33] issue #535441: https://code.google.com/p/chromium/issues/detail?id=535441

[34] bug #154842: https://bugs.webkit.org/show_bug.cgi?id=154842

[35] bug #151597: https://bugs.webkit.org/show_bug.cgi?id=151597

[36] bug #158505: https://bugs.webkit.org/show_bug.cgi?id=158505

[37] bug #151598: https://bugs.webkit.org/show_bug.cgi?id=151598

[38] regexpu: https://github.com/mathiasbynens/regexpu

[39] Источник: https://habrahabr.ru/post/338366/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best