Expressões regulares são basicamente usadas para processar strings e é muito conveniente usá-las para combinar, extrair e substituir strings.
No entanto, aprender expressões regulares ainda é um pouco difícil. Conceitos como correspondência gananciosa, correspondência não gananciosa, captura de subgrupos e subgrupos não-capturados não são apenas difíceis de entender para iniciantes, mas também para muitas pessoas que trabalham há vários anos.
Então, qual é a melhor maneira de aprender expressões regulares? Como dominar rapidamente expressões regulares?
Recomendo uma forma de aprender regras regulares que considero muito boa: aprendendo através do AST .
O princípio de correspondência das expressões regulares é analisar a sequência de padrões no AST e, em seguida, usar esse AST para corresponder à sequência de destino.
Várias informações na sequência de padrões serão armazenadas no AST após a análise. AST é uma árvore de sintaxe abstrata, como o nome sugere, é uma árvore organizada de acordo com uma estrutura gramatical. A partir da estrutura do AST, você pode facilmente conhecer a sintaxe suportada pelas expressões regulares.
Como visualizar o AST de uma expressão regular?
Você pode visualizá-lo visualmente através do site astexplorer.net:
Ao mudar a linguagem de análise para RegExp, você pode visualizar o AST das expressões regulares.
Como mencionado anteriormente, AST é uma árvore organizada de acordo com a gramática, de modo que várias gramáticas podem ser facilmente classificadas a partir de sua estrutura.
Então vamos aprender várias sintaxes da perspectiva do AST:
Vamos começar com a simples /abc/ Tal regular pode corresponder à string de 'abc', e seu AST é assim:
3 Char, os valores são a, b, c respectivamente, o tipo é simples. A correspondência subsequente consiste em percorrer o AST e combinar esses três caracteres, respectivamente.
Testamos usando a API exec:
O 0º elemento é a string correspondente e o índice é o índice inicial da string correspondente. input é a string de entrada.
Vamos tentar caracteres especiais novamente:
/ddd/ significa combinar três números d é um metacaractere (meta char) com significado especial suportado por expressões regulares.
Também podemos ver através do AST que embora eles também sejam Char, seu tipo é de fato meta:
Qualquer número pode ser correspondido usando o metacaractere d:
Quais são meta caracteres e quais são caracteres simples podem ser vistos rapidamente através do AST.
Regular suporta a especificação de um conjunto de caracteres por meio de [], o que significa que pode corresponder a qualquer um dos caracteres.
Também podemos ver no AST que ele é envolvido por uma camada de CharacterClass, que significa classe de personagem, ou seja, pode corresponder a qualquer caractere que contenha.
Este é realmente o caso em teste:
Expressões regulares suportam a especificação de quantas vezes um determinado caractere é repetido, usando a forma {de,para},
por exemplo, /b{1,3}/ significa que o caractere b é repetido de 1 a 3 vezes , /[abc ]{1,3}/ significa que esta classe de caracteres a/b/c é repetida de 1 a 3 vezes.
Como pode ser visto no AST, esta sintaxe é chamada de Repetição:
Possui um atributo quantificador que representa o quantificador. O tipo aqui é intervalo, de 1 a 3.
Expressões regulares também suportam abreviações de alguns quantificadores, como + indicando 1 a inúmeras vezes, * indicando 0 a inúmeras vezes e ?
Eles são diferentes tipos de quantificadores:
Alguns estudantes podem perguntar: o que significa o atributo ganancioso aqui?
Ganancioso significa ganancioso. Este atributo indica se esta Repetição é uma correspondência gananciosa ou não gananciosa.
Se você adicionar um ? após o quantificador, descobrirá que ganancioso se torna falso, o que significa mudar para correspondência não gananciosa:
Então, o que significa ganancioso e não ganancioso?
Vejamos um exemplo.
A correspondência de repetição padrão é gananciosa e continuará a corresponder enquanto as condições forem atendidas, portanto, acbac pode ser correspondido aqui.
Adicionando um ? depois que o quantificador mudar para não ganancioso, e apenas o primeiro será correspondido:
Esta é a correspondência gananciosa e a correspondência não gananciosa. Por meio do AST, podemos saber claramente que a correspondência gananciosa e a não gananciosa são usadas. O padrão é a correspondência gananciosa.
A expressão regularsuporta colocar parte da string correspondente em um subgrupo e retorná-la por meio de ().
Dê uma olhada no AST:
O AST correspondente é denominado Grupo.
E você descobrirá que ele possui um atributo de captura, cujo padrão é verdadeiro:
O que isto significa?
Esta é a sintaxe para captura de subgrupo.
Se não quiser capturar subgrupos, você pode escrever assim (?:aaa)
Olha, a captura tornou-se falsa.
Qual é a diferença entre captura e não captura?
Vamos tentar:
Ah, acontece que o atributo de captura do Grupo representa extrair ou não.
Podemos ver no AST que a captura é para subgrupos. O padrão é captura, o que significa que o conteúdo do subgrupo é extraído. Você pode mudar para não captura por meio de ?: e o conteúdo do subgrupo não será extraído.
Já estamos familiarizados com o uso de AST para entender a sintaxe regular, mas vamos ver algo um pouco mais difícil:
Expressões regulares suportam a expressão de asserções antecipadas através da sintaxe de (?=xxx), que é usado para julgar um determinado caractere se a string é precedida por uma determinada string.
Através do AST, você pode ver que essa sintaxe é chamada de Assertion, e o tipo é lookahead, que significa olhar para frente, correspondendo apenas ao significado anterior:
O que isto significa? Por que você escreve isso? Qual é a diferença entre /bbb(ccc)/ e /bbb(?:ccc)/?
Vamos tentar:
Pode ser visto nos resultados:
/bbb(ccc)/ corresponde ao subgrupo de ccc e extrai este subgrupo porque o subgrupo padrão é capturado.
/bbb(?:ccc)/ corresponde ao subgrupo de ccc, mas não é extraído porque definimos o subgrupo para não capturar por meio de ?:.
/bbb(?=ccc)/ O subgrupo correspondente a ccc não é extraído, indicando que também não é capturado. A diferença entre ele e ?: é que ccc não aparece no resultado correspondente.
Esta é a natureza de uma asserção lookahead: uma asserção lookahead significa que uma determinada string é precedida por uma determinada string, o subgrupo correspondente não é capturado e a string declarada não aparecerá no resultado correspondente.
Se não for seguido por essa string, não corresponderá:
Altere ?= para ?! Então o significado muda.
Embora a afirmação antecipada ainda seja afirmada primeiro, há um atributo negativo adicional de verdadeiro.
O significado é óbvio. Originalmente, significa que a frente é uma determinada corda. Após a negação, significa que a frente não é uma determinada corda.
Então o resultado correspondente é exatamente o oposto:
Agora ele só corresponde se não houver uma determinada string na frente dele. Esta é uma afirmação antecipada negativa.
Se houver uma asserção anterior, naturalmente haverá uma asserção final, ou seja, ela corresponderá apenas se for seguida por uma determinada string.
Da mesma forma, também pode ser negado:
O AST correspondente a (?<=aaa) é fácil de pensar, que é uma asserção lookbehind:
O AST correspondente a (?<!aaa) é adicionar um atributo negativo:
Asserção antecipada e asserção retrospectiva são as sintaxes de expressões regulares mais difíceis de entender. É muito mais fácil de entender se você aprender por meio do AST ~
As expressões regulares são uma ferramenta muito conveniente para processar strings, mas ainda são um tanto. difícil de aprender. Muitas pessoas ficam confusas sobre sintaxe, como correspondência gananciosa, correspondência não gananciosa, captura de subgrupos, subgrupos não-capturados, asserções lookahead, asserções lookbehind, etc.
Eu recomendo aprender regras regulares por meio do AST. AST é uma árvore de objetos organizada de acordo com a estrutura gramatical. Várias sintaxes podem ser facilmente esclarecidas por meio dos nomes e atributos dos nós AST.
Por exemplo, esclarecemos por meio do AST:
A sintaxe de repetição (Repetição) está na forma de caractere + quantificador. O padrão é correspondência gananciosa (ganancioso é verdadeiro), o que significa correspondência até que não haja correspondência. Adicionar um ? -correspondência gananciosa, pare quando um caractere for correspondido.
A sintaxe do subgrupo (Grupo) é usada para extrair uma determinada string. O padrão é capturar (a captura é verdadeira), o que significa que a extração é necessária. Você pode alternar para a não captura por meio de (?:xxx), que apenas corresponde, mas não extrai. .
Sintaxe de asserção (Asserção) representa uma determinada string antes ou depois dela. Ela é dividida em asserção lookahead e asserção lookbehind. A sintaxe é (?=xxx) e (?<=xxx) respectivamente. negação (negativo é verdadeiro), o que significa exatamente o oposto.
É o profundo entendimento da sintaxe em vários documentos ou o profundo entendimento da sintaxe no compilador?
Não há necessidade de perguntar, deve ser o compilador!
Então é naturalmente melhor aprender gramática por meio da árvore sintática analisada de acordo com a gramática do que pelo documento.
Isso é verdade para expressões regulares e também para aprender outras gramáticas. Se você puder aprender a gramática usando AST, não precisará ler a documentação.