Muitos programadores conhecem a alegria de criar uma expressão regular rápida, hoje em dia com a ajuda de algum serviço da Web, ou mais tradicionalmente em um prompt interativo, ou talvez escrevendo um pequeno script com a expressão regular em desenvolvimento e uma coleção de casos de teste . Em qualquer um dos casos, o processo é iterativo e bastante rápido: continue hackeando a cadeia de aparência enigmática até que ela corresponda e capture o que você deseja e rejeite o que você não deseja.
Para um resultado simples, pode ser algo como isto, como um regexp Java:
Pattern re = Pattern.compile(
"^\\s*(?:(?:([\\d]+)\\s*:\\s*)?(?:([\\d]+)\\s*:\\s*))?([\\d]+)(?:\\s*[.,]\\s*([0-9]+))?\\s*$"
);
Muitos programadores também sabem o que é necessário para editar uma expressão regular ou apenas codificar em torno de uma expressão regular em uma base de código herdada. Com um pouco de edição para dividir, o regexp acima ainda é muito fácil de entender para qualquer pessoa razoavelmente familiarizada com o regexps, e um veterano do regexp deve ver imediatamente o que ele faz (responda no final do post, caso alguém queira fazer o exercício de descobrir por si mesmos).
No entanto, as coisas não precisam ficar muito mais complexo para um regexp para se tornar verdadeiramente coisa, e mesmo com a documentação diligente (que todo mundo só escrevo- claro faz para todos regexps complexos que escrevem ...), modificando as regexps torna-se um tarefa difícil. Também pode ser uma tarefa muito perigosa, se o regexp não for cuidadosamente testado por unidade (mas todos obviamente terão testes de unidade abrangentes para todos os seus regexps complexos, positivos e negativos ...).
Então, para encurtar a história, existe uma solução / alternativa gravação / leitura para expressões regulares sem perder seu poder? Como seria a regexp acima com uma abordagem alternativa? Qualquer idioma é bom, embora uma solução em vários idiomas seja melhor, na medida em que os regexps são em vários idiomas.
E então, o que o regexp anterior faz é o seguinte: analisa uma sequência de números em formato 1:2:3.4
, capturando cada número, onde os espaços são permitidos e somente 3
necessários.
fonte
Respostas:
Várias pessoas mencionaram a composição de partes menores, mas ninguém deu um exemplo ainda, então aqui está o meu:
Não é o mais legível, mas acho que é mais claro que o original.
Além disso, C # tem o
@
operador que pode ser prefixado a uma corda, a fim de indicar que é para ser tomado literalmente (sem caracteres de escape), entãonumber
seria@"([\d]+)";
fonte
[\\d]+
e[0-9]+
deve ser apenas\\d+
(bem, alguns podem achar[0-9]+
mais legível). Não vou editar a pergunta, mas você pode querer corrigir esta resposta.\d
corresponderão a qualquer coisa considerada um número, mesmo em outros sistemas de numeração (chinês, árabe, etc.), enquanto[0-9]
corresponderão apenas aos dígitos padrão. Eu padronizei\\d
, no entanto, e considerei ooptionalDecimal
padrão.A chave para documentar a expressão regular é documentá-la. Com demasiada frequência, as pessoas lançam o que parece ser ruído de linha e deixam assim.
Dentro de perl, o
/x
operador no final da expressão regular suprime o espaço em branco, permitindo documentar a expressão regular.A expressão regular acima se tornaria:
Sim, consome um pouco de espaço em branco vertical, embora seja possível reduzi-lo sem sacrificar muita legibilidade.
Observando esta expressão regular, podemos ver como ela funciona (e não funciona). Nesse caso, esse regex corresponderá à string
1
.Abordagens semelhantes podem ser adotadas em outro idioma. A opção python re.VERBOSE funciona lá.
O Perl6 (o exemplo acima foi para o perl5) leva isso adiante com o conceito de regras que leva a estruturas ainda mais poderosas que o PCRE (fornece acesso a outras gramáticas (livres de contexto e sensíveis ao contexto) do que apenas regulares e estendidas).
Em Java (de onde este exemplo se baseia), pode-se usar a concatenação de cadeias para formar o regex.
É certo que isso cria muito mais
"
na string, possivelmente levando a alguma confusão lá, pode ser mais facilmente lido (especialmente com sintaxe destacada na maioria dos IDEs) e documentado.A chave é reconhecer o poder e a natureza "escrever uma vez" nas quais expressões regulares frequentemente se enquadram. Escrever o código para evitar isso defensivamente, de modo que a expressão regular permaneça clara e compreensível, é fundamental. Formaizamos o código Java para maior clareza - as expressões regulares não são diferentes quando a linguagem oferece a opção de fazê-lo.
fonte
O modo "detalhado" oferecido por alguns idiomas e bibliotecas é uma das respostas para essas preocupações. Nesse modo, o espaço em branco na cadeia de caracteres regexp é removido (portanto, você precisa usar
\s
) e os comentários são possíveis. Aqui está um pequeno exemplo no Python que suporta isso por padrão:Em qualquer idioma que não exija, implementar um tradutor do modo detalhado para o "normal" deve ser uma tarefa simples. Se você está preocupado com a legibilidade de seus regexps, provavelmente justificaria esse investimento de tempo com bastante facilidade.
fonte
Todo idioma que usa expressões regulares permite que você as componha a partir de blocos mais simples para facilitar a leitura e, com algo mais complicado que (ou tão complicado quanto) o seu exemplo, você definitivamente deve tirar proveito dessa opção. O problema específico com Java e muitas outras linguagens é que elas não tratam expressões regulares como cidadãos de "primeira classe", exigindo que elas se infiltrem na linguagem por meio de literais de strings. Isso significa muitas aspas e barras invertidas que não fazem parte da sintaxe regex e dificultam a leitura, e também significa que você não pode ficar muito mais legível do que isso sem definir efetivamente seu próprio mini-idioma e intérprete.
A melhor maneira prototípica de integrar expressões regulares era, obviamente, o Perl, com sua opção de espaço em branco e operadores de cotação de expressões regulares. O Perl 6 estende o conceito de criar expressões regulares de partes para gramáticas recursivas reais, o que é muito melhor usar que realmente não há comparação. O idioma pode ter perdido o barco da pontualidade, mas seu apoio à expressão regular era The Good Stuff (tm).
fonte
Eu gosto de usar o Expresso: http://www.ultrapico.com/Expresso.htm
Este aplicativo gratuito possui os seguintes recursos que considero úteis ao longo do tempo:
Por exemplo, com o regex que você acabou de enviar, seria semelhante a:
Claro, experimentá-lo vale mais que mil palavras descrevendo-o. Observe também que estou de alguma forma relacionado ao editor deste aplicativo.
fonte
Para algumas coisas, pode ser útil usar apenas uma gramática como o BNF. Isso pode ser muito mais fácil de ler do que expressões regulares. Uma ferramenta como o GoldParser Builder pode converter a gramática em um analisador que faz o trabalho pesado para você.
As gramáticas BNF, EBNF etc. podem ser muito mais fáceis de ler e criar do que uma expressão regular complicada. O OURO é uma ferramenta para essas coisas.
O link wiki c2 abaixo tem uma lista de alternativas possíveis que podem ser pesquisadas no Google, com alguma discussão sobre elas incluída. É basicamente um link "consulte também" para completar minha recomendação de mecanismo gramatical:
Alternativas às expressões regulares
fonte
Esta é uma pergunta antiga e eu não vi nenhuma menção às Expressões Verbais, então pensei em adicionar essas informações aqui também para futuros candidatos. As expressões verbais foram projetadas especificamente para tornar o regex humano compreensível, sem a necessidade de aprender o significado do símbolo do regex. Veja o exemplo a seguir. Eu acho que isso faz melhor o que você está pedindo.
Este exemplo é para javascript, você pode encontrar esta biblioteca agora para muitas das linguagens de programação.
fonte
A maneira mais simples seria ainda usar o regex, mas construir sua expressão compondo expressões mais simples com nomes descritivos, por exemplo, http://www.martinfowler.com/bliki/ComposedRegex.html (e sim, isso é da string concat)
no entanto, como alternativa, você também pode usar uma biblioteca combinadora de analisadores, por exemplo, http://jparsec.codehaus.org/, que fornecerá um analisador decente recursivo completo. novamente o poder real aqui vem da composição (desta vez composição funcional).
fonte
Eu pensei que valeria a pena mencionar as expressões grok do logstash . O Grok se baseia na idéia de compor expressões longas de análise a partir das mais curtas. Ele permite testes convenientes desses componentes e vem pré-empacotado com mais de 100 padrões comumente usados . Além desses padrões, ele permite o uso de todas as expressões regulares de sintaxe.
O padrão acima expresso em grok é (eu testei no aplicativo depurador, mas poderia ter cometido um erro):
As partes e espaços opcionais fazem com que pareça um pouco mais feia do que o habitual, mas tanto aqui como em outros casos, o uso do grok pode tornar a vida muito mais agradável.
fonte
No F #, você tem o módulo FsVerbalExpressions . Ele permite que você componha Regexes a partir de expressões verbais, mas também possui alguns regexes pré-criados (como URL).
Um dos exemplos dessa sintaxe é o seguinte:
Se você não estiver familiarizado com a sintaxe F #, groupName é a sequência "GroupNumber".
Em seguida, eles criam uma expressão verbal (VerbEx) que eles constroem como "COD (? <GroupNumber> [0-9] {3}) END". Em seguida, eles testam na cadeia "COD123END", onde obtêm o grupo de captura nomeado "GroupNumber". Isso resulta em 123.
Sinceramente, acho o regex normal muito mais fácil de compreender.
fonte
Primeiro, entenda que o código que simplesmente funciona é um código incorreto. Um bom código também precisa relatar com precisão os erros encontrados.
Por exemplo, se você estiver escrevendo uma função para transferir dinheiro da conta de um usuário para a conta de outro usuário; você não retornaria apenas um booleano "funcionou ou falhou" porque isso não dá ao chamador nenhuma idéia do que deu errado e não permite que o chamador informe o usuário adequadamente. Em vez disso, você pode ter um conjunto de códigos de erro (ou um conjunto de exceções): não foi possível encontrar a conta de destino, fundos insuficientes na conta de origem, permissão negada, não pode se conectar ao banco de dados, muita carga (tente mais tarde), etc. .
Agora pense no seu exemplo "analise uma sequência de números no formato 1: 2: 3,4". Tudo o que a regex faz é relatar uma "aprovação / reprovação" que não permite que o feedback adequado seja apresentado ao usuário (se esse feedback é uma mensagem de erro em um log ou uma GUI interativa em que os erros são mostrados em vermelho como o tipos de usuário ou qualquer outra coisa). Que tipos de erros ele não consegue descrever adequadamente? Caractere incorreto no primeiro número, primeiro número muito grande, dois pontos ausentes após o primeiro número, etc.
Para converter "código incorreto que simplesmente funciona" em "código válido que fornece erros descritivos adequados", é necessário dividir a regex em muitas regexes menores (normalmente, regexes tão pequenas que é mais fácil fazê-lo sem regexes em primeiro lugar )
Tornar o código legível / sustentável é apenas uma conseqüência acidental de tornar o código bom.
fonte
:
? Imagine um compilador que tenha apenas uma mensagem de erro ("ERRO") que seja estúpida demais para dizer ao usuário qual é o problema. Agora imagine milhares de sites que são igualmente estúpidos e exibem (por exemplo) "Endereço de e-mail incorreto" e nada mais.