Introdução às expressões regulares

Cenário

Esta postagem foi originada do meu blog pessoal, em http://www.mullie.eu/regular-expressions-basics/

As expressões regulares são pouco valorizadas e a maioria dos desenvolvedores tende a saber apenas o básico. Ter um entendimento completo de como as expressões regulares funcionam será extremamente útil quando você precisar analisar dados estruturados.

Em essência, uma expressão regular – ou regex – é um conjunto de instruções para analisar “certos dados” em uma “certa string”. Um exemplo simples de tal regex é /[o-9]+/: instruções para localizar ocorrências numéricas em uma string.

Expressões regulares são mais comumente baseadas em PCRE, um conjunto de instruções derivado da implementação de expressões regulares em Perl. Discutirei a implementação PCRE em PHP em particular. Enquanto em outras linguagens de programação da implementação específica pode diferir, a maioria das línguas modernas têm PCRE ( P erl C ompatible R egular E Xpressions) apoio e os conceitos deve e será em grande parte o mesmo na língua de sua escolha.

Introdução

Expressões regulares são usadas para combinar peças dentro de uma string. Um exemplo fictício simples seria verificar se a string “foo bar baz” contém a palavra “bar” ou substituir todas as ocorrências de “bar” por “qux”.

// check if our string contains 'bar'
if(preg_match('/bar/', 'foo bar baz')) echo 'match!';
// replace 'bar' by 'qux'
echo preg_replace
('/bar/', 'qux', 'foo bar baz');

Esses exemplos fictícios, entretanto, não fazem sentido. PHP (ou a linguagem de sua escolha) tem suas próprias ferramentas adequadas para manipulação de strings simples como essa: strpose str_replaceteria sido suficiente para esses exemplos simples e eles teriam sido até mais rápidos.

As expressões regulares são, de certa forma, uma meta-linguagem. Em segundo plano, há um compilador que analisa sua expressão regular e processa essas instruções na string de destino. Pense nisso como se a string ‘foo bar baz’ fosse iterada, caractere por caractere, e avaliada em relação à expressão regular /bar/. Isso é basicamente o que está acontecendo “nos bastidores”:

<table>
<tr>
<th> Character </th>
<th> Resultado </th>
</tr>
<tr>
<td> f </td>
<td> Não, isso não é b </td>
< / tr>
<tr>
<td> o </td>
<td> Não, nem é b </td>
</tr>
<tr>
<td> o </td>
<td> Não, ainda não b </td>
</tr>
<tr>
<td> </td>
<td> Não, isso não é b </td>
</tr>
<tr>
<td> b </td>
<td> Sim, é b, agora para o próximo caractere </td>
</tr>
<tr>
<td> a </td>
<td> Sim, é um, o próximo, por favor </td>
</tr>
<tr>
<td> r </td>
<td> Sim, é r, combinamos nosso regex completo! </td>
< / tr>
<tr>
<td> </td>
<td> Não, não b </td>
</tr>
<tr>
<td> b </td>
<td> Sim, outro b, avançando. .. </td>
</tr>
<tr>
<td> a </td>
<td> Sim, aqui está outro a, para o próximo </td>
</tr>
<tr>
<td> z < / td>
<td> Não, isso não é r, regex não foi correspondido </td>
</tr>
</table>

Esta, é claro, é praticamente a regex mais simples imaginável.

Imagine que desejamos substituir ‘bar’ e ‘baz’. Usando as funções de manipulação de string nativas do PHP, isso poderia ser feito da seguinte forma: No formato regex, isso seria:echo str_replace(array('bar', 'baz'), 'qux', 'foo bar baz');
echo preg_replace('/ba[rz]/', 'qux', 'foo bar baz');

Neste exemplo, acabamos de introduzir novos metacaracteres de expressão regular:, que inclui classes de caracteres. Este apenas significa que todos os personagens dentro são uma combinação valiosa. Mais sobre esses conceitos mais adiante no post.[ ]

Delimitador

Documentos PHP

As expressões regulares devem sempre ser colocadas entre 2 caracteres para denotar o início e o fim da expressão. Praticamente qualquer caractere (“Um delimitador pode ser qualquer caractere não alfanumérico, não barra invertida ou espaço em branco”) pode ser usado como um delimitador, desde que esteja consistentemente no início e no encerramento da expressão regular, sem nenhum outro (sem escape ) ocorrências do personagem dentro da expressão.

A pena alguns delimitadores “especiais” conhecer são: , , e . Eles não exigem exatamente o mesmo caractere para incluir uma expressão regular, mas o colchete oposto, por exemplo:[ ]( ){ }< >{[0-9]+}

Conselho : Vamos todos usar a barra como um delimitador, por uma questão de uniformidade!

Metacaracteres

Documentos PHP

Os metacaracteres são as instruções reais: o conjunto de ferramentas que permite construir expressões regulares para analisar dados complexos. Uma lista completa de metacaracteres disponíveis:

<table>
<tr>
<th> Caractere </th>
<th> Uso </th>
</tr>
<tr>
<td>. </td>
<td> O ponto corresponde a qualquer caractere (exceto novas linhas, a menos que o modificador s esteja definido): / bg / corresponde a big, bag, bdg, b7g, … </td>
</tr>
<tr>
<td>? </td>
<td> 1: Define que o o caractere principal pode ser correspondido 0 ou 1 vez: /b.?g/ corresponde a bg, big, bag, bdg, b8g, … 2: Ao seguir + ou , ele inverte a ganância daquele quantificador (mais sobre isso mais tarde) . </td>
</tr>
<tr>
<td>
</td>
<td> Define que o caractere inicial pode ser correspondido 0, 1 ou várias vezes: / b.g / corresponde a bg, big, bag, bdg, b8g, boog, b7wrg, … </td>
</tr>
<tr>
<td> + </td>
<td> Define que o caractere principal pode ser correspondido 1 ou várias vezes: /b.+g/ corresponde a big, bag, bdg, b8g, boog, b7wrg, … </td>
</tr>
<tr>
<td> {} </td>
<td> Define que o caractere inicial pode ser correspondido uma quantidade arbitrária de vezes: /b.{2}g/ corresponde a boog, b34g, …; /b.{2,3}g/ corresponde a boog, b7wrg, …; /b.{2,}g/ corresponde a boog, b7wrg, bhe73hg, … </td>
</tr>
<tr>
<td> [] </td>
<td> Define uma classe de caractere,

<tr>
<td> – </td>
<td> Define um intervalo em uma classe de caractere: / [0-9] {2} / corresponde a tudo de 00 a 99. </td>
</tr>
<tr>
<td> ^ </td>
<td> 1: Avalia o início exato de uma string: / ^ O / corresponde à ocorrência de “The” apenas se estiver no início da string em questão. 2: Quando usado em conjunto com [], nega a classe de caractere: / [^ 0-9] / corresponde a qualquer caractere não numérico. </td>
</tr>
<tr>
<td> $ </td>
<td> Avalia o final exato de uma string: / end $ / corresponde à ocorrência de “fim” apenas se estiver no final da string em questão. <



<td> Define um subpadrão e pode ser usado para alternância, referências anteriores e asserções antecipadas / posteriores. </td>
</tr>
<tr>
<td> | </td>
<td> Indica uma alternância em um subpadrão, o que significa que o lado esquerdo ou direito deve ser correspondido: / (Sábado | Dom) dia / corresponderá a “Sábado” e “Domingo”. </td>
</tr>
<tr>
<td> </td>
< td> Caractere de escape, permitindo o uso de qualquer um dos metacaracteres ou delimitador atual como literal para corresponder: / (.
?) / encontra tudo entre parênteses. </td>
</tr>
</table>

Não se preocupe se nem todos os itens acima fizerem sentido imediatamente, iremos abordar a maioria deles com mais detalhes posteriormente neste post. Enquanto você provavelmente poderia desenvolver facilmente seu próprio algoritmo equivalente a, por exemplo, /[o-9]{2}/encontrar um número de 2 dígitos em uma string de destino, você descobrirá que uma análise mais complexa logo ficará muito mais difícil se você fizer isso sem expressões regulares . Geralmente, esses metacaracteres são os blocos de construção que compõem uma expressão regular: uma metlinguagem que – nas suas costas – compila em instruções reais que serão aplicadas à sua string de assunto para encontrar exatamente o que você precisa em um desempenho -como possível.

Exemplo

Imagine que nos sejam apresentados alguns dados em que estamos procurando preços, basicamente um cifrão seguido por um espaço opcional, uma sequência de números, um separador decimal e 2 decimais. A expressão regular nós construir seria algo como: /\$ ?[0-9]+\.[0-9]{2}/. Dividida, esta expressão regular consiste nestas partes:

<table>
<tr>
<th> Parte </th>
<th> Descrição </th>
</tr>
<tr>
<td> / </td>
<td> Delimitador de abertura: início da expressão regular </ td >
</tr>
<tr>
<td> \ $ </td>
<td> A correspondência deve começar com o cifrão. Para que não seja interpretado como caractere de dólar (em vez do metacaractere de dólar), precisamos fazer um escape com uma barra invertida. </td>
</tr>
<tr>
<td> </td>
<td> Após o caractere de dólar, vamos deixar um espaço para iniciar os números. <



<td> O espaço mencionado é opcional, pode ocorrer uma vez ou não, representado por este quantificador. </td>
</tr>
<tr>
<td> [0-9] </td>
<td > Esta é uma classe de caracteres com um intervalo de 0 a 9: qualquer caractere de 0 a 9 (portanto, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) está sujeito a correspondência. </ Td >
</tr>
<tr>
<td> + </td>
<td> O caractere anterior (classe) está sujeito a corresponder pelo menos uma vez com este quantificador, tornando-o efetivamente compatível com qualquer coisa de 0 a 99999 … </ td>
</tr>
<tr>
<td>. </td>
<td> A seguir: ponto separador decimal. Dado que o caractere ponto funciona como metacaractere, também precisamos escapar dele para fazer com que corresponda a um ponto léxico. </td>
</tr>
<tr>
<td> [0-9] </td>
<td> Novamente, intervalo de classe de caractere: cada caractere de 0 a 9. </td>
</tr>
<tr>
<td> {2} </td>
<td> O caractere anterior (classe) deve ser correspondido exatamente 2 vezes, fazendo com que corresponda efetivamente a qualquer coisa de 00 a 99. </td>
</tr>
<tr>
<td> / </td>
<td> Delimitador de fechamento: fim da expressão regular </td>
</tr>

A expressão regular acima deve corresponder a qualquer uma das seguintes possibilidades: $ 99,99 , $ 0,00 , $ 15,00 ,…, mas não: $ 99 , 0,00 $ ou € 0,00 .

Modificadores de padrão

Documentos PHP

Basicamente, os modificadores de padrão são a razão pela qual precisamos de delimitadores na implementação PCRE do PHP: o delimitador de fechamento pode ser seguido por um ou vários modificadores de padrão. Um modificador de padrão irá alterar a maneira como uma certa expressão regular será interpretada depois de executada. Um exemplo simples seria o modificador i, afirmando que os caracteres alfabéticos usados ​​na expressão regular devem ser avaliados sem maiúsculas e minúsculas, o que significa que corresponderia não apenas a “teste”, mas também a “Teste” ou “tEsT”./test/i

Uma lista completa dos modificadores de padrão disponíveis em PHP:

<table>
<tr>
<th> Modificador </th>
<th> Bit de opção </th>
<th> Uso </th>
</tr>
<tr>
<td> i </td>
<td> PCRE CASELESS </td>
<td> Avalia a expressão regular sem caso: / test / i corresponde a “teste”, “Teste”, “tEsT”, … </td>
</tr>
<tr>
<td> m < / td>
<td> PCRE
MULTILINE </td>
<td> Faz com que a regex avalie ^ para o início de uma nova linha na string do assunto. Sem esta opção,



<td> PCRE DOTALL </td>
<td> Faz com que o ponto também corresponda às novas linhas. Se seu padrão corresponder a algo em que o conteúdo a ser correspondido pelo ponto esteja em várias linhas, você precisará que o ponto também inclua novas linhas. </td>
</tr>
<tr>
<td> x </ td >
<td> PCRE
EXTENDED </td>
<td> Ignora espaços em branco sem escape na expressão regular. Particularmente útil ao comentar em linha a regex. </td>
</tr>
<tr>
<td> e </td>
<td> PREG REPLACE EVAL </td>
<td> Uso desencorajado! Esta é uma adição do PHP,substituir função apenas. Se este modificador for definido, a string de substituição será avaliada. </td>
</tr>
<tr>
<td> A </td>
<td> PCRE
ANCORADA </td>
<td> Este modificador é bonito muito equivalente a adicionar ^ ao início de seu padrão: vincula a expressão ao início da string do assunto. </td>
</tr>
<tr>
<td> D </td>
<td> PCRE DOLLAR ENDONLY < / td>
<td> Onde PCREMULTILINE faz ^ avaliar para todos os começos de linha após uma nova linha (por padrão, ele avalia apenas para o início da string completa), este modificador faz exatamente o oposto para $. $ por padrão corresponde ao final da string de assunto completa, bem como ao final de uma linha antes de cada nova linha. O modificador D inverte isso, fazendo com que $ corresponda apenas ao final do assunto completo. </td>
</tr>
<tr>
<td> S </td>
<td> </td>
<td> De acordo com o Na documentação do PHP, esse modificador executará sua expressão regular mais lentamente, “estudando” seu uso, tornando-a mais rápida no longo prazo ao usá-la várias vezes. </td>
</tr>
<tr>
<td> U </ td>

<td> Isso é equivalente a usar? logo após um quantificador no sentido de que ele inverte o comportamento ganancioso padrão, mas o faz para toda a expressão regular. </td>
</tr>
<tr>
<td> X </td>
<td> PCRE EXTRA </td>
<td> De acordo com a documentação do PHP, este modificador “ativará a funcionalidade adicional de PCRE que é incompatível com Perl”, embora não exista essa funcionalidade adicional (ainda). </td>
</tr>
<tr>
<td > J </td>
<td> PCRE
INFO JCHANGED </td>
<td> Isso permite nomes duplicados para subpadrões. </td>



UTF8 </td>
<td> Isso tornará sua expressão regular compatível com UTF-8. </td>
</tr>
</table>

Existem alguns modificadores muito úteis (i, m, s, x, U, J, u), alguns de valor questionável (e, A, D) e alguns modificadores totalmente inúteis (S, X) – e você pode aplicar todos os eles simultaneamente: aplicará o modificador i-, m- e s-à expressão regular anterior./xyz/ims

A implementação desses modificadores de padrão em outras linguagens de programação pode variar. Javascript, por exemplo, suporta apenas i, m (ambas as implementações de PHP) e g:

<table>
<tr>
<th> Modificador </th>
<th> Bit de opção </th>
<th> Uso </th>
</tr>
<tr>
<td> g </td>
<td> < / td>
<td> Poderíamos considerar um Javascript-regex regular equivalente à função preg match () do PHP : a expressão regular irá corresponder apenas à primeira ocorrência na string de assunto. Aplicar o g-modificador o tornaria equivalente à função preg match_all () do PHP , fazendo com que a expressão regular encontrasse todas as correspondências aplicáveis ​​na string em questão. </td>
</tr>
</table>

Exemplo

Como vimos na visão geral dos metacaracteres, o ponto corresponderá a todos os caracteres, exceto as novas linhas.

$html = '<p>This is a paragraph on one single line.</p>
<p>

This however,

is a paragraph

spanning multiple lines

</p>'
;
echo preg_replace
('/<p>.*?<\/p>/', '', $html);

Esta regex corresponde <p>primeiro, seguida por qualquer caractere , seguido por </p>, após o que ela substitui toda a correspondência por uma string vazia.

Ao contrário do que você possa pensar, apenas o primeiro parágrafo será substituído, isso porque o PCRE avalia as strings com base na linha: ele tentará combinar 1 linha com a expressão regular e, se um resultado não for encontrado, ele tentarei combinar a próxima linha. A primeira linha corresponderá a uma abertura

tag, “um monte de caracteres” e uma tag de fechamento </p>. A segunda linha, entretanto, não corresponde a isso, nem a 3ª linha, a 4ª, a 5ª nem a 6ª. No entanto, usando o modificador
PCRE DOTALL, o ponto também corresponderá a uma nova linha e se estenderá pelas linhas 2 a 6. A expressão regular, quando aplicado o modificador PCRE DOTALL, terá a seguinte aparência:preg_replace('/<p>.*?<\/p>/s', '', $html);

Suponha que também desejamos corresponder à tag de parágrafo HTML em maiúsculas

, também poderíamos aplicar o modificador PCRE CASELESS, que faria com que o compilador ignorasse a incompatibilidade de maiúsculas e minúsculas entre nossa regex “p” e a string P. Isso tornaria nossa expressão regular em: `preg replace (‘/

. *? <\ / p> / si ‘,’ ‘, $ html); `

Classes de personagens

Documentos PHP

As classes de caracteres definem uma série de caracteres específicos a serem combinados e são colocados entre colchetes . Qualquer caractere entre os colchetes está sujeito a correspondência na string do assunto.[ ]

Um exemplo simples seria:, /fac[et]/que corresponderá a “face” e “fato”. Não se confunda com as letras dentro da classe de caracteres: a menos que um quantificador siga a classe de caracteres, os dois não podem ocorrer. “faceta”, por exemplo, não seria uma correspondência. A menos que a regex seja /fac[et]{2}/, que na verdade corresponderia a “facet”, bem como “facte”, “factt” e “facee”.

Quando apresentado um intervalo, como , ele corresponderá a qualquer coisa entre e incluindo os limites fornecidos, neste exemplo, todo o alfabeto ocidental em minúsculas.[a-z]

Cuidado : intervalos são construídos sobre a mesa ASCII , o que significa que, por exemplo, não só incluem então todo maiúsculas e minúsculas do alfabeto ocidental, mas também , , , , e `. Para corresponder realmente cada maiúsculas e minúsculas caracteres latinos, podemos usar uma gama composto da seguinte forma: .[A-z][\]^_[a-zA-Z]

Uma combinação de um intervalo e caracteres simples é perfeitamente válido, bem como, como você pode ver nesta expressão regular números, caracteres alfabéticos e sublinhados harmonização: ./[0-9a-z_]/i

Uma classe de personagem também pode ser negada, formando uma instrução “combine tudo, exceto para esses caracteres”. Uma classe de caractere negativo é construída precedendo o caractere com um acento circunflexo ( ^), como este:, que corresponderia a tudo, exceto um caractere numérico.[^0-9]

Um tipo mais popular de classe de caracteres é a notação POSIX. Este é basicamente um intervalo, definido por um termo mais descritivo, como (equivalente a ), (equivalente a ) ou (dígitos hexadecimais, equivalente a ). Pessoalmente, não sou grande fã dessa notação, pois acho mais confusa do que apenas soletrar o intervalo, mas o que quer que flutue em seu barco, eu acho. Certifique-se de dar uma olhada na lista completa de classes de caracteres de notação POSIX se você gosta![:alpha:][a-zA-Z][:lower:][