Nadchodzące funkcje wyrażeń regularnych

Jakb Gruber
Yang Guo

W ES2015 wprowadziliśmy w języku JavaScript wiele nowych funkcji, w tym znaczne ulepszenia w składni wyrażeń regularnych z flagami Unicode (/u) i przyklejonych (/y). Jednak od tamtej pory rozwój nie został zatrzymany. W ścisłej współpracy z innymi członkami TC39 (organu zajmującego się standardami ECMAScript) zespół V8 zaproponował i wspólnie opracował kilka nowych funkcji, które sprawią, że regularne wyrażenia będą jeszcze skuteczniejsze.

Proponujemy włączenie tych funkcji do specyfikacji JavaScriptu. Mimo że propozycje nie zostały w pełni zaakceptowane, są już na 3 etapie procesu TC39. Umieściliśmy te funkcje za flagą (patrz poniżej), aby umożliwić nam terminowe przekazanie opinii na temat projektu i wdrożenia odpowiednim autorom oferty przed sfinalizowaniem specyfikacji.

Ten post na blogu przedstawia przedsmak tego ekscytującej przyszłości. Jeśli chcesz ich zapoznać się z przyszłymi przykładami, na stronie chrome://flags/#enable-javascript-harmony włącz eksperymentalne funkcje JavaScriptu.

Nazwane zapisy

Wyrażenia regularne mogą zawierać tak zwane zapisy (lub grupy), które umożliwiają przechwytywanie części dopasowanego tekstu. Obecnie deweloperzy mogli odwoływać się do tych zapisów tylko za pomocą indeksu, który jest określany na podstawie pozycji przechwytywania w obrębie wzorca.

const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2017-07-10');
// result[0] === '2017-07-10'
// result[1] === '2017'
// result[2] === '07'
// result[3] === '10'

Jednak wyrażenia regularne są zwykle trudne do odczytania, pisania i obsługiwania, a odwołania liczbowe mogą powodować komplikacje. Na przykład w przypadku dłuższych wzorców może być trudno określić indeks konkretnego przechwytywania:

/(?:(.)(.(?<=[^(])(.)))/  // Index of the last capture?

Co gorsza, zmiany we wzorcu mogą zmienić indeksy wszystkich istniejących zapisów:

/(a)(b)(c)\3\2\1/     // A few simple numbered backreferences.
/(.)(a)(b)(c)\4\3\2/  // All need to be updated.

Przechwytywanie z nazwą to nadchodząca funkcja, która pomoże rozwiązać te problemy, umożliwiając deweloperom przypisywanie nazw do zapisów. Składnia jest podobna do składni Perl, Java, .Net i Ruby:

const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2017-07-10');
// result.groups.year === '2017'
// result.groups.month === '07'
// result.groups.day === '10'

Do nazwanych zapisów można się też odwoływać za pomocą nazwanych odwołań i String.prototype.replace:

// Named backreferences.
/(?<LowerCaseX>x)y\k<LowerCaseX>/.test('xyx');  // true

// String replacement.
const pattern = /(?<fst>a)(?<snd>b)/;
'ab'.replace(pattern, '$<snd>$<fst>');                              // 'ba'
'ab'.replace(pattern, (m, p1, p2, o, s, {fst, snd}) => fst + snd);  // 'ba'

Szczegółowe informacje na temat tej nowej funkcji znajdziesz w ofercie pakietowej.

flaga dotAll

Domyślnie fragment . w wyrażeniach regularnych pasuje do dowolnego znaku oprócz znaków końca wierszy:

/foo.bar/u.test('foo\nbar');   // false

Oferta pakietowa wprowadza tryb dotAll włączony za pomocą flagi /s. W trybie dotAll dyrektywa . uwzględnia również zakończenie wierszy.

/foo.bar/su.test('foo\nbar');  // true

Szczegółowe informacje na temat tej nowej funkcji znajdziesz w ofercie pakietowej.

Zmiany znaczenia właściwości Unicode

Po wprowadzeniu obsługi standardu Unicode w ES2015 nagle pojawia się o wiele więcej znaków, które można uznać za liczby, na przykład cyfra 1 w kółku: 1 lub znaki słowne, na przykład chiński znak śniegu: 雪.

Żadnej z tych wartości nie można dopasować do kryterium \d ani \w. Zmiana znaczenia tych skrótów spowoduje złamanie istniejących wzorców wyrażeń regularnych.

Zamiast tego wprowadzamy nowe sekwencje zmiany znaczenia właściwości. Pamiętaj, że są one dostępne tylko w przypadku wyrażeń regularnych rozpoznających kod Unicode oznaczonych flagą /u.

/\p{Number}/u.test('①');      // true
/\p{Alphabetic}/u.test('雪');  // true

Odwrotność można dopasować do funkcji \P.

/\P{Number}/u.test('①');      // false
/\P{Alphabetic}/u.test('雪');  // false

Konsorcjum Unicode definiuje o wiele więcej właściwości, np. symboli matematycznych lub japońskich znaków hiragana:

/^\p{Math}+$/u.test('∛∞∉');                            // true
/^\p{Script_Extensions=Hiragana}+$/u.test('ひらがな');  // true

Pełną listę obsługiwanych klas właściwości Unicode znajdziesz w bieżącej ofercie pakietowej. Więcej przykładów znajdziesz w tym artykule.

Potwierdzenia podglądu

Asercje patrzenia z wyprzedzeniem są od samego początku częścią składni wyrażeń regularnych w JavaScripcie. Wreszcie przedstawiamy ich odpowiedniki, które nie są zbyt precyzyjne. Niektórzy z Was mogą pamiętać, że od dłuższego czasu jest to część wersji V8. W celu zaimplementowania flagi Unicode podanej w ustawie ES2015 używamy nawet stwierdzeń „śledzących”.

Nazwa całkiem dobrze opisuje jej znaczenie. Umożliwia ograniczenie dopasowania wzorca tylko wtedy, gdy jest poprzedzony wzorcem w grupie Lookbehind. Dostępne są zarówno pasujące, jak i niepasujące smaki:

/(?<=\$)\d+/.exec('$1 is worth about ¥123');  // ['1']
/(?<!\$)\d+/.exec('$1 is worth about ¥123');  // ['123']

Więcej informacji znajdziesz w poprzednim poście na blogu na temat asercji osób niewiarygodnych, a także w powiązanych przypadkach testowych V8.

Podziękowania

Ten post na blogu nie byłby wyczerpujący, gdyby nie wspomnieć o osobach, które ciężko pracowały, aby to osiągnąć: zwłaszcza mistrzów języka: Mathias Bynens, Dan Ehrenberg, Claude Pache, Briana Terlsona, Thomasa Wooda, Gorkema Yakina i Irregexp guru, a ich implementacji.Erik Corry} ma również te funkcje.Erik Corry.Erik Corry

Mamy nadzieję, że te nowe funkcje wyrażeń regularnych podobają Ci się tak samo jak my.