Prefácio
Recentemente, descobri que o desempenho das regularidades em JavaScript em alguns lugares é um pouco diferente daqueles em outros idiomas ou ferramentas e é relativamente alternativa. Embora seja quase impossível para você escrevê -los e dificilmente pode usar as regras que mencionei abaixo, é bom entendê -las, afinal.
Os exemplos de código deste artigo são executados em um ambiente JavaScript que é compatível com o ES5. Ou seja, o desempenho nas versões antes do IE9, versões em torno do FX4, etc. provavelmente será diferente do que eu mencionei abaixo.
1. Classe de personagem vazia
Uma classe de caracteres que não contém [] é chamada de classe empty char class vazio. Acredito que você nunca ouviu os outros chamarem porque, em outros idiomas, este método de redação é ilegal e todos os documentos e tutoriais não falam sobre uma sintaxe ilegal. Deixe -me demonstrar como outros idiomas ou ferramentas relatam este erro:
$ echo | grep '[]' Grep: incomparável [ou [^$ echo | sed '/[]/' sed: -e Expression #1, caractere 4: endereço não terminado Expressão regular $ echo | awk '/[]/' Awk: cmd. Linha: 1: /[] /awk: cmd. Linha: 1: ^ Regexpawk sem terminação: cmd. Linha: 1: Erro: incomparável [ou [^:/[] // $ echo | perl -ne '/[]/' incomparável [em regex; Marcado por <-aqui em m/ [<-aqui]/ AT -E LINE 1. $ ECHO | ruby -ne '/[]/'-e:1: empty char-class: /[]/$python -c 'import re;re.match("[]","")'Traceback (most recent call last): File "<string>", line 1, in <module> File "E:/Python/lib/re.py", line 137, in match return _compile(pattern, flags).match(string) File "E: /python/lib/re.py", linha 244, em _Compile Raise Error, v # inválido expressionsre_constants.error: fim inesperado da expressão regular No JavaScript, a classe de personagem vazia é um componente regular legal, mas seu efeito é "nunca comparado", ou seja, tudo falhará. É equivalente ao efeito de uma (empty negative lookahead)(?!) :
js> "O que quer que/n" .match (/[]/g) // Classe de caracteres nula, nunca corresponda a nulljs> "o que quer que você
Obviamente, esse tipo de coisa é inútil em JavaScript.
2. Negue a classe de personagem vazia
As classes de caracteres negativas que não contêm caracteres são chamadas de classe de char vazio negativo ou classe de char negativa vazia, porque esse substantivo foi "criado" e semelhante à classe de caracteres vazia mencionada acima. Este método de escrita também é ilegal em outros idiomas:
$ echo | grep '[^]' Grep: incomparável [ou [^$ echo | sed '/[^]/' sed: -e Expression #1, caractere 5: endereço não terminado Expressão regular $ echo | awk '/[^]/' Awk: cmd. Linha: 1: /[^] /awk: cmd. Linha: 1: ^ Regexpawk sem terminação: cmd. Linha: 1: Erro: incomparável [ou [^:/[^] // $ echo | perl -ne '/[^]/' incomparável [em regex; Marcado por <-aqui em m/ [<-aqui ^]/ AT -E LINE 1. $ ECHO | ruby -ne '/[^]/'-e:1: empty char-class: /[^]/$python -c 'import re;re.match("[^]","")'Traceback (most recent call last): File "<string>", line 1, in <module> File "E:/Python/lib/re.py", line 137, in match return _compile(pattern, flags).match(string) File "E: /python/lib/re.py", linha 244, em _Compile Raise Error, v # inválido expressionsre_constants.error: final inesperado da expressão regular $ No JavaScript, negar a classe de caracteres nulos é um componente regular legal. Seu efeito é exatamente o oposto do efeito da classe de caracteres nulo. Ele pode corresponder a qualquer caractere, incluindo a nova linha "/n" , ou seja, é equivalente ao comum [/s/S] e [/w/W] :
JS> "qualquer coisa/n" .match (/[^]/g) // classe de caracteres neizontal, corresponde a qualquer personagem ["w", "h", "a", "t", "e", "v", "e", "r"/n "] js>" que o que for/n. "T", "E", "V", "E", "R", "/N"]
Deve -se notar que não pode ser chamado de "regularidade permanente de correspondência", porque a classe de personagem deve ter um personagem para corresponder. Se a string de destino estiver vazia ou tiver sido consumida pela regularidade esquerda, a partida falhará, por exemplo:
js> /abc[^ ]/.test("abc ") // não há caracteres após c, e a correspondência falhou.falseSe você quiser saber as verdadeiras "regras de correspondência permanente", pode conferir um artigo que eu traduzi antes: regras "vazias"
3. []] e [^]]
Isso é relativamente simples, ou seja: nas expressões regulares de Perl e em alguns outros comandos Linux, se a classe de caracteres [] contiver um suporte parado direito imediatamente após []] suporte quadrado esquerdo, o suporte parado direito será considerado um caractere normal, ou seja, ele só pode corresponder "]". No JavaScript, essa regularidade será reconhecida como uma classe de caracteres vazia seguida por um suporte quadrado direito, e a classe de caracteres vazia não corresponderá a nada .[^]] é semelhante: no JavaScript, ele corresponde a um caractere arbitrário (classe de caracteres nulo negativo) seguida por um suporte quadrado direito, como "a]","b]" , enquanto em outros idiomas, ele corresponde a qualquer caractere não que não seja].
$ perl -e 'print "]" = ~/[]]/' 1 $ js -e 'print (/[]]/. test ("]"))' false $ perl -e 'print "x" = ~/[^]]/' 1 $ js -e 'print (/[^]]/. test ("x")' '4. $ Anchor Point
Alguns iniciantes pensam que $ corresponde ao personagem da nova linha "/n" , o que é um grande erro. $ é uma afirmação de largura zero, é impossível corresponder a um personagem real, ele só pode corresponder a uma posição. A diferença que eu quero falar acontece no modo não multi-line: você pode pensar que no modo não multi-line não corresponde à posição após o último caractere? Na verdade, não é tão simples. Na maioria dos outros idiomas, se o último caractere na sequência de destino for o caractere da nova linha "/n" , $ também corresponderá à posição antes da nova linha, ou seja, corresponder às duas posições nos lados esquerdo e direito da quebra da linha no final. Muitos idiomas têm duas anotações /z e /z. Se você souber a diferença entre eles, deve entender que em outros idiomas (Perl, Python, Php, Java, C#...), $ no modo não multi-line é equivalente a /z, enquanto em JavaScript, $ no modo não multi-line é equivalente a /z (ele corresponderá apenas à última posição, independentemente de o último caractere ser um novo). Ruby é um caso especial porque é o padrão de várias linhas. $ no modo multi-line corresponderá à posição antes de cada nova linha e, é claro, também incluirá a quebra de linha que pode aparecer no final. O livro de Yu Sheng, "Diretrizes regulares", também fala sobre esses pontos.
$ perl -e 'impressão "qualquer coisa/n" = ~ s/$/substitua o caractere/rg' // Substituição global qualquer caractere // a posição antes da quebra da linha ser substituída pelo caractere de substituição // a posição após a quebra da linha é substituída pelo raciocínio $ js -e 'impressão ("qualquer que seja/n" .Rearplace (/$/g, "5. DOT Metacharacter "."
Em expressões regulares em JavaScript, o DOT Metacharacter "." pode corresponder a todos os caracteres, exceto quatro terminadores de linha (retorno de carrinho /REFRO, /N-LINE NEWLINE, /separador de linha U2028, /U2029 parágrafo separador), enquanto em outros idiomas comuns, somente a linha newline /n será excluída.
6. Citação para a frente
Todos sabemos que existe uma referência traseira regularmente, ou seja, uma referência de número de barragem + à string que correspondeu ao grupo de captura anterior. O objetivo é corresponder novamente ou como resultado de substituição (/ se torna $). Mas há um caso especial de que, se o grupo de captura referenciado não tiver iniciado (o suporte esquerdo é delimitado), ele usa a referência traseira, o que acontecerá? Por exemplo, regular /(/2(a)){2}/ , (a) é o segundo grupo de captura, mas o resultado correspondente é usado no lado esquerdo. Sabemos que partidas regulares da esquerda para a direita. Esta é a origem do título que encaminha a referência nesta seção. Não é um conceito rigoroso. Então agora você pensa sobre isso, o que o seguinte código JavaScript retornará:
js>/(/2 (a)) {2}/. Exec ("aaa") ???Antes de responder a essa pergunta, vamos dar uma olhada no desempenho em outros idiomas. Da mesma forma, em outros idiomas, escrever dessa maneira é basicamente inválido:
$ echo aaa | Grep '(/2 (a)) {2}' Grep: referência de volta inválida $ echo aaa | sed -r '/(/2 (a)) {2}/' sed: -e expressão nº 1, caractere 12: referência ilegal de referência $ echo aaa | awk '/(/2 (a)) {2}/' $ echo aaa | perl -ne 'print/(/2 (a)) {2}/' $ echo aaa | ruby -ne 'imprimir $ _ = ~/(/2 (a)) {2}/' $ python -c 'import re; print re.match ("(/2 (a)) {2}", "aaa")' nenhumNão há erro no AWK porque o AWK não suporta essa referência de back -referência e /2 é interpretado como um caractere com o código ASCII 2. No entanto, não há erro no Perl Ruby Python. Não sei por que esse design deve ser aprendido por Perl, mas os efeitos são os mesmos. Nesse caso, é impossível corresponder com sucesso.
No JavaScript, não apenas não relata um erro, mas também pode corresponder a ele com sucesso. Vamos ver que a resposta é a mesma da que você acabou de pensar:
js> /(/2(a)) (2a )/.exec("aaa") Para impedir que você esqueça qual é o resultado devolvido pelo método exec , deixe -me dizer. O primeiro elemento é a sequência de correspondência completa, ou seja, RegExp["$&"] , seguida pelo conteúdo de cada correspondência de Capture Group, ou seja, RegExp.$1 e RegExp.$2. Por que a correspondência pode ter sucesso? Qual é o processo de correspondência? Meu entendimento é:
Primeiro, entramos no primeiro grupo de captura (o suporte esquerdo mais à esquerda), onde a primeira partida válida é /2, mas neste momento o segundo grupo de captura (A) ainda não esteve na rodada; portanto, o valor de RegExp.$2 ainda não é undefined , então 2 corresponde a um caractere vazio à esquerda do primeiro a partir do alvo, ou "posição". O ponto é que a partida é bem -sucedida. Continue a ir e, em seguida, o segundo grupo de captura (a) corresponde ao primeiro A na sequência de destino, e o valor do RegExp.$2 também é atribuído a "A" e, em seguida, o primeiro grupo de captura termina (o suporte esquerdo mais à direita mais à direita mais à direita), o valor de RegExp.$1 Também é "a" A ". Depois, há o quantificador {2}, ou seja, após o primeiro A na sequência de alvo, uma nova rodada de correspondência de regular (/2(a)) é iniciada. O ponto -chave está aqui: o valor de RegExp.$2 é que o valor de /2 corresponde ou é o valor atribuído no final da primeira rodada de correspondência "A". A resposta é: "Não", os valores RegExp.$1 e RegExp.$2 serão limpos como undefined e /1 e /2 serão os mesmos da primeira vez, correspondendo com sucesso a um caractere vazio (equivalente a nenhum efeito, seja escrito ou não). O segundo A na sequência de destino é correspondido com sucesso e os valores de RegExp.$1 e RegExp.$2 se tornam "a" novamente, o valor de RegExp["$&"] se torna a string completa, os dois primeiros A: "AA".
Nas versões anteriores do Firefox (3.6), a re-correspondência de quantificadores não limpará o valor do grupo capturado existente, de modo que é, na segunda rodada de partidas, /2 corresponderá ao segundo a, assim:
js> /(/2(a)) (2a)
Além disso, o final de um grupo de captura depende se o suporte de fechamento está fechado. Por exemplo,/(a/1) {3}/. Embora o primeiro grupo de captura tenha começado a corresponder quando /1 é usado, ele ainda não terminou. Esta também é uma referência avançada, então a correspondência entre /1 ainda está vazia:
js> /(a/1) (3a }/.exec("aaau")["aaa "," a "]Outro exemplo:
js> /(?:(f)(o)(o)|(b)(a)(r)*/.exec("foobar"). * é um quantificador. Após a primeira rodada de correspondência: US $ 1 é "F", US $ 2 é "O", US $ 3 é "O", US $ 4 é indefinido, US $ 5 são undefined e US $ 6 são undefined .
No início da segunda rodada de partidas: todos os valores capturados são redefinidos para undefined .
Após a segunda rodada de partidas: US $ 1 é undefined , US $ 2 são undefined , US $ 3 são undefined , US $ 4 é "B", US $ 5 é "A" e US $ 6 é "R".
& é atribuído como "Foobar", e a partida termina.
Resumir
O exposto acima é o conteúdo inteiro que resume as diferenças entre a regularidade do JavaScript e outros idiomas. Espero que o conteúdo deste artigo seja útil para o estudo e o trabalho de todos.