As sugestões neste artigo concentram-se principalmente na legibilidade das expressões regulares. Ao desenvolver esses hábitos durante o desenvolvimento, você considerará mais claramente o design e a estrutura da expressão, o que ajudará a reduzir bugs e a manutenção do código. você mesmo é o mantenedor deste código. Você pode dar uma olhada e prestar atenção a essas experiências com expressões regulares em seu uso real.
Expressões regulares são difíceis de escrever, difíceis de ler e difíceis de manter. Elas geralmente não combinam com texto inesperado ou perdem texto válido. Esses problemas são causados pelo desempenho e pelos recursos das expressões regulares. A combinação de capacidades e nuances de cada metacaractere torna o código impossível de interpretar sem recorrer a truques intelectuais.
Muitas ferramentas incluem recursos que facilitam a leitura e a escrita de expressões regulares, mas também são pouco idiomáticas. Para muitos programadores, escrever expressões regulares é uma arte mágica. Eles se apegam às características que conhecem e têm uma atitude de otimismo absoluto. Se estiver disposto a adotar os cinco hábitos discutidos neste artigo, você será capaz de criar expressões regulares que resistam à tentativa e erro.
Este artigo usará as linguagens Perl, PHP e Python como exemplos de código, mas os conselhos deste artigo se aplicam a quase todas as implementações de expressão de substituição (regex).
1. Use espaços e comentários
Para a maioria dos programadores, o uso de espaços e recuo em um ambiente de expressão regular não é um problema. Se eles não fizerem isso, seus colegas e até mesmo leigos certamente zombarão deles. Quase todo mundo sabe que comprimir o código em uma linha dificulta a leitura, a gravação e a manutenção. Qual é a diferença para expressões regulares?
A maioria das ferramentas de expressão de substituição possui um recurso de espaço em branco estendido, que permite aos programadores estender suas expressões regulares em várias linhas e adicionar comentários no final de cada linha. Por que apenas um pequeno número de programadores aproveita esse recurso? As expressões regulares do Perl 6 usam padrões de espaço estendido por padrão. Não deixe que o idioma expanda espaços por padrão para você, aproveite-os você mesmo.
Um truque para lembrar sobre espaços em branco estendidos é dizer ao mecanismo de expressão regular para ignorar os espaços em branco estendidos. Dessa forma, se você precisar combinar espaços, deverá especificá-lo explicitamente.
Na linguagem Perl, adicione x no final da expressão regular, então "m/foo bar/" fica na seguinte forma:
m/
foo
bar
/x
Na linguagem PHP, adicione x no final da expressão regular, então ""/foo bar/"" fica no seguinte formato:
"/
foo
bar
/x"
Na linguagem Python, passe o parâmetro de modificação de padrão "re.VERBOSE" para obter a função compilada da seguinte forma:
padrão = r'''
foo
bar
'''
regex = re.compile(pattern, re.VERBOSE)
lida com expressões regulares mais complexas, espaços e comentários se tornarão mais importantes. Suponha que a seguinte expressão regular seja usada para corresponder números de telefone nos Estados Unidos:
(?d{3})? ?d{3}[-.]d{4}
Esta expressão regular corresponde a números de telefone como como "(314)555-4000", você acha que esta expressão regular corresponde a "314-555-4000" ou "555-4000"? A resposta é que nenhum deles corresponde. Escrever tal linha de código esconde as deficiências e os próprios resultados do design. O código de área do telefone é necessário, mas a expressão regular não possui um símbolo separador entre o código de área e o prefixo.
Dividir esta linha de código em várias linhas e adicionar comentários irá expor as deficiências e facilitar a modificação.
Na linguagem Perl deve estar no seguinte formato:
/
(? # parênteses opcionais
d{3} # Código de área telefônico obrigatório
)? # parênteses opcionais
[-s.]? # O delimitador pode ser um travessão, espaço ou ponto final
d{3} # Prefixo de três dígitos
[-.] # Outro delimitador
d{4} # Número de telefone de quatro dígitos
/x
A regex reescrita agora tem um separador opcional após o código de área, de modo que deve corresponder a "314-555-4000", porém o código de área ainda é obrigatório. Outro programador que precisa tornar o código de área do telefone opcional pode ver rapidamente que agora ele não é opcional e uma pequena alteração pode resolver o problema.
2.
Existem três níveis de teste na escrita de testes. Cada nível adiciona uma camada de confiabilidade ao seu código. Primeiro, você precisa pensar cuidadosamente sobre quais códigos você precisa combinar e se pode lidar com incompatibilidades. Segundo, você precisa usar instâncias de dados para testar a expressão regular. Finalmente, você precisa passar formalmente em um painel de teste.
Decidir o que combinar é, na verdade, encontrar um equilíbrio entre combinar os resultados errados e perder os resultados certos. Se o seu regex for muito rigoroso, algumas correspondências corretas serão perdidas; se for muito frouxo, produzirá uma correspondência incorreta; Depois que uma expressão regular é lançada no código real, você pode não perceber ambas. Considere o exemplo de número de telefone acima, que corresponderia a “800-555-4000 = -5355”. Na verdade, as correspondências erradas são difíceis de detectar, por isso é importante planejar com antecedência e testá-las bem.
Continuando com o exemplo do número de telefone, se você estiver confirmando um número de telefone em um formulário da web, poderá ficar satisfeito com um número de dez dígitos em qualquer formato. No entanto, se quiser separar números de telefone de uma grande quantidade de texto, pode ser necessário excluir cuidadosamente correspondências falsas que não atendam aos requisitos.
Ao pensar nos dados que você deseja combinar, anote alguns cenários. Escreva algum código para testar sua expressão regular em um cenário de caso. Para qualquer expressão regular complexa, é melhor escrever um pequeno programa para testá-la, que pode assumir a seguinte forma específica.
Na linguagem Perl:
#!/usr/bin/perl
my @tests = ( "314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
);
foreach meu $teste (@tests) {
if ($teste =~m/
(? # parênteses opcionais
d{3} # Código de área telefônico obrigatório
)? # parênteses opcionais
[-s.]? # O delimitador pode ser um travessão, espaço ou ponto final
d{3} # Prefixo de três dígitos
[-s.] # Outro delimitador
d{4} # Número de telefone de quatro dígitos
/x) {
print "Correspondência em $testn";
}
outro {
print "Falha na correspondência em $testn";
}
}
Na linguagem PHP:
<?php
$testes = array( "314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345");
$regex = "/
(? # parênteses opcionais
d{3} # Código de área telefônico obrigatório
)? # parênteses opcionais
[-s.]? # O delimitador pode ser um travessão, espaço ou ponto final
d{3} # Prefixo de três dígitos
[-s.] # Outro delimitador
d{4} # Número de telefone de quatro dígitos
/x";
foreach ($testes as $test) {
if (preg_match($regex, $teste)) {
echo "Correspondido em $test
;";
}
outro {
echo "Falha na correspondência em $test
;";
}
}
?>;
Na linguagem Python:
import
retests = ["314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
]
padrão = r'''
(? # parênteses opcionais
d{3} # Código de área telefônico obrigatório
)? # parênteses opcionais
[-s.]? # O delimitador pode ser um travessão, espaço ou ponto final
d{3} # Prefixo de três dígitos
[-s.] # Outro delimitador
d{4} # Número de telefone de quatro dígitos
'''
regex = re.compile( pattern, re.VERBOSE ) para teste em testes:
se regex.match(teste):
imprima "Correspondente em", teste, "n"
outro:
print "Failed match on", test, "n"
A execução do código de teste revelará outro problema: ele corresponde a "1234-123-12345".
Em teoria, você precisa integrar todos os testes de todo o aplicativo em uma equipe de teste. Mesmo que você ainda não tenha um grupo de testes, seus testes de expressões regulares serão uma boa base para isso, e agora é um bom momento para começar um. Mesmo que ainda não seja o momento certo para criá-la, você ainda deve executar e testar a expressão regular após cada modificação. Passar um pouco de tempo aqui lhe poupará muitos problemas.
3. Operações alternadas de grupo
O símbolo de operação alternada ( ) tem uma prioridade baixa, o que significa que muitas vezes alterna mais do que o programador pretendia. Por exemplo, a expressão regular para extrair endereços de e-mail do texto pode ser a seguinte:
^CC: To:(.*)
A tentativa acima está incorreta, mas esse bug geralmente não é percebido. O objetivo do código acima é encontrar o texto que começa com “CC:” ou “To:” e extrair o endereço de e-mail no final desta linha.
Infelizmente, se “To:” aparecer no meio de uma linha, esta expressão regular não capturará nenhuma linha começando com “CC:” e, em vez disso, extrairá vários trechos aleatórios de texto. Falando francamente, a expressão regular corresponde a uma linha que começa com “CC:”, mas não captura nada; ou corresponde a qualquer linha que contenha “To:”, mas captura o resto da linha. Normalmente, esta expressão regular capturaria um grande número de endereços de e-mail, então ninguém notaria o bug.
Se você quiser atender à intenção real, adicione parênteses para deixar claro. A expressão regular é a seguinte:
(^CC:) (To:(.*))
Se a intenção real é capturar texto começando com ". CC:" ou "To:" o resto da linha, então a expressão regular correta é:
^(CC: To:)(.*)
Este é um bug comum de correspondência incompleta que você evitará se tiver o hábito de agrupar para operações alternadas Este erro.
4. Use quantificadores soltos
Muitos programadores evitam usar quantificadores soltos como "*?", "+?" e "??", mesmo que eles tornem a expressão mais fácil de escrever e entender.
Quantificadores relaxados correspondem ao mínimo de texto possível, o que ajuda a correspondência exata a ser bem-sucedida. Se você escrevesse "foo(.*?)bar", o quantificador pararia de corresponder na primeira vez que encontrasse "bar", e não na última vez. Isto é importante se você deseja capturar "###" de "foo###bar+++bar". Um quantificador estrito capturaria "###bar++ +". ;), isso causará muitos problemas. Se você usar quantificadores relaxados, poderá gerar novas expressões regulares gastando muito pouco tempo montando tipos de caracteres.
Quantificadores relaxados são de grande valor quando você conhece a estrutura do contexto no qual deseja capturar o texto.
5. Use delimitadores disponíveis.
As linguagens Perl e PHP geralmente usam uma barra esquerda (/) para marcar o início e o fim de uma expressão regular. Se você insiste em usar barras esquerdas em Perl e PHP, você vai querer evitar barras invertidas nas expressões; se você usar aspas em Python, você vai querer evitar barras invertidas (). A escolha de diferentes delimitadores ou aspas pode permitir evitar metade da expressão regular. Isso tornará as expressões mais fáceis de ler e reduzirá possíveis erros causados pelo esquecimento de evitar símbolos.
As linguagens Perl e PHP permitem que quaisquer caracteres não numéricos e de espaço sejam usados como delimitadores. Se você mudar para um novo delimitador, poderá evitar perder a barra esquerda ao corresponder URLs ou tags HTML (como "http://" ou "<br/>;").
Por exemplo, "/http://(S)*/" pode ser escrito como "#http://(S)*#".
Delimitadores comuns são "#", "!" e " ". Se você usar colchetes, colchetes angulares ou chaves, apenas mantenha-os correspondentes. Aqui estão alguns exemplos de delimitadores comuns:
#…# !…! {…} s … … (somente Perl) s[…][…] (somente Perl) s<…>;/…/ (somente Perl)
Em Python, uma expressão regular é primeiramente tratada como uma string. Se você usar aspas como delimitadores, perderá todas as barras invertidas. Mas você pode evitar esse problema usando a string "r''". Se você usar três aspas simples consecutivas para a opção "re.VERBOSE", isso permitirá incluir novas linhas. Por exemplo, regex = "( file://w+)(//d +)" pode ser escrito no seguinte formato:
regex = r'''
(c+)
(d+)
'''