Ich stolperte über eine überraschende (für mich) Tatsache.
console.log("asdf".replace(/.*/g, "x"));
Warum zwei Ersetzungen? Es scheint, dass jede nicht leere Zeichenfolge ohne Zeilenumbrüche genau zwei Ersetzungen für dieses Muster erzeugt. Mit einer Ersetzungsfunktion kann ich sehen, dass die erste Ersetzung für die gesamte Zeichenfolge und die zweite für eine leere Zeichenfolge gilt.
javascript
regex
rekursiv
quelle
quelle
"asdf".match(/.*/g)
return ["asdf", ""]"aa".replace(/b*/, "b")
zu dem man führen wolltebabab
. Und irgendwann haben wir alle Implementierungsdetails von Webbrowsern standardisiert.Antworten:
Gemäß dem ECMA-262- Standard ruft String.prototype.replace RegExp.prototype [@@ replace] auf , das besagt:
wo
rx
ist/.*/g
undS
ist'asdf'
.Siehe 11.c.iii.2.b:
Deshalb
'asdf'.replace(/.*/g, 'x')
ist darin eigentlich:[]
, lastIndex =0
'asdf'
, Ergebnisse =[ 'asdf' ]
, lastIndex =4
''
ergibt =[ 'asdf', '' ]
, liest =4
,AdvanceStringIndex
setzen liest zu5
null
, Ergebnisse =[ 'asdf', '' ]
, RückgabeDaher gibt es 2 Übereinstimmungen.
quelle
'asdf'
leeren Zeichenfolge übereinstimmt''
.Gemeinsam in einem Offline-Chat mit yawkat haben wir eine intuitive Methode gefunden , um zu verstehen, warum
"abcd".replace(/.*/g, "x")
genau zwei Übereinstimmungen erzeugt werden. Beachten Sie, dass wir nicht überprüft haben, ob es vollständig der vom ECMAScript-Standard auferlegten Semantik entspricht. Nehmen Sie dies daher als Faustregel.Faustregeln
(matchStr, matchIndex)
in chronologischer Reihenfolge, die angeben, welche Zeichenfolgenteile und Indizes der Eingabezeichenfolge bereits aufgefressen wurden.matchIndex
Überschreiben des TeilstringsmatchStr
an dieser Position angegeben werden. WennmatchStr = ""
, dann ist der "Ersatz" effektiv Einfügen.Formal wird der Vorgang des Abgleichs und Ersetzens als Schleife beschrieben, wie in der anderen Antwort zu sehen ist .
Einfache Beispiele
"abcd".replace(/.*/g, "x")
Ausgänge"xx"
:Die Matchliste ist
[("abcd", 0), ("", 4)]
Insbesondere enthält es nicht die folgenden Übereinstimmungen, an die man aus folgenden Gründen hätte denken können:
("a", 0)
,("ab", 0)
: Der Quantifizierer*
ist gierig("b", 1)
,("bc", 1)
: Aufgrund des vorherigen Spiels("abcd", 0)
sind die Saiten"b"
und"bc"
bereits aufgefressen("", 4), ("", 4)
(dh zweimal): Die Indexposition 4 ist bereits beim ersten offensichtlichen Match aufgebrauchtDaher
"x"
ersetzt die Ersetzungszeichenfolge die gefundenen Übereinstimmungszeichenfolgen genau an diesen Positionen: An Position 0 ersetzt sie die Zeichenfolge"abcd"
und an Position 4 ersetzt sie""
.Hier können Sie sehen, dass das Ersetzen als echtes Ersetzen eines vorherigen Strings oder nur als Einfügen eines neuen Strings dienen kann.
"abcd".replace(/.*?/g, "x")
mit einem Lazy Quantifier*?
Ausgänge"xaxbxcxdx"
Die Matchliste ist
[("", 0), ("", 1), ("", 2), ("", 3), ("", 4)]
Im Gegensatz zum vorherigen Beispiel, hier
("a", 0)
,("ab", 0)
,("abc", 0)
, oder auch("abcd", 0)
nicht wegen der Trägheit des quantifier enthält , die es streng die kürzeste mögliche Übereinstimmung zu finden begrenzt.Da alle Übereinstimmungszeichenfolgen leer sind, erfolgt keine tatsächliche Ersetzung, sondern das Einfügen
x
an den Positionen 0, 1, 2, 3 und 4."abcd".replace(/.+?/g, "x")
mit einer Lazy Quantifier+?
Ausgänge"xxxx"
[("a", 0), ("b", 1), ("c", 2), ("d", 3)]
"abcd".replace(/.{2,}?/g, "x")
mit einer Lazy Quantifier[2,}?
Ausgänge"xx"
[("ab", 0), ("cd", 2)]
"abcd".replace(/.{0}/g, "x")
Ausgänge"xaxbxcxdx"
nach der gleichen Logik wie in Beispiel 2.Härtere Beispiele
Wir können die Idee des Einfügens anstelle des Ersetzens konsequent ausnutzen, wenn wir nur immer eine leere Zeichenfolge abgleichen und die Position steuern, an der solche Übereinstimmungen zu unserem Vorteil stattfinden. Zum Beispiel können wir reguläre Ausdrücke erstellen, die mit der leeren Zeichenfolge an jeder geraden Position übereinstimmen, um dort ein Zeichen einzufügen:
"abcdefgh".replace(/(?<=^(..)*)/g, "_"))
mit einem positiven Lookbehind(?<=...)
Ausgängen"_ab_cd_ef_gh_"
(nur in Chrome so weit unterstützt)[("", 0), ("", 2), ("", 4), ("", 6), ("", 8)]
"abcdefgh".replace(/(?=(..)*$)/g, "_"))
mit einem positiven Lookahead-(?=...)
Ausgang"_ab_cd_ef_gh_"
[("", 0), ("", 2), ("", 4), ("", 6), ("", 8)]
quelle
while (!input not eaten up) { matchAndEat(); }
. Die obigen Kommentare weisen auch darauf hin, dass das Verhalten vor langer Zeit vor der Existenz von JavaScript entstanden ist.("abcd", 0)
nicht die Position 4 einnimmt, an die das folgende Zeichen gehen würde, die Übereinstimmung mit null Zeichen("", 4)
jedoch iss die Position 4, wo das folgende Zeichen hingehen würde. Wenn ich dies von Grund auf neu entwerfen würde, würde ich die Regel verwenden,(str2, ix2)
die(str1, ix1)
iff folgen könnteix2 >= ix1 + str1.length() && ix2 + str2.length() > ix1 + str1.length()
, was diese Fehlfunktion nicht verursacht.("abcd", 0)
isst Position 4 nicht, weil sie"abcd"
nur 4 Zeichen lang ist und daher nur die Indizes 0, 1, 2, 3 isst. Ich kann sehen, woher Ihre Argumentation kommen könnte: Warum können wir keine("abcd" ⋅ ε, 0)
5 Zeichen lange Übereinstimmung haben, bei der ⋅ stimmtε
die Verkettung und die Breite Null überein? Formal weil"abcd" ⋅ ε = "abcd"
. Ich habe in den letzten Minuten über einen intuitiven Grund nachgedacht, aber keinen gefunden. Ich denke, man muss immer so behandeln,ε
als würde man nur so alleine auftreten wie""
. Ich würde gerne mit einer alternativen Implementierung ohne diesen Fehler oder diese Leistung spielen."" ⋅ ε = ""
obwohl ich nicht sicher bin, welchen Unterschied Sie zwischen""
und machen möchtenε
, was dasselbe bedeutet). Der Unterschied kann also nicht als intuitiv erklärt werden - er ist es einfach.Das erste Match ist offensichtlich
"asdf"
(Position [0,4]). Da das globale Flag (g
) gesetzt ist, wird die Suche fortgesetzt. An diesem Punkt (Position 4) findet es eine zweite Übereinstimmung, eine leere Zeichenfolge (Position [4,4]).Denken Sie daran, dass
*
null oder mehr Elemente übereinstimmen.quelle
Der erste
x
ist einfach für den Ersatz des Matchingsasdf
.Sekunde
x
für die leere Zeichenfolge nachasdf
. Die Suche wird beendet, wenn sie leer ist.quelle