Prefacio
Recientemente, descubrí que el rendimiento de las regularidades en JavaScript en algunos lugares es algo diferente de los de otros idiomas o herramientas, y es relativamente alternativo. Aunque es casi imposible para usted escribirlos y apenas puede usar las reglas que mencioné a continuación, es bueno comprenderlas después de todo.
Los ejemplos de código en este artículo se ejecutan en un entorno JavaScript que es compatible con ES5. Es decir, el rendimiento en las versiones antes de IE9, versiones alrededor de FX4, etc., es probable que sea diferente de lo que mencioné a continuación.
1. Clase de personaje vacía
Una clase de caracteres que no contiene ningún [] se llama clase empty char class vacío. Creo que nunca has escuchado a otros llamarlo porque en otros idiomas, este método de escritura es ilegal, y todos los documentos y tutoriales no hablan de una sintaxis ilegal. Permítanme demostrar cómo otros idiomas o herramientas informan este error:
$ Echo | Grep '[]' Grep: inigualable [o [^$ Echo | Sed '/[]/' Sed: -e Expresión #1, Carácter 4: Dirección no terminada Expresión regular $ Echo | Awk '/[]/' Awk: cmd. línea: 1: /[] /awk: cmd. Línea: 1: ^ Regexpawk no terminado: CMD. Línea: 1: Error: inigualable [o [^:/[] // $ Echo | perl -ne '/[]/' inigualable [en regex; marcado por <-aquí en m/ [<-aquí]/ at -e línea 1. $ echo | ruby -ne '/[]/' -e: 1: vacía char -class:/[]/$ python -c 'import re; re.match ("[]", "")' TraceBack (la más reciente llamada reciente): file "<string>", línea 1, en <module> archivo "e: /python/lib/re.py", línea 137, en el regreso de la coincidencia _compile (patrón (patrón, flags). "E: /python/lib/re.py", línea 244, en _Compile aumente el error, v # inválido expresiones_constants.error: final inesperado de la expresión regular En JavaScript, la clase de personaje vacía es un componente regular legal, pero su efecto es "nunca coincidir", es decir, todo fallará. Es equivalente al efecto de una (empty negative lookahead)(?!) :
js> "lo que sea/n" .match (/[]/g) // clase de caracteres nulo, nunca coincida con nulljs> "lo que sea/n" .match (/(?)/g) // nulo negativo hacia adelante mirando a la vuelta, nunca coincida nulo
Obviamente, este tipo de cosas es inútil en JavaScript.
2. Niega la clase de personaje vacía
Las clases de caracteres negativos que no contienen ningún personaje se llaman clase de char vacío negativo o clase de char negativo vacío, porque este sustantivo fue "creado por sí mismo" y similar a la clase de caracteres vacía mencionada anteriormente. Este método de escritura también es ilegal en otros idiomas:
$ Echo | Grep '[^]' GREP: inigualable [o [^$ echo | Sed '/[^]/' Sed: -e Expresión #1, Carácter 5: Dirección no terminada Expresión regular $ Echo | Awk '/[^]/' Awk: cmd. Línea: 1: /[^] /AWK: CMD. Línea: 1: ^ Regexpawk no terminado: CMD. Línea: 1: Error: inigualable [o [^:/[^] // $ ECHO | perl -ne '/[^]/' inigualizado [en regex; marcado por <-aquí en m/ [<-aquí ^]/ at -e línea 1. $ echo | Ruby -ne '/[^]/' -E: 1: vacío Char -class:/[^]/$ Python -c 'import re; re.match ("[^]", "")' TraceBack (la más reciente llamada reciente): file "<tred>", línea 1, en <module> file "e: /python/lib/re.py", línea 137, en el partido de regreso _compile (patrón). "E: /python/lib/re.py", línea 244, en _Compile sube error, v # inválido expresiones_constants.error: fin inesperado de la expresión regular $ En JavaScript, negar la clase de personajes nulos es un componente regular legal. Su efecto es justo lo opuesto al efecto de la clase de carácter nulo. Puede coincidir con cualquier carácter, incluida la nueva línea "/n" , es decir, es equivalente al común [/s/S] y [/w/W] :
js> "lo que sea/n" .match (/[^]/g) // clase de caracteres neizontal, coincide con cualquier carácter ["w", "h", "a", "t", "e", "v", "e", "r", "/n"] js> "lo que/n" .match (/[/s]/g) "t", "e", "v", "e", "r", "/n"]
Cabe señalar que no se puede llamar "regularidad de coincidencia permanente", porque la clase de caracteres debe tener un personaje para coincidir. Si la cadena de destino está vacía o ha sido consumida por la regularidad de la izquierda, la coincidencia fallará, por ejemplo:
JS> /ABC[^font>/.test("ABC ") // No hay personajes después de C, y la coincidencia falló.Si desea conocer las verdaderas "reglas de coincidencia permanente", puede consultar un artículo que traducí antes: reglas "vacías"
3. []] y [^]]
Esto es relativamente simple, es decir: en las expresiones regulares de Perl y algunos otros comandos de Linux, si la clase de caracteres [] contiene un soporte cuadrado derecho inmediatamente después []] soporte cuadrado izquierdo, el soporte cuadrado derecho se considerará como un carácter normal, es decir, solo puede coincidir "]". En JavaScript, esta regularidad se reconocerá como una clase de personaje vacía seguida de un soporte cuadrado derecho, y la clase de personajes vacías no coincidirá con nada .[^]] es similar: en JavaScript, coincide con un carácter arbitrario (clase de carácter nulo negativo) seguido de un soporte cuadrado derecho, como "a]","b]" , mientras que en otros idiomas, coincide con cualquier caracteres no].
$ perl -e 'print "]" = ~/[]]/' 1 $ js -e 'print (/[]]/. test (""]))' falso $ perl -e 'imprim "x" = ~/[^]]/' 1 $ js -e 'print (/[^]]/. Test ("x"))' falso)4. $ Punto de anclaje
Algunos principiantes piensan que $ coincide con el personaje de Newline "/n" , que es un gran error. $ es una afirmación de ancho cero, es imposible coincidir con un carácter real, solo puede coincidir con una posición. La diferencia de la que quiero hablar ocurre en el modo no múltiple: ¿podría pensar que en el modo no múltiple, no coincidirá con la posición después del último personaje? En realidad no es tan simple. En la mayoría de los otros idiomas, si el último carácter en la cadena de destino es el carácter de Newline "/n" , $ también coincidirá con la posición antes de que la nueva línea, coincida con las dos posiciones en los lados izquierdo y derecho de la línea de ruptura al final. Muchos idiomas tienen dos anotaciones /z y /z. Si conoce la diferencia entre ellos, debe comprender que en otros idiomas (Perl, Python, PHP, Java, C#...), $ en modo no múltiple es equivalente a /z, mientras que en JavaScript, $ en el modo de línea no multi-línea es equivalente a /z (solo coincidirá con la última posición, independientemente de si la última característica es una nueva línea). Ruby es un caso especial porque es predeterminado al modo de múltiples líneas. $ en modo múltiple coincidirá con la posición antes de cada nueva línea y, por supuesto, también incluirá el descanso de línea que puede aparecer al final. El libro de Yu Sheng "Directrices regulares" también habla sobre estos puntos.
$ perl -e 'imprimir "lo que/n" = ~ s/$/reemplazar carácter/rg' // reemplazo global cualquier carácter // la posición antes de que la ruptura de la línea sea reemplazada por el carácter de reemplazo // la posición después de que la ruptura de la línea se reemplaza por el impreso $ js -e '
5. Dot Metacharacter "."
En expresiones regulares en JavaScript, el Dot Metacharacter ". puede coincidir con todos los caracteres, excepto cuatro terminadores de línea (retorno /retorno de carruaje R, /n-línea newline, /separador de línea U2028, separador de párrafo U2029), mientras que en otros idiomas comunes, solo se excluirá la línea nueva /n.
6. Cita adelante
Todos sabemos que hay una referencia posterior en una referencia regular, es decir, una referencia de número de barra de inactividad + a la cadena que ha coincidente en el grupo de captura anterior. El propósito es coincidir nuevamente o como resultado de reemplazo (/ se convierte en $). Pero hay un caso especial de que si el grupo de captura referenciado no ha comenzado (el soporte izquierdo está limitado), usa la referencia posterior, ¿qué sucederá? Por ejemplo, el regular /(/2(a)){2}/ , (a) es el segundo grupo de captura, pero el resultado coincidente se usa en su lado izquierdo. Sabemos que los partidos regulares de izquierda a derecha. Este es el origen de la referencia del título de reenvío en esta sección. No es un concepto estricto. Así que ahora lo piensas, ¿qué devolverá el siguiente código JavaScript:
js>/(/2 (a)) {2}/. Exec ("AAA") ???Antes de responder a esta pregunta, echemos un vistazo a la actuación en otros idiomas. Del mismo modo, en otros idiomas, escribir de esta manera es básicamente inválido:
$ Echo AAA | GREP '(/2 (a)) {2}' GREP: referencia de retroceso no válido $ echo aaa | sed -r '/(/2 (a)) {2}/' Sed: -e Expresión #1, Carácter 12: Referencia de espalda ilegal $ ECHO AAA | AWK '/(/2 (a)) {2}/' $ ECHO AAA | perl -ne 'print/(/2 (a)) {2}/' $ echo aaa | ruby -ne 'print $ _ = ~/(/2 (a)) {2}/' $ python -c 'import re; print re.match ("(/2 (a)) {2}", "aaa")' ningunoNo hay error en AWK porque AWK no admite esta referencia de backs, y /2 se interpreta como un carácter con el código ASCII 2. Sin embargo, no hay error en Perl Ruby Python. No sé por qué Perl debe aprender este diseño, pero los efectos son los mismos. En este caso, es imposible igualar con éxito.
En JavaScript, no solo no informa un error, sino que también puede igualarlo con éxito. Veamos que la respuesta es la misma que la que acabas de pensar:
js> /(/2(a))) =2h /.
Para evitar que olvide lo que devuelve el resultado por exec , permítanme decir. El primer elemento es la cadena de coincidencia completa, es decir, RegExp["$&"] , seguido del contenido de cada grupo de captura coincidencia, es decir, RegExp.$1 y RegExp.$2. ¿Por qué puede ser exitoso la coincidencia? ¿Cuál es el proceso de correspondencia? Mi entendimiento es:
Primero, ingresamos al primer grupo de captura (el soporte de la izquierda más izquierda), donde el primer partido válido es /2, pero en este momento el segundo grupo de captura (a) aún no ha estado en la ronda, por lo que el valor de RegExp.$2 todavía está undefined , por lo que /2 coincide con un carácter vacío en la izquierda de la primera A en la cadena objetivo, o "posición", al igual que ^ y otras aviones cero urbos. El punto es que el partido es exitoso. Continúe, y luego el segundo grupo de captura (A) coincide con la primera A en la cadena de destino, y el valor de RegExp.$2 también se asigna a "A", y luego el primer grupo de captura finaliza (la derecha más derecha es la derecha más derecha), el valor de RegExp.$1 también es "A". Luego está el cuantificador {2}, es decir, después de la primera en la cadena de destino, se inicia una nueva ronda de coincidencia de regular (/2(a)) . El punto clave está aquí: el valor de RegExp.$2 es que el valor de /2 coincide o es el valor asignado al final de la primera ronda de coincidencia "A". La respuesta es: "No", los valores RegExp.$1 y RegExp.$2 se borrarán como undefined , y /1 y /2 serán los mismos que la primera vez, coincidir con éxito con un carácter vacío (equivalente a ningún efecto, ya sea escrito o no). El segundo A en la cadena de destino se combina correctamente, y los valores de RegExp.$1 y RegExp.$2 se convierten en "A" nuevamente, el valor de RegExp["$&"] se convierte en la cadena de correspondencia completa, las dos primeras a: "AA".
En versiones anteriores de Firefox (3.6), el reunión de cuantificadores no borrará el valor del grupo capturado existente, por lo que es, en la segunda ronda de partidos, /2 coincidirá con el segundo A, por lo tanto:
js> /(/2(a))) =2h /.
Además, el final de un grupo de captura depende de si el soporte de cierre está cerrado. Por ejemplo,/(a/1) {3}/. Aunque el primer grupo de captura ha comenzado a coincidir cuando se usa /1, aún no ha terminado. Esta también es una referencia hacia adelante, por lo que la coincidencia entre /1 todavía está vacía:
js> /(a/1) =3'/.exec("aaa")["aaa "," A "]Otro ejemplo:
js> /(?:(f)(o)(o)(b)(a)(r))*/.Exec("foobar")[&"foobar ", indefinido, indefinido, indefinido," B "," A "," R "] * es un cuantificador. Después de la primera ronda de coincidencia: $ 1 es "F", $ 2 es "O", $ 3 es "O", $ 4 está indefinido, $ 5 está undefined y $ 6 está undefined .
Al comienzo de la segunda ronda de partidos: todos los valores capturados se restablecen a undefined .
Después de la segunda ronda de partidos: $ 1 está undefined , $ 2 está undefined , $ 3 está undefined , $ 4 es "B", $ 5 es "A" y $ 6 es "R".
y se asigna como "Foobar", y el partido termina.
Resumir
Lo anterior es todo el contenido que resume las diferencias entre la regularidad de JavaScript y otros idiomas. Espero que el contenido de este artículo sea útil para el estudio y el trabajo de todos.