Was ist der Unterschied zwischen eckigen Klammern und Klammern in einer Regex?

100

Hier ist ein regulärer Ausdruck, den ich zur Verwendung in JavaScript erstellt habe:

var reg_num = /^(7|8|9)\d{9}$/

Hier ist ein weiterer Vorschlag meines Teammitglieds.

var reg_num = /^[7|8|9][\d]{9}$/

Die Regel ist, eine Telefonnummer zu validieren:

  • Es sollten nur zehn Zahlen sein.
  • Die erste Zahl soll 7, 8 oder 9 sein.
Jayapal Chandran
quelle

Antworten:

123

Diese regulären Ausdrücke sind äquivalent (für Matching-Zwecke):

  • /^(7|8|9)\d{9}$/
  • /^[789]\d{9}$/
  • /^[7-9]\d{9}$/

Die Erklärung:

  • (a|b|c)ist ein regulärer Ausdruck "ODER" und bedeutet "a oder b oder c", obwohl das Vorhandensein von Klammern, die für den OP erforderlich sind, auch die Ziffer erfasst . Um genau gleichwertig zu sein, würden Sie Code verwenden (?:7|8|9), um es zu einer nicht erfassenden Gruppe zu machen .

  • [abc]ist eine "Zeichenklasse", die "jedes Zeichen aus a, b oder c" bedeutet (eine Zeichenklasse kann Bereiche verwenden, z. B. [a-d]= [abcd]).

Der Grund, warum diese regulären Ausdrücke ähnlich sind, ist, dass eine Zeichenklasse eine Abkürzung für ein "oder" ist (jedoch nur für einzelne Zeichen). Im Wechsel können Sie auch so etwas tun, (abc|def)was sich nicht in eine Zeichenklasse übersetzen lässt.

Böhmisch
quelle
30
(7|8|9)und [789]sind nicht gleichwertig, weil der erste erfasst, der letztere nicht. (?:7|8|9)wäre auf der anderen Seite gleichwertig (ich denke du weißt das natürlich ...).
hochl
Ich sehe diesen regulären Ausdruck : [<<|>>|\]\]|\[\[]. Aufgrund des Kontexts weiß ich, dass Regex versucht, mit <<oder >>oder [[oder übereinzustimmen ]]. Aber von dem, was Sie gesagt haben, sollte es übereinstimmen <oder >oder [oder oder ]. Wenn Sie |zwischen verwenden [], verhalten sich die Klammern anders?
Daniel Kaplan
1
@DanielKaplan wird nicht |innerhalb einer Zeichenklasse verwendet [...], es sei denn, Sie möchten das Pipe-Zeichen selbst abgleichen . Auch das Duplizieren von Zeichen in einer Zeichenklasse hat keine Auswirkung - eine Zeichenklasse ist eine Liste von Zeichen und entspricht genau einem von ihnen. Ich vermute, Sie möchten eine Gruppe , die normale runde Klammern verwendet:(<<|>>|\]\]|\[\[)
Bohemian
57

Der Rat Ihres Teams ist fast richtig, bis auf den Fehler, der gemacht wurde. Sobald Sie herausgefunden haben, warum, werden Sie es nie vergessen. Schauen Sie sich diesen Fehler an.

/^(7|8|9)\d{9}$/

Was dies tut:

  • ^und $bezeichnet verankerte Übereinstimmungen, die behaupten, dass das Untermuster zwischen diesen Ankern die gesamte Übereinstimmung ist. Die Zeichenfolge stimmt nur überein, wenn das Untermuster mit der Gesamtheit übereinstimmt, nicht nur mit einem Abschnitt.
  • ()bezeichnet eine Erfassungsgruppe .
  • 7|8|9bezeichnet entweder aus passenden 7, 8oder 9. Dies geschieht mit Wechsel , was der Rohrbetreiber |tut - abwechselnd. Dieser Rückweg zwischen den Wechsel: Wenn der erste Wechsel nicht übereinstimmt, muss der Motor zurückkehren, bevor sich die Zeigerposition während des Abgleichs bewegt hat, um den nächsten Wechsel fortzusetzen. Während die Zeichenklasse nacheinander vorrücken kann . Sehen Sie diese Übereinstimmung auf einer Regex-Engine mit deaktivierten Optimierungen:
Pattern: (r|f)at
Match string: carat

Wechsel

Pattern: [rf]at
Match string: carat

Klasse

  • \d{9}entspricht neun Ziffern. \dist ein Kurzzeichen-Metazeichen, das mit beliebigen Ziffern übereinstimmt.
/^[7|8|9][\d]{9}$/

Schau dir an, was es tut:

  • ^und $bezeichnet auch verankerte Übereinstimmungen.
  • [7|8|9]ist eine Charakterklasse . Alle Zeichen aus der Liste 7, |, 8, |, oder 9können angepasst werden, damit die |in nicht ordnungsgemäß hinzugefügt. Dies stimmt ohne Rückverfolgung überein.
  • [\d]ist eine Zeichenklasse, die den Metacharakter bewohnt \d. Die Kombination der Verwendung einer Zeichenklasse und eines einzelnen Metazeichens ist übrigens eine schlechte Idee, da die Abstraktionsebene die Übereinstimmung verlangsamen kann. Dies ist jedoch nur ein Implementierungsdetail und gilt nur für einige Regex-Implementierungen. JavaScript ist keines, aber es verlängert das Untermuster etwas.
  • {9} gibt an, dass das vorherige Einzelkonstrukt insgesamt neun Mal wiederholt wird.

Der optimale Regex ist /^[789]\d{9}$/, weil /^(7|8|9)\d{9}$/unnötige Erfassungen erforderlich sind , was bei den meisten Regex-Implementierungen zu einer Leistungsminderung führt (zufällig eine, wenn man bedenkt, dass die Frage ein Schlüsselwort varim Code verwendet (dies ist wahrscheinlich JavaScript). Die Verwendung vonWenn Sie für das Preg-Matching auf PCRE ausgeführt werden, wird das fehlende Backtracking optimiert. Wir sind jedoch auch nicht in PHP, sodass Klassen []anstelle von Alternativen verwendet werden| bietet also einen Leistungsbonus, da das Match nicht zurückverfolgt wird und daher beide Übereinstimmungen schneller als bei Verwendung Ihres vorheriger regulärer Ausdruck.

Unihedron
quelle
6
Nur aus Interesse, aus welchem ​​Programm stammt dieser Screenshot?
Mr Mystery Guest
12

Die ersten beiden Beispiele verhalten sich sehr unterschiedlich, wenn Sie sie durch etwas ERSETZEN. Wenn Sie diesbezüglich übereinstimmen:

str = str.replace(/^(7|8|9)/ig,''); 

Sie würden 7 oder 8 oder 9 durch die leere Zeichenfolge ersetzen.

Wenn Sie dazu passen

str = str.replace(/^[7|8|9]/ig,''); 

Sie werden ersetzen 7oder 8oder 9ODER DIE VERTIKALE BAR !!!! durch die leere Zeichenfolge.

Ich habe das gerade auf die harte Tour herausgefunden.

Sheila
quelle
6
Willkommen bei SO! Ersetzen oder abgleichen ist einfach falsch. Viele Leute machen diesen Fehler und kommen normalerweise damit durch - manchmal jahrelang -, weil ihre Eingabezeichenfolgen niemals eine Pipe enthalten ( |).
Alan Moore