Expressões Regulares com PHP e Python na prática
Pessoal, estou pensando em preparar um material bem detalhado sobre apache e mod_rewrite, indispensáveis para um bom trabalho de SEO (Search Engine Optimization) com URL Amigáveis.
Antes de me aprofundar nesse assunto com vocês, é necessário ter uma boa base em expressões regulares para podermos personalizar da maneira que desejarmos nossas URLs.
Neste artigo introduziremos as principais expressões regulares que usaremos na próxima abordagem prática. Eu escolhi para esse artigo o PHP pois é bastante interessante usá-lo junto com as "URLs Açucaradas" posteriormente e alguns exemplos em Python (3.0.1) pela facilidade em testar as ERs diretamente no terminal.
Primeiramente o que é uma Expressão Regular?
Expressão Regular não são apenas aqueles pequenos conjuntos de caracteres que geralmente resolvem os maiores e mais difíceis problemas do cotidiano no desenvolvimento de software. Como o próprio nome diz, Regular nos vem algo que não foge a regra, regra no caso definida pelas Expressões. Logo Expressões Regulares podem ser definidas com métodos formais para especificar padrões encontrados nesses textos.
Algo que é regular só nos retorna dois valores, verdadeiro ou falso, não existe o padrão meio-termo. Sendo assim podemos fazer verificações utilizando ERs para saber se determinado texto condiz (Casar, do inglês Match) com determinado padrão especificado anteriormente.
Com ERs podemos validar todo dado que segue padrões tal como e-mail que tem como padrão (texto arroba texto ponto texto), tomando texto como sendo não necessariamente literal. Dentre os usos de Expressões Regulares podemos destacar também na validação de CPF, RG, diversos protocolos como http e ftp, tags de marcação, cartões de crédito e etc...
Quais as desvantagens das Expressões Regulares?
ERs parece ser a resposta para todos os seus problemas, não? Errado, ela pode ser o seu problema se usada de maneira errada e/ou de maneira demasiada. O grande poder das Expressões Regulares pode custar muito caro na performance da sua solução pois consome bastante tempo e memória, tornando a navegação lenta, o ideal é evitar o seu uso se existirem alternativas disponíveis.
Outra variável no uso de expressões regulares é o tamanho do seu projeto, em um site por exemplo, que contêm poucas visitas diárias, o uso de expressões regulares não custará tanto, porém um site com várias requisições simultâneas seria prejudicado, pois você terá muito processamento agregado em ERs.
Por exemplo, se for necessário verificar se uma variável contêm @ (arroba) ao invés de usarmos ERs podemos fazer uma verificação simples com strpos(), strstr(),..
<?php $texto = "ola@mundo"; $existe = strpos($texto,"@"); if($existe === false){ // não existe arroba no texto } else { // existe arroba no texto, e esta na posição $existe } ?>
Falando em PHP, uma maneira funcional de calcular a diferença de performance utilizando ERs e uma verificação simples é o uso da função microtime() do PHP, como vemos abaixo.
Calculando o tempo gasto na checagem de padrão manualmente:
<?php ob_start(); // atribui o tempo inicial $inicial = microtime(); $texto = "ola@mundo"; strpos($texto,"@"); // atribui o tempo final $final = microtime(); /* calcula a diferença entre final e inicial o valor retornado pela função microtime() é em milissegundos então multiplicamos por 1000 para obter o valor em segundos */ echo "Tempo gasto: ".(($final - $inicial)*1000)." segundos."; ?>
O valor retornado poderá variar cada vez que atualizar a página, pois o processador pode estar processando outras threads, o retorno médio no meu computador foi de (0.058 segundos) para encontrar a primeira ocorrência de arroba na variável "ola@mundo".
Calculando o tempo gasto na checagem de padrão usando Expressões Regulares:
<?php ob_start(); // atribui o tempo inicial $inicial = microtime(); $texto = "ola@mundo"; // Expressão Regular utilizada para checar a presença de arroba $er = '/(.*)?@(.*)?/'; preg_match($er,$texto); // atribui o tempo final $final = microtime(); /* calcula a diferença entre final e inicial o valor retornado pela função microtime() é em milisegundos então multiplicamos por 1000 para obter o valor em segundos */ echo "Tempo gasto: ".(($final - $inicial)*1000)." segundos."; ?>
O retorno médio no meu computador foi de (0.090 segundos) para encontrar a primeira ocorrência de arroba na variável "ola@mundo".
Podemos ver que o tempo de processamento é quase o dobro, sendo:
Maneira Alternativa: 0.058 segundos
Expressões Regulares: 0.090 segundos
Essa diferença de tempo aumenta de acordo com a dificuldade da operação realiza, se o nosso texto tivesse 400 linhas, a diferença de tempo seria gritante, fica como exercício para vocês fazerem os testes de performance (benchmarking).
Vamos ao estudo das expressões, eu recomendo para estudo a prática das expressões, pois assim como linguagem de programação ERs só se aprende fazendo.
Essas expressões vistas singularmente também podem ser chamadas de metacaracteres.
Como o nosso conhecimento em ERs é bastante limitado até agora, não podemos ainda criar expressões (Composição de Metacatacteres) então iremos nos restringir a exemplos bem simples.
PHP trabalha com dois tipos diferentes de expressão regular sendo POSIX e Perl-Compatible. POSIX é menos poderosa e as vezes mais lenta que as expressões Perl-Compatible porém pode ser mais fácil de utilizar. Nós usaremos Perl-Compatible pois também usaremos esse padrão no artigo de mod_rewrite.
A função para expressões regulares Perl-Compatible no php é preg_match() e para POSIX é ereg()
1) O metacaracter circunflexo ^:
O circunflexo representa o começo da string. Por exemplo, se quisermos saber se determinado texto começa com 'abc' poderemos prosseguir da seguinte maneira:
^abc em PHP:
<?php // Texto alvo $texto = "abcdefgh"; // Ao adicionarmos o modificador i no final dizemos // que a comparação deve ser case-insensitive $er = '/^abc/i'; if(preg_match($er,$texto)) echo "Começa com abc"; else echo "Não começa com abc"; ?>
^abc em Python:
// Biblioteca para trabalhar com ERs no Python import re // Texto alvo texto = 'abcdefgh' // re.match para comparação re.match(Padrao,String,[bandeiras]) if(re.match('^abc',texto,re.IGNORECASE)): print('Começa com abc ou ABC') else: print('Não começa com abc nem ABC')
2) O metacaracter ponto . :
O ponto implica no fato de aceitar qualquer caracter em determinada posição. Por exemplo, se fossemos brincar com a palavra PHP aceitando como válido o H ser qualquer coisa, P(qualquer caracter)P faremos:
P.P em PHP:
<?php $texto = "PUP"; $er = '/P.P/i'; if(preg_match($er,$texto)) echo "PUP é válido com o padrão estabelecido pela ERs"; else echo "PUP não é válido com o padrão estabelecido pela ERs"; ?>
P.P em Python:
import re texto = 'PUP' // re.match para comparação re.match(Padrao,String,[bandeiras]) if(re.match('P.P',texto,re.IGNORECASE)): print('PUP é válido com o padrão estabelecido pela ERs') else: print('PUP é não válido com o padrão estabelecido pela ERs')
3) O metacaracter Cifrão $:
Semelhante ao circunflexo, porém atua na verificação no final da string, se quisermos saber se além de começar com 'abc' a string termina com 'xyz' poderemos prosseguir da seguinte maneira:
^(abc)(xyz)$ em PHP:
<?php $texto = "abcxyz"; $er = '/^(abc)(xyz)$/i'; echo (preg_match($er,$texto))? 'válido' : 'inválido'; ?>
^(abc)(xyz)$ em Python:
import re texto = 'abcxyz' // re.match para comparação re.match(Padrao,String,[bandeiras]) if(re.match('^(abc)(xyz)$',texto,re.IGNORECASE)): print('válido') else: print('inválido')
4) Os metacaracteres asterisco, mais, interrogação e chaves * + ? {}:
Os quatro metacaracteres são multiplicadores de ocorrências.
I) O multiplicador asterisco * indica que o caractere que esta antes dele poderá ocorrer 0 (zero) ou infinitas vezes.
II) O multiplicador + indica que o caractere anterior poderá ocorrer 1 (uma) ou infinitas vezes (porém necessariamente uma vez).
III) O multiplicador binário ? indica que o caractere anterior poderá ocorrer 1 (uma) ou 0 (zero) vezes, ou seja, poderá existir ou não.
IV) O multiplicador {} funciona de maneira explicita, sendo que podemos ou não, definir o número de repetições. Por exemplo:
a{n} indica que a deve ocorrer exatamente 'n' vezes. Ex: a{3} = aaa
a{n,m} indica que a deve ocorrer pelo menos 'n' vezes e não mais que 'm' vezes. Ex: a{3,6} = aaaaa
a{n,} indica que a deve ocorrer pelo menos n vezes. Ex: a{3,} = aaaaaaa
Sendo assim, agora podemos permitir mais valores entre 'abc' e 'xyz' com a ajuda do metacaracter ponto que aceita qualquer coisa e usando multiplicidade, poderemos prosseguir da seguinte maneira:
^abc(.*)xyz$ em PHP:
<?php $texto = "abcdefghijkmnopqrstuvwxyz"; // Indicamos que além de começar com abc e // terminar com xyz, poderá existir 0 (zero) ou // mais caracteres nesse intervalo $er = '/^abc(.*)xyz$/i'; echo (preg_match($er,$texto))? 'válido' : 'inválido'; ?>
^abc(.*)xyz$ em Python:
import re texto = 'abcdefghijkmnopqrstuvwxyz' // re.match para comparação re.match(Padrao,String,[bandeiras]) if(re.match('^abc(.*)xyz$',texto,re.IGNORECASE)): print('válido') else: print('inválido')
5) A lista [] e Grupos ():
As listas e grupos permitem que você defina um espaço amostral. Por exemplo, se quisermos saber se um registro começa com vogais poderemos usar a lista junto com o circunflexo, dessa maneira ^[aeiou] onde irá casar com palavras como "arquitetura","e-mail","inteligência" e etc..
Com o mesmo circunflexo podemos negar a lista, apenas colocando-o dentro da lista [^aeiou] assim, será válido iniciar com qualquer consoante.
Perceba que a lista só retorna 1 (um) catacter, podemos resolver isso utilizando os Grupos () e utilizando o delimitador "Ou" | (http:|https:|ftp:) Assim poderemos validar esses protocolos da seguinte maneira:
^(http:|https:|ftp:) em PHP:
<?php $texto = "http://www.rafaelcapucho.com"; $er = '/^(http:|https:|ftp:)/i'; echo (preg_match($er,$texto))? 'válido' : 'inválido'; ?>
^(http:|https:|ftp:) em Python:
import re texto = 'http://www.rafaelcapucho.com' // re.match para comparação re.match(Padrao,String,[bandeiras]) if(re.match('^(http:|https:|ftp:)',texto,re.IGNORECASE)): print('válido') else: print('inválido')
I) Estudo de Caso - Validando e-mail com Expressão Regulares
Agora que nós já conhecemos os principais metacaracteres podemos começar a brincar com eles, vamos aos estudos de caso, como poderemos proceder para validar um endereço eletrônico? Primeiro devemos analisar, o padrão estabelecido que no caso seria algo como:
texto @ texto ponto texto
Como não podemos saber quantos caracteres tem os textos previamente, devemos utilizar multiplicadores, devemos multiplicar o metacaracter . (ponto) pois ele casará com qualquer coisa.
Usaremos o multiplicador + (mais) pois o nosso texto deve ter pelo menos 1 caractere, sendo assim temos algo como (.*).
Aplicando ao padrão temos algo como: (.+)@(.+).(.+) perceba que não podemos utilizar o . (ponto) para representar um ponto literal pois ele será tratado como metacaracter então escapamos ele com uma contra-barra \. Teremos:
(.+)@(.+)\.(.+) Estamos esperando o que para testar?
(.+)@(.+)\.(.+) em PHP:
<?php $texto = "palavra@provedor.com"; $er = '/(.+)@(.+)\.(.+)/i'; echo (preg_match($er,$texto))? 'válido' : 'inválido'; ?>
(.+)@(.+)\.(.+) em Python:
import re texto = 'palavra@provedor.com' // re.match para comparação re.match(Padrao,String,[bandeiras]) if(re.match('(.+)@(.+)\.(.+)',texto,re.IGNORECASE)): print('email válido') else: print('email inválido')
II) Estudo de Caso - Removendo Tags com Expressão Regulares
Primeiramente devemos analisar o padrão estabelecido pelas Tags, escolhi para o exemplo Tags HTML pela familiaridade que muitos tem com esse padrão, mapeando teremos algo como:
texto <tag> texto </tag> texto
A idéia é remover as tags, todas as tags independente do texto dentro da tag, ou seja, devemos remover tudo que estiver entre <> seja lá o que estiver escrito.
Se não sabemos previamente o que estará dentro das Tags HTML, podemos representar com a multiplicação indefinida de qualquer catactere .*, que deve estar dentro das Tags, algo como:
<.*?>
Removendo Tags com PHP e Expressões Regulares:
<?php $texto = "texto1<strong>texto2</strong>texto3"; $er = '/<.+?>/i'; // Função preg_replace(pattern, replacement, subject [, limit ]); echo $novoTexto = preg_replace($er,"",$texto); ?>
A saída do código acima será: texto1texto2texto3 onde trocamos todas as tags encontradas pelo conteúdo expresso pelo segundo parâmetro da função preg_replace().
Removendo Tags com Python e Expressões Regulares:
import re texto = 'texto1<strong>texto2</strong>texto3' // Compila a ER com a flag IGNORECASE padrao = re.compile('<.*?>',re.IGNORECASE) // Baseando no padrao, substitui o primeiro parametro // no segundo parametro novoTexto = padrao.sub("",texto) print(novoTexto)
Com esse artigo já temos uma visão considerável sobre ER para utilizarmos no artigo sobre mod_rewrite e outras aplicações.
Dessa maneira, termino esse artigo sobre Expressões Regulares em caso de dúvidas, entre em contato! Bom proveito!










Muito bom o artigo,
possui aplicações diretas no desenvolvimento de softwares,
alem de ser de facil intendimento mesmo para quem não tem conhecimentos profundos em programação!
Eu estava me perguntando como avaliar a eficiencia no tempo de processamento de um codigo, ta respondido!
[...] estou adicionando a vídeo aula sobre Apache Mod_Rewrite que eu fiz para dar continuidade ao Artigo Expressões Regulares com PHP e Python. É muito importante a leitura desse artigo antes de assistir o vídeo tutorial para não ficar [...]
[...] É recomendado que antes de assistir o vídeo o usuário que não tem um bom conhecimento sobre expressões regulares (Regular Expression) leia esse artigo: http://blog.rafaelcapucho.com/expressoes-regulares/expressoes-regulares-com-php-e-python-na-pratica…. [...]
É rapaz, muito bom o artigo mto detalhado.. bem feito.. parabéns…
Metas do Google Analytics + Expressoes Regulares, combinam demais….
flw abração.
Manolo, isto é que é explicação!
Consegui assimilar tudo cara, wlw!