lookaheads (a lookbehinds) v regulárních výrazech JavaScriptu

Regulární výrazy (regulární výrazy) jsou samy o sobě výzvou. Mně to vždy trvá pár minut, než pochopím, co konkrétní regulární výraz dělá. Jsou kouzelné a o jejich užitečnosti není pochyb.

Dnes jsem si dal nedělní ranní kávu a propracoval se přes slide deck „Co je nového v ES2018“ od Benedikta Meurera a Mathiase Bynense.

Na těchto snímcích je tolik užitečných informací. Kromě nových jazykových funkcí, jako jsou asynchronní iterace, vlastnosti šíření objektů a pojmenované skupiny zachycení v regulárních výrazech (🎉), pokrývají výhledy regulárních výrazů (a nadcházející vzhled).

Tu a tam mi cestu zkříží předpovědi v regulárních výrazech JavaScriptu a musím přiznat, že jsem je nikdy nemusel používat, ale nyní je protějšek hledá budou také v jazyce, takže jsem se rozhodl přečíst si nějakou dokumentaci a konečně se naučit, co jsou tyto regex lookaheads a lookbehind.

Regulační vyhledávání v JavaScriptu

Můžete definovat vzory, které se shodují pouze tehdy, když je následuje nebo nenásleduje jiný vzor s předběžnými informacemi.

Článek MDN o regulárních výrazech popisuje dva různé typy předběžných dotazů v regulárních výrazech.

Pozitivní a negativní výhledy:

  • x(?=y) – pozitivní výhled (odpovídá „x“, když za ním následuje „y“)
  • x(?!y) – negativní výhled (odpovídá 'x', pokud není následováno 'y')

Zachycené skupiny v JavaScriptu – podobně vypadající společníci

No dobře... x(?=y) – to je složitá syntaxe, pokud se mě ptáte. Co mě zpočátku zmátlo, je, že obvykle používám () pro zachycené skupiny nebo nezachycující skupiny ve výrazech JavaScript.

Podívejme se na příklad zachycené skupiny:

const regex = /\w+\s(\w+)\s\w+/;

regex.exec('eins zwei drei');
// ['eins zwei drei', 'zwei']
//                      /\
//                      ||
//                captured group
//                 defined with
//                    (\w+)

Regulární výraz výše zachycuje slovo (zwei v tomto případě), který je obklopen mezerami a dalším slovem.

Přehledy regulárních výrazů nejsou jako zachycené skupiny

Podívejme se na typický příklad, který najdete, když si přečtete o předběžných dotazech v regulárních výrazech JavaScriptu.

// use positive regex lookahead
const regex = /Max(?= Mustermann)/;

regex.exec('Max Mustermann');
// ['Max']
regex.exec('Max Müller');
// null

Tento příklad odpovídá Max vždy, když za ním následuje mezera a Mustermann jinak se neshoduje a vrátí null . Pro mě je zajímavé, že odpovídá pouze Max a ne vzor definovaný ve výhledu ((?= Mustermann) ). Toto vyloučení se může po práci s regulárními výrazy zdát divné, ale když se nad tím zamyslíte, je to rozdíl mezi výhledy a skupinami. Pomocí předběžných dotazů můžete testovat řetězce proti vzorům, aniž byste je zahrnuli do výsledné shody.

Příklad "Max Mustermann" není příliš užitečný, ale pojďme se ponořit do pozitivních a negativních výhledů s případem použití v reálném světě.

Pozitivní regulární výraz v JavaScriptu

Předpokládejme, že máte dlouhý řetězec Markdown, který obsahuje seznam lidí a jejich preferencí v jídle. Jak byste přišli na to, kteří lidé jsou vegani, když je všechno jen dlouhý řetězec?

const people = `
- Bob (vegetarian)
- Billa (vegan)
- Francis
- Elli (vegetarian)
- Fred (vegan)
`;

// use positive regex lookahead
const regex = /-\s(\w+?)\s(?=\(vegan\))/g;
//                |----|  |-----------|
//                  /            \
//           more than one        \
//           word character      positive lookahead
//           but as few as       => followed by "(vegan)"
//           possible

let result = regex.exec(people);

while(result) {
  console.log(result[1]);
  result = regex.exec(people);
}

// Result:
// Billa
// Fred

Pojďme se rychle podívat na regulární výraz a pokusit se jej formulovat slovy.

const regex = /-\s(\w+?)\s(?=\(vegan\))/g;

Dobře... pojďme na to!

Negativní/negující regulární výrazy v JavaScriptu

Na druhou stranu, jak byste přišli na to, kdo není vegan?

const people = `
- Bob (vegetarian)
- Billa (vegan)
- Francis
- Elli (vegetarian)
- Fred (vegan)
`;

// use negative regex lookahead
const regex = /-\s(\w+)\s(?!\(vegan\))/g;
//                |---|  |-----------|
//                  /          \
//           more than one      \
//           word character     negative lookahead
//           but as few as      => not followed by "(vegan)"
//           possible

let result = regex.exec(people);

while(result) {
  console.log(result[1]);
  result = regex.exec(people);
}

// Result:
// Bob
// Francis
// Elli

Pojďme se rychle podívat na regulární výraz a zkusme jej také formulovat slovy.

const regex = /-\s(\w+)\s(?!\(vegan\))/g;

Regexové předpovědi budou mít brzy společnost od pohledu zezadu

Lookbehinds bude fungovat stejně, ale u vzorů vedoucích. Lookaheads zvažují vzory po odpovídající části zatímco hledí zvažují vzory dříve . Lookbehinds jsou v Chrome dnes podporovány. Budou také k dispozici jako pozitivní lookbehind x(?<=y) a negativní lookbehind x(?<!y) .

Když otočíme vzorové řetězce a upravíme regulární výraz tak, aby používal lookbehinds, vše stále funguje.

const people = `
- (vegetarian) Bob
- (vegan) Billa
- Francis
- (vegetarian) Elli
- (vegan) Fred
`;

// use positive regex lookbehind
const regex = /(?<=\(vegan\))\s(\w+)/g;
//             |------------|  |---|  
//                  /             \__
//         positive lookbehind        \
//       => following "(vegan)"     more than one
//                                  word character
//                                  but as few as possible

let result = regex.exec(people);

while(result) {
  console.log(result[1]);
  result = regex.exec(people);
}

// Result:
// Billa
// Fred

Poznámka:Obvykle doporučuji RegExr pro pohrávání si s regulárními výrazy, ale lookbehinds zatím nejsou podporovány.

Další zdroje

Pokud vás zajímají další špičkové funkce, podívejte se na snímky Mathiase a Benedikta o nových funkcích přicházejících do JavaScriptu, protože přijdou mnohem zajímavější věci.

Další poznámka na okraj:Pokud vyvíjíte v prohlížeči, nezapomeňte nejprve zkontrolovat podporu lookbehinds. V době psaní tohoto článku nejsou ve Firefoxu podporovány.

Abychom si zapamatovali syntaxi pro dopředný pohled a dopředný pohled, vytvořil jsem o tom rychlý cheat sheet.