Heim  >  Artikel  >  Web-Frontend  >  Reguläre Ausdrücke in JavaScript unterscheiden sich in mehrfacher Hinsicht von anderen Sprachen

Reguläre Ausdrücke in JavaScript unterscheiden sich in mehrfacher Hinsicht von anderen Sprachen

高洛峰
高洛峰Original
2016-11-26 16:26:271306Durchsuche

Ich habe viele Sprachen kennengelernt. Ich schätze, ob die regulären Ausdrücke einer Sprache mächtig sind und ob die regulären Ausdrücke und die Grammatik eng miteinander verbunden sind. Derzeit läuft JavaScript recht gut, zumindest mit regulären Literalen. Das mächtigste ist natürlich Perl. Ich habe kürzlich entdeckt, dass sich das Verhalten regulärer Ausdrücke in JavaScript etwas von dem regulärer Ausdrücke in anderen Sprachen oder Tools unterscheidet. Obwohl es für Sie fast unmöglich ist, die regulären Regeln, über die ich weiter unten sprechen werde, zu schreiben und sie fast nie anzuwenden, ist es doch gut, sie zu verstehen. Die Codebeispiele in diesem Artikel werden alle in einer JavaScript-Umgebung ausgeführt, die mit ES5 kompatibel ist. Das heißt, die Leistung in Versionen vor IE9, Versionen um Fx4 usw. unterscheidet sich wahrscheinlich von dem, was ich unten beschreiben werde.

1. Leere Zeichenklasse

Eine Zeichenklasse, die keine Zeichen enthält [], wird als leere Zeichenklasse bezeichnet Es heißt so, weil diese Schreibweise in anderen Sprachen illegal ist und in allen Dokumenten und Tutorials nicht auf eine illegale Syntax eingegangen wird. Lassen Sie mich zeigen, wie andere Sprachen oder Tools diesen Fehler melden:

$echo |.

grep: Unmatched [^

$sed '/[ ]/'

sed: -e Ausdruck #1, Zeichen 4: nicht abgeschlossener Adressregulärer Ausdruck

$echo |. cmd. line:1: /[]/

awk: cmd line:1: ^ nicht abgeschlossener regulärer Ausdruck

awk: cmd line:1: error : Unmatched [^: / []//

$echo |. perl -ne '/[]/'

Unmatched [ in regex; markiert durch < ;-- HIER in m/[ <-- HIER ]/ at -e Zeile 1.

$ruby ​​-ne '/[]/'

-e:1: leere Zeichenklasse: /[]/

$python -c 'import re;re.match("[]","")'

Traceback ( letzter Aufruf zuletzt):

Datei „“, Zeile 1, in

Datei „E:Pythonlibre.py“, Zeile 137, in match

return _compile(pattern, flags).match(string)

Datei „E:Pythonlibre.py“, Zeile 244, in _compile

löst Fehler aus, v # ungültiger Ausdruck

sre_constants.error: unerwartetes Ende des regulären Ausdrucks

In JavaScript ist die leere Zeichenklasse eine zulässige reguläre Komponente, aber ihre Wirkung ist „niemals übereinstimmen“, d. h. die Übereinstimmung mit irgendetwas schlägt fehl . Es entspricht dem Effekt eines leeren negativen Lookaheads (?!):

js> //Leere Zeichenklasse, niemals match

null

js> "whatevern".match(/(?!)/g) //Leerer negativer Vorwärtsblick, niemals Übereinstimmung

null

Offensichtlich ist so etwas in JavaScript von geringem Nutzen.

2. Negieren Sie die leere Zeichenklasse

enthält keine. Die negative Zeichenklasse [^] eines Zeichens kann sein Wird in beiden Fällen als negative leere Zeichenklasse oder leere negative Zeichenklasse bezeichnet, da dieser Begriff meine „eigene Kreation“ ist und mit der oben genannten identisch ist. Die Nullzeichenklasse ist ähnlich. Diese Schreibmethode ist auch in anderen Sprachen illegal:

$echo |.

grep: Unmatched [^

$echo |. /[^]/'

sed: -e Ausdruck #1, Zeichen 5: nicht abgeschlossener Adressregulärer Ausdruck

$echo |.

awk: cmd line:1: /[^]/

awk: cmd line:1: ^ nicht abgeschlossener regulärer Ausdruck

awk: cmd line:1: Fehler: Unmatched [ oder [^: /[^]//

$ echo |. markiert durch <-- HERE in m/[ <-- HERE ^]/ in -e Zeile 1.

$ruby ​​-ne '/[^] /'

-e:1: empty char-class: /[^]/

$python -c 'import re;re.match("[^ ]","")'

Traceback (letzter Aufruf zuletzt):

Datei „ “, Zeile 1, in

Datei „E:Pythonlibre.py“, Zeile 137, in match

return _compile(pattern, flags).match( string)

Datei „E:Pythonlibre.py“, Zeile 244, in _compile

Fehler auslösen, v # ungültiger Ausdruck

sre_constants.error: unerwartetes Ende des regulären Ausdrucks

$

In JavaScript wird die leere Zeichenklasse negiert ist eine legale reguläre Komponente. Ihre Wirkung ist genau das Gegenteil der leeren Zeichenklasse, einschließlich der Symbole „n“, also äquivalent zu den üblichen Zeichen [sS] und [wW]:

js> "whatevern".match(/[^]/g) Negiert die leere Zeichenklasse, passt zu jedem Zeichen

["w", "h", „a“, „t“, „e“, „v“, „e“, „r“, „n“ ]

js> /Komplementäre Zeichenklasse, passt zu jedem Zeichen

["w", "h", "a", "t", "e", "v", "e", "r", "n" ]

Es ist zu beachten, dass es nicht als „immer passender regulärer Ausdruck“ bezeichnet werden kann, da die Zeichenklasse ein übereinstimmendes Zeichen haben muss. Wenn die Zielzeichenfolge leer ist oder vom regulären Ausdruck auf der linken Seite verbraucht wurde, wird Matching ausgeführt scheitern, zum Beispiel:

js> /abc[^]/.test("abc") //Es gibt keine Zeichen nach c und der Abgleich schlägt fehl.

falsch

Wenn Sie die tatsächliche „immer passende Regelmäßigkeit“ wissen möchten, können Sie einen Artikel lesen, den ich zuvor übersetzt habe: „Leere“ Regelmäßigkeit

3.[]] und [^] ]

Das ist relativ einfach zu sagen, das heißt: Wenn in den regulären Ausdrücken von Perl und anderen Linux-Befehlen die Zeichenklasse [] eine rechte eckige Klammer []] gefolgt von einer linken eckigen Klammer enthält, dann Diese rechte eckige Klammer wird als gewöhnliches Zeichen behandelt, das heißt, sie kann nur mit „]“ übereinstimmen. In JavaScript wird dieses reguläre Muster als leere Zeichenklasse gefolgt von einer rechten eckigen Klammer erkannt Nichts ist ebenfalls ähnlich: In JavaScript entspricht es einem beliebigen Zeichen (wobei die leere Zeichenklasse negiert wird), gefolgt von einer rechten Klammer, z. B. „a]“, „b]“, während es in anderen Sprachen zutrifft Jedes andere Zeichen als ].

$perl -e 'print "]" =~ /[]]/'

$js -e 'print(/[]]/.test("]"))'

false

$perl -e 'print " x" =~ /[^]]/'

$js -e 'print(/[^]]/.test("x")) '

falsch

4.$-Ankerpunkt

Einige Anfänger denken, dass $ mit dem Zeilenumbruchzeichen „n“ übereinstimmt. Dies ist völlig falsch (Zero-Breite-Behauptung) Es ist unmöglich, ein echtes Zeichen zu finden, es kann nur mit einer Position übereinstimmen. Der Unterschied, über den ich sprechen möchte, tritt im nicht-mehrzeiligen Modus auf: Sie denken vielleicht, dass im nicht-mehrzeiligen Modus mehrzeilig ist Stimmt $ nicht mit der Position nach dem letzten Zeichen überein? In den meisten anderen Sprachen stimmt $ auch mit der Position überein, wenn das letzte Zeichen in der Zielzeichenfolge das Zeilenumbruchzeichen ist Es entspricht der Position vor dem Zeilenumbruchzeichen, d. h. es entspricht den beiden Positionen auf der linken und rechten Seite des letzten Zeilenumbruchzeichens. In vielen Sprachen gibt es zwei Notationen: Z und Z Sie kennen den Unterschied zwischen ihnen, dann sollten Sie sehen, dass in anderen Sprachen (Perl, Python, PHP, Java, C#...) $ im nicht-mehrzeiligen Modus äquivalent zu Z ist, während in JavaScript $ in Der nicht-mehrzeilige Modus entspricht z (entspricht nur der letzten Position, unabhängig davon, ob das letzte Zeichen ein Zeilenumbruchzeichen ist, da im mehrzeiligen Modus standardmäßig $ verwendet wird). Passen Sie die Position vor jedem Zeilenumbruchzeichen an und natürlich auch das mögliche Zeilenumbruchzeichen. Diese Punkte werden auch im Buch „Regular Guide“ von Yu Sheng erwähnt -e 'print "whatevern" =~ s/ $/replacement Character/rg' //Globaler Ersatz

welches Ersatzzeichen auch immer                                                                                                                                                     >

$js -e 'print("whatevern ".replace(/$/g,"replacement Character"))' //Globaler Ersatz

whatever

Ersetze das Zeichen // Die Position des After-Change-Zeichens wird ersetzt

5. Zitieren

Wir alle wissen, dass es im regulären Normal einen Rückverweis gibt, d Der Zweck besteht darin, es zum erneuten Abgleichen oder als Ersatzergebnis zu verwenden (wird zu $). Was passiert, wenn die referenzierte Erfassung verwendet wird, bevor die Gruppierung begonnen hat? Im regulären Ausdruck /(2(a)){2}/ ist (a) beispielsweise die zweite einfangende Gruppe, aber links davon wird 2 verwendet, um auf das entsprechende Ergebnis zu verweisen Ausdrücke stimmen von links nach rechts überein. Daher handelt es sich nicht um ein strenges Konzept. Nun möchten Sie darüber nachdenken, was der folgende JavaScript-Code zurückgibt

js> /(2(a)){2}/.exec("aaa")

???

Bevor Sie diese Frage beantworten, schauen Sie sich zunächst die an Leistung in anderen Sprachen. Ebenso ist es in anderen Sprachen grundsätzlich ungültig, so zu schreiben:

$echo aaa |(2(a)){2}'

grep: Ungültige Rückreferenz

$echo aaa |. e Ausdruck Nr. 1, Zeichen 12: Unzulässige Rückreferenz

$echo aaa | (2(a)){2}/'

$echo aaa |. perl -ne 'print /(2(a)){2}/'

$echo aaa |. perl -ne 'print $_ = ~ /(2(a)){2}/'

$python -c 'import re;print re.match("(2(a)){2}"," aaa")'

Keine

In awk wird kein Fehler gemeldet, da awk kein umgekehrtes Anführungszeichen unterstützt, 2 wird als Zeichen mit ASCII-Code 2 interpretiert. In Perl, Ruby und Python nein Es wird ein Fehler gemeldet. Ich weiß nicht, warum es so konzipiert ist. Sie sollten alle Perl lernen, aber der Effekt ist derselbe. Es ist unmöglich, erfolgreich zuzuordnen.

In JavaScript wird nicht nur kein Fehler gemeldet, sondern es kann auch erfolgreich abgeglichen werden, ob die Antwort mit der Antwort übereinstimmt, an die Sie gerade gedacht haben:

js> /(2(a)) {2}/.exec("aaa")

["aa", "a", "a"]

Falls Sie was vergessen Das von der Exec-Methode zurückgegebene Ergebnis lautet: „Schauen Sie sich das erste Element an“ ist die vollständige übereinstimmende Zeichenfolge, nämlich RegExp[„$&“], und das Folgende ist der übereinstimmende Inhalt jeder Erfassungsgruppe, nämlich RegExp.$1 und RegExp.$2. Warum kann der Match erfolgreich sein? 2, aber zu diesem Zeitpunkt wurde die zweite Capture-Gruppe (a) noch nicht gerundet, sodass der Wert von RegExp.$2 noch undefiniert ist, sodass 2 mit einem Nullzeichen oder einer „Position“ links vom ersten a in übereinstimmt die Zielzeichenfolge, genau wie ^ und andere Aussagen mit der Breite Null. Der entscheidende Punkt ist, dass die zweite Erfassungsgruppe (a) mit der ersten a in der Zielzeichenfolge übereinstimmt Der Wert von RegExp.$2 wird ebenfalls „a“ zugewiesen und dann der erste. Am Ende der Erfassungsgruppe (die Klammer ganz rechts) ist der Wert von RegExp.$1 ebenfalls „a“. Dann kommt der Quantor {2}. Das heißt, beginnend mit dem ersten a in der Zielzeichenfolge, der neuen Matching-Runde der Regularisierung (2(a)), der entscheidende Punkt ist hier: ist der Wert von RegExp.$2, also der Wert von 2 Matching oder handelt es sich um den zugewiesenen Wert „a“ am Ende der ersten Matching-Runde, lautet die Antwort: „Nein“, die Werte von RegExp.$1 und RegExp.$2 werden auf undefiniert gelöscht, und 1 und 2 sind die gleichen wie beim ersten Mal und entsprechen erfolgreich einem Nullzeichen (entspricht keiner Auswirkung, es ist dasselbe, ob geschrieben oder nicht). Zu diesem Zeitpunkt werden die Werte erfolgreich abgeglichen ​​​​von RegExp.$1 und RegExp.$2 wird wieder zu „a“, und der Wert von RegExp[„$&“] wird zur vollständigen passenden Zeichenfolge „aa“.

Im In der frühen Version von Firefox (3.6) löscht eine neue Runde des Quantifiziererabgleichs den Wert der vorhandenen Erfassungsgruppe nicht, d. h. in der zweiten Runde. Beim Abgleich stimmt 2 mit dem zweiten a überein, also:

js> /(2(a)){2}/.exec("aaa")

["aaa", "aa", "a"]

Darüber hinaus hängt das Ende einer Erfassungsgruppe davon ab, ob die rechte Klammer geschlossen ist, z. B. /(a1){3}/, obwohl 1 verwendet wird, hat die erste Erfassungsgruppe mit dem Abgleich begonnen, dies ist jedoch nicht der Fall bereits beendet. Dies ist auch eine Vorwärtsreferenz, daher ist die übereinstimmende 1 noch leer:

js>/(aaa)

["aaa", "a"]

Ein weiteres Beispiel:

js> b)(a)(r))*/.exec("foobar")

["foobar", undefiniert, undefiniert, undefiniert, "b", "a", "r"]

* ist ein Quantifizierer: $1 ist „f“, $2 ist „o“, $3 ist „o“, $4 ist undefiniert, $5 ist undefiniert, $6 ist undefiniert.

Wenn die zweite Matching-Runde beginnt: Alle erfassten Werte werden auf undefiniert zurückgesetzt.

Nach der zweiten Matching-Runde: $1 ist undefiniert, $2 ist undefiniert, $3 ist undefiniert, $4 ist „b“, $5 ist „a“, $6 ist „r“.

$& wird der Wert „foobar“ zugewiesen und die Übereinstimmung endet.

Die letzte Frage:

js> /(?:^(a)|1(a)|(ab)){2}/.exec( "aab")

?? ??

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn