Pergunta

Acabei de ver um enorme regex para Java que me fez pensar um pouco sobre a manutenção de expressões regulares em geral. Acredito que a maioria das pessoas - exceto alguns traficantes de perl badass - concordaria que expressões regulares dificilmente são sustentáveis.

Eu estava pensando em como essa situação poderia ser consertada. Até agora, a ideia mais promissora que tenho é usar um interface fluente. Para dar um exemplo, em vez de:

Pattern pattern = Pattern.compile("a*|b{2,5}");

Alguém poderia escrever algo assim

import static util.PatternBuilder.*

Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();

Pattern alternative = 
  or(
    string("a").anyTimes(),
    string("b").times(2,5)
  )
  .compile();

Neste exemplo muito curto, a maneira comum de criar expressão regular ainda é bastante legível para qualquer desenvolvedor medíocre talentoso. No entanto, pense nas expressões assustadoras que preenchem duas ou mais linhas com 80 caracteres cada. Claro, a interface fluente (detalhada) exigiria várias linhas em vez de apenas duas, mas tenho certeza de que seria muito mais legível (daí manutenção).

Agora minhas perguntas:

  1. Você conhece alguma abordagem semelhante às expressões regulares?

  2. Você concorda que essa abordagem pode ser melhor do que usar seqüências simples?

  3. Como você criaria a API?

  4. Você usaria um utilitário tão legal em seus projetos?

  5. Você acha que isso seria divertido implementar? ;)

EDITAR:Imagine que poderia haver métodos que estão em um nível mais alto do que construções simples que todos nós não são de regex, por exemplo

// matches aaaab@example.com - think of it as reusable expressions
Pattern p = string{"a").anyTimes().string("b@").domain().compile();

EDIT - Resumo curto dos comentários:

É interessante ler que a maioria das pessoas pensa que as expressões regulares estão aqui para ficar - embora sejam necessárias ferramentas para lê -las e caras inteligentes para pensar em maneiras de torná -las sustentáveis. Embora eu não tenha certeza de que uma interface fluente é o melhor caminho a seguir, tenho certeza de que alguns engenheiros inteligentes - nós? ;) - Deve gastar algum tempo para fazer expressões regulares uma coisa do passado - basta que elas estejam conosco há 50 anos, sabem, você não acha?

Recompensa aberta

A recompensa será concedida à melhor idéia (nenhum código necessário) para uma nova abordagem às expressões regulares.

EDIT - Um bom exemplo:

Aqui está o tipo de padrão que estou falando - Parabéns extras para o primeiro que é capaz de traduzi -lo - regexbuddies permitidos (é de um projeto Apache btw) Partelos extras concedidos a Chii e mez: é um padrão de validação de endereço de e -mail compatível com RFC - embora seu RFC822 (Vejo ex-parrot.com), não 5322 - Mas não tenho certeza se há uma diferença - se sim, eu concederei a próxima elogios extras por um patch para ajustar 5322;)

private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
    + "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
    + "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
    + "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
    + "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
    + "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
    + "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
    + "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
    + "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
    + "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
    + " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
    + "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
    + ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
    + " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
    + ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
    + "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
    + "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
    + "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
    + "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
    + "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
    + "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
    + "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
    + ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
    + ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
    + ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
    + "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
    + "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
    + "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
    + "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
    + ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
    + "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
    + "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
    + "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
    + ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
    + "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
    + ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
    + "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
    + "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
    + "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
    + "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
    + "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
    + ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
    + ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
    + ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
    + "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
    + "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
    + "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
    + "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
    + "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
    + "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
    + "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
    + ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
    + "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
    + "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
    + "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
    + "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
    + "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
    + "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
    + "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
    + " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
    + ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
    + "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
    + "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
    + "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
    + "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
    + ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
    + "?:\\r\\n)?[ \\t])*))*)?;\\s*)";
Foi útil?

Solução

Como você criaria a API?

Eu emprestaria uma página da API de critérios de hibernação. Ao invés de usar:

string("a").anyTimes().or().string("b").times(2,5).compile()

Use um padrão como:

Pattern.or(Pattern.anyTimes("a"), Pattern.times("b", 2, 5)).compile()

Essa notação é um pouco mais concisa, e sinto que é mais fácil entender a hierarquia/estrutura do padrão. Cada método pode aceitar uma string ou um fragmento de padrão como o primeiro argumento.

Você conhece alguma abordagem semelhante às expressões regulares?

Não é de inchaço, não.

Você concorda que essa abordagem pode ser melhor do que usar seqüências simples?

Sim, absolutamente ... se você está usando o REGEX para qualquer coisa remotamente complicada. Para casos muito curtos e simples, uma corda é mais conveniente.

Você usaria um utilitário tão legal em seus projetos?

Possivelmente, como se tornou comprovado/estável ... rolando -o em um projeto de utilidade maior, como o Apache Commons, pode ser uma vantagem.

Você acha que isso seria divertido implementar? ;)

+1

Outras dicas

Martin Fowler sugere outra estratégia. Ou seja, pegando partes significativas do regex e substitui -as por variáveis. Ele usa o seguinte exemplo:

 "^score\s+(\d+)\s+for\s+(\d+)\s+nights?\s+at\s+(.*)" 

torna-se

   String scoreKeyword = "^score\s+";
   String numberOfPoints = "(\d+)";
   String forKeyword = "\s+for\s+";
   String numberOfNights = "(\d+)";
   String nightsAtKeyword = "\s+nights?\s+at\s+";
   String hotelName = "(.*)";

   String pattern = scoreKeyword + numberOfPoints +
      forKeyword + numberOfNights + nightsAtKeyword + hotelName;

O que é muito mais legível e sustentável.

O objetivo do exemplo anterior era analisar números de pontos, noturnos e nome de hotel de uma lista de strings como:

score 400 for 2 nights at Minas Tirith Airport

Pode ser um pouco mais fácil para alguém sem nenhuma experiência regex, mas depois que alguém aprende seu sistema, ele ainda não poderá ler um regex normal em outro lugar.

Além disso, acho que sua versão é mais difícil de ler para um especialista regex.

Eu recomendaria anotar o regex como este:

Pattern pattern = Pattern.compile(
  "a*     # Find 0 or more a        \n" +
  "|      # ... or ...              \n" +
  "b{2,5} # Find between 2 and 5 b  \n",
Pattern.COMMENTS);

É fácil ler para qualquer nível de experiência e, para os inexperientes, ensina Regex ao mesmo tempo. Além disso, os comentários podem ser adaptados à situação para explicar as regras de negócios por trás do Regex, em vez de apenas a estrutura.

Além disso, uma ferramenta como Regexbuddy pode levar seu regex e traduzi -lo em:

Match either the regular expression below (attempting the next alternative only if this one fails) «a*»
   Match the character "a" literally «a*»
      Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Or match regular expression number 2 below (the entire match attempt fails if this one fails to match) «b{2,5}»
   Match the character "b" literally «b{2,5}»
      Between 2 and 5 times, as many times as possible, giving back as needed (greedy) «{2,5}»

Este é um conceito intrigante, mas como é apresentado, existem algumas falhas.

Mas as primeiras respostas às principais perguntas feitas:

Agora minhas perguntas:

1. Você conhece alguma abordagem semelhante às expressões regulares?

Nenhum que ainda não foi mencionado. E aqueles que eu descobri lendo a pergunta e as respostas.

2. Você concorda que essa abordagem pode ser melhor do que usar strings simples?

Se funcionar como anunciado, definitivamente tornaria as coisas muito mais fáceis de depurar.

3. Como você projetaria a API?

Veja minhas anotações na próxima seção. Pego seus exemplos e a biblioteca .NET vinculada como ponto de partida.

4. Você usaria um utilitário tão elegante em seus projetos?

Indeciso. Não tenho nenhum problema em trabalhar com a versão atual das expressões criptais regularmente. E eu precisaria de uma ferramenta para converter expressões regulares existentes na versão de linguagem fluente.

5. Você acha que isso seria divertido implementar? ;)

Eu gostaria de elaborar o nível mais alto de como escrever o código real. Que explica o muro de texto que é essa resposta.


Aqui estão alguns problemas que notei e a maneira como lidaria com isso.

Estrutura pouco clara.

Seu exemplo parece criar um regex concatenando em strings. Isso não é muito robusto. Acredito que esses métodos devem ser adicionados aos objetos String e Patern/REGEX, porque tornará a implementação e o limpador de código. Além disso, é semelhante à maneira como as expressões regulares são definidas classicamente.

Só porque não consigo vê -lo funcionando de outra maneira, o restante das minhas anotações para o esquema proposto assumirá que todos os métodos agem e retornam objetos de padrão.

Editar: Parece que tenho usado as seguintes convenções por toda parte. Então, eu os esclareci e os movo aqui.

  • Métodos de instância: aumento do padrão. Por exemplo: captura, repetição, olhe as afirmações.

  • Operadores: Ordem de Operações. alternância, concatenação

  • Constantes: classes de personagens, limites (no local de w, $, b, etc)

Como a captura/agrupamento será tratada?

Capturar é uma grande parte das expressões regulares.

Eu vejo cada objeto de padrão sendo armazenado internamente como um cluster. (?: padrão) em termos perl. Permitindo que os tokens de padrões sejam facilmente misturados e misturados sem interferir em outras peças.

Espero que a captura seja feita como um método de instância em um padrão. Tomando uma variável para armazenar as strings correspondentes [s] em.

pattern.capture(variable) armazenaria o padrão em variável. No caso de a captura fazer parte de uma expressão a ser correspondente várias vezes, a variável deve conter uma matriz de cordas de todas as correspondências para o padrão.

Línguas fluentes podem ser muito ambíguas.

As línguas fluentes não são adequadas para a natureza recursiva das expressões regulares. Portanto, é preciso considerar a ordem das operações. Apenas métodos de encadeamento juntos não permitem expressões regulares muito complexas. Exatamente a situação em que essa ferramenta seria útil.

Faz

Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();

produzir /a*|b{2,5}/ ou /(a*|b){2,5}/ ?

Como esse esquema lidaria com a alternância aninhada? Por exemplo: /a*|b(c|d)|e/

Eu vejo três maneiras de lidar com a alternância em expressões regulares

  1. Como operador: pattern1 or pattern2 => pattern # /pattern1|pattern2/
  2. Como um método de classe: Pattern.or( pattern1, pattern2[, pattern3]*) => pattern # /pattern1|patern2|patern3|...|/
  3. Como um método de instância: pattern1.or(pattern2) => pattern # /pattern1|patern2/

Eu lidaria com a concatenação da mesma maneira.

  1. Como operador: pattern1 + pattern2 => pattern # /pattern1pattern2/
  2. Como um método de classe: Pattern.concatenate( pattern1, pattern2[, pattern3]*) => pattern # /pattern1patern2patern3.../
  3. Como um método de instância: pattern1.then(pattern2) => pattern # /pattern1patern2/

Como se estender para padrões comumente usados

O esquema proposto usado .domain() o que parece ser um regex comum. Tratar padrões definidos pelo usuário como métodos não facilita a adição de novos padrões. Em um idioma como os usuários Java da biblioteca, teriam que substituir a classe para adicionar métodos para padrões comumente usados.

Isso é abordado pela minha sugestão para tratar todas as peças como um objeto. Um objeto de padrão pode ser criado para cada regex comumente usado como corresponder a um domínio, por exemplo. Dados meus pensamentos anteriores sobre a captura, não seria muito difícil garantir que a captura de obras para várias cópias do mesmo padrão comum que contém uma seção capturada.

Também deve haver constantes para padrões que correspondam a várias classes de personagens.

Largura zero Olhe em volta das afirmações

Expandindo meus pensamentos de que todas as peças devem ser implicitamente agrupadas. Olhe em volta as afirmações não devem ser muito difíceis de fazer com um método de instância.

pattern.zeroWidthLookBehind() produziria (?<patten).


Coisas que ainda precisam ser consideradas.

  • BackReferências: espero que não seja muito difícil com o nomeado captura discutida anteriormente
  • Como realmente implementá -lo. Eu não pensei muito nos internos. É onde a verdadeira magia vai acontecer.
  • Tradução: realmente deve haver uma ferramenta traduzida de e para as regexs clássicas (digamos dialeto perl) e o novo esquema. Traduzir do novo esquema pode fazer parte do pacote

Juntando tudo, minha versão proposta de um padrão que corresponde a um endereço de e -mail:

Pattern domain_label = LETTER_CHARACTER + (LETTER_CHARACTER or "-" or DIGIT_CHARACTER).anyTimes()
Pattern domain = domain_label + ("." + domain_label).anyTimes()
Pattern pattern = (LETTER_CHARACTER + ALPHANUMERIC_CHARACTER + "@" + domain).compile

Em retrospectiva, meu esquema empresta muito a abordagem de Martin Fowler em uso. Embora eu não tenha pretendido que as coisas seguissem por esse caminho, isso definitivamente torna o uso desse sistema mais sustentável. Ele também aborda um ou dois problemas com a abordagem de Fowler (captura de ordem).

Minha própria humilde tentativa pode ser encontrada em Github. Embora eu não ache que vale a pena usar para expressões simples, isso fornece algumas vantagens além da melhoria da legibilidade:

  • Ele cuida da correspondência de suporte
  • Ele lida

Alguns exemplos simples:

 // Matches a single digit
    RegExBuilder.build(anyDigit()); // "[0-9]"

 // Matches exactly 2 digits
    RegExBuilder.build(exactly(2).of(anyDigit())); // "[0-9]{2}"

 // Matches between 2 and 4 letters
    RegExBuilder.build(between(2,4).of(anyLetter())); // "[a-zA-Z]{2,4}"

E um mais complicado (que mais ou menos valida endereços de email):

final Token ALPHA_NUM = anyOneOf(range('A','Z'), range('a','z'), range('0','9'));
final Token ALPHA_NUM_HYPEN_UNDERSCORE = anyOneOf(characters('_','-'), range('A','Z'), range('a','z'), range('0','9'));

String regexText = RegExBuilder.build(
 // Before the '@' symbol we can have letters, numbers, underscores and hyphens anywhere
    oneOrMore().of(
        ALPHA_NUM_HYPEN_UNDERSCORE
    ),
    zeroOrMore().of(
        text("."), // Periods are also allowed in the name, but not as the initial character
        oneOrMore().of(
            ALPHA_NUM_HYPEN_UNDERSCORE
        )
    ),
    text("@"),
 // Everything else is the domain name - only letters, numbers and periods here
    oneOrMore().of( 
        ALPHA_NUM
    ),
    zeroOrMore().of(
        text("."), // Periods must not be the first character in the domain
        oneOrMore().of(
            ALPHA_NUM
        )
    ),
    text("."), // At least one period is required
    atLeast(2).of( // Period must be followed by at least 2 letters (this is the TLD)
        anyLetter()
    )
);

Há um Biblioteca Fluente Regexps para .net.

Resposta curta: Eu já vi que se aproximou de um ângulo de linha e compilação, o que eu acho que é algo a considerar para isso.

Resposta longa: A empresa para a qual trabalho produz mecanismos de expressão regular baseados em hardware para aplicativos de filtragem de conteúdo corporativo. Pense em executar aplicativos antivírus ou firewall a velocidades de 20 GB/s nos roteadores de rede, em vez de ocupar valiosos ciclos de servidor ou processador. A maioria dos aplicativos antivírus, anti-spam ou firewall são um monte de expressões regex no núcleo.

De qualquer forma, a maneira como os Regex são escritos tem um enorme impacto no desempenho da digitalização. Você pode escrever regexs de várias maneiras diferentes para fazer a mesma coisa, e alguns terão desempenho drasticamente mais rápido. Escrevemos compiladores e linheiros para nossos clientes para ajudá -los a manter e ajustar suas expressões.

De volta à pergunta do OP, em vez de definir uma sintaxe totalmente nova, eu escrevia um linhador (desculpe, o nosso é proprietário) cortaria e colaria os regex que quebrarão que quebrarão o legado de regex e a saída de "inglês fluente" para que alguém entenda melhor. Eu também adicionaria verificações e sugestões de desempenho relativas para modificações comuns.

A resposta curta, para mim, é que, uma vez que você chega a expressões regulares (ou outra correspondência de padrões que faz a mesma coisa) que são longos o suficiente para causar um problema ... você provavelmente deve estar considerando se eles são a ferramenta certa Para o trabalho em primeiro lugar.

Honestamente, qualquer interface fluente parece que seria mais difícil de ler do que uma expressão regular padrão. Para expressões realmente curtas, a versão fluente é detalhada, mas não muito longa; É legível. Mas o mesmo acontece com a expressão regular para algo por tanto tempo.

Para uma expressão regular de tamanho médio, uma interface fluente se torna pesada; o suficiente para que seja difícil, se não impossível, ler.

Para uma longa expressão regular (ou seja, o endereço de email um), onde a expressão regular é realmente difícil (se não impossível) de ler, a versão fluente se tornou impossível de ler 10 páginas atrás.

Você conhece alguma abordagem semelhante às expressões regulares?

Não, exceto a resposta anterior

Você concorda que essa abordagem pode ser melhor do que usar seqüências simples?

Mais ou menos - acho que, em vez de caracteres únicos, para representar construções, poderíamos usar uma marcação mais descritiva, mas duvido que isso tornasse a lógica complexa qualquer coisa mais clara.

Como você criaria a API?

Traduza cada construto em um nome de método e permita chamadas de função aninhadas para que seja muito fácil pegar uma string e substituir nomes de métodos para ele.

Eu acho que a maior parte do valor estará na definição de uma biblioteca robusta de funções de utilidade, como corresponder "emails", "números de telefone", "linhas que não contêm x", etc. que podem ser personalizadas.

Você usaria um utilitário tão legal em seus projetos?

Talvez - mas provavelmente apenas para os mais longos, onde seria mais fácil depurar chamadas de funções do que depurar a edição de string ou onde há uma boa função de utilidade pronta para uso.

Você acha que isso seria divertido implementar? ;)

É claro!

Em resposta à última parte da pergunta (para parabéns)

private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
    + "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
    + "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
    + "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
    + "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
    + "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
    + "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
    + "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
    + "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
    + "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
    + " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
    + "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
    + ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
    + " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
    + ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
    + "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
    + "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
    + "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
    + "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
    + "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
    + "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
    + "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
    + ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
    + ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
    + ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
    + "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
    + "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
    + "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
    + "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
    + ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
    + "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
    + "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
    + "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
    + ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
    + "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
    + ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
    + "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
    + "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
    + "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
    + "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
    + "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
    + ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
    + ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
    + ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
    + "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
    + "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
    + "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
    + "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
    + "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
    + "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
    + "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
    + ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
    + "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
    + "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
    + "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
    + "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
    + "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
    + "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
    + "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
    + " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
    + ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
    + "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
    + "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
    + "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
    + "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
    + ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
    + "?:\\r\\n)?[ \\t])*))*)?;\\s*)";

corresponde aos endereços de email compatíveis com RFC: D

Uma expressão regular é uma descrição de uma máquina de estado finita. A representação textual clássica não é necessariamente ruim. É compacto, é relativamente inequívoco e é bem adotado.

Pode ser que uma melhor representação seja um diagrama de transição de estado, mas isso provavelmente seria difícil de usar no código -fonte.

Uma possibilidade seria construí -lo a partir de uma variedade de objetos de contêiner e combinador.

Algo ao longo da linha do seguinte (transformando isso de pseudo-código para a linguagem preferido é deixado como um exercício para os ansiosos):

domainlabel = oneormore(characterclass("a-zA-Z0-9-"))
separator = literal(".")
domain = sequence(oneormore(sequence(domainlabel, separator)), domainlabel)
localpart = oneormore(characterclassnot("@"))
emailaddress = sequence(localpart, literal("@"), domain)

Observe que o exposto acima classificará incorretamente o arbrito muitos endereços de email como válidos que não estão em conformidade com a gramática que eles precisam seguir, pois essa gramática requer mais do que um FSM simples para análise completa. Eu não acredito que ele classificaria mal um endereço válido como inválido, no entanto.

Deve corresponder a [^@]+@([a-za-z0-9-]+.)+. ([A-ZA-Z0-9-]+)

4. Você usaria um utilitário tão elegante em seus projetos?

Eu provavelmente não. Eu acho que este é um caso de usar a ferramenta certa para o trabalho. Existem ótimas respostas aqui, como: 1579202 De Jeremy Stein. Recentemente, obtive expressões regulares em um projeto recente e as achei muito úteis da mesma forma que são e, quando comentadas adequadamente, elas são compreensíveis se você conhece a sintaxe.

Eu acho que a parte "Conhecer a sintaxe" é o que afasta as pessoas para expressões regulares, para aqueles que não entendem que parecem misteriosos e enigmáticos, mas são uma ferramenta poderosa e na ciência da computação aplicada (por exemplo, software de redação para viver) Sinto que profissionais inteligentes deveriam e devem ser capazes de aprender a usá -los e nós deles adequadamente.

Como se costuma dizer, "com grande poder vem uma grande responsabilidade". Vi pessoas usarem expressões regulares em todos os lugares para tudo, mas usadas criteriosamente por alguém que dedicou um tempo para aprender a sintaxe minuciosamente, elas são incrivelmente úteis; Para mim, adicionar outra camada derrotaria de certa forma seu propósito ou, no mínimo, retiraria seu poder.

Esta é apenas minha própria opinião e eu posso entender de onde as pessoas vêm de quem desejariam uma estrutura como essa, ou que evitariam expressões regulares, mas tenho dificuldade em ouvir "expressões regulares são ruins" daqueles que não dedicaram tempo para aprendê -los e tomar uma decisão informada.

Para deixar todos felizes (regex Masters e proponentes da interface fluida), verifique se a interface fluida pode gerar um padrão Regex bruto apropriado e também pegar um regex regular usando um método de fábrica e gerar código de fluido para ele.

O que você está procurando pode ser encontrado aqui:. É um buillder de expressão regular que segue o Padrão de design do assistente

Eu tive recentemente essa mesma ideia.

Pensei em implementá -lo sozinho, mas então eu encontrei Verbalexpressões.

Vamos comparar: eu trabalhei com frequência com (n) consultas de hibernato de iCriteria, que podem ser consideradas um Fluente Mapeamento para SQL. Eu estava (e ainda estou) entusiasmado com eles, mas eles tornaram as consultas SQL mais legíveis? Não, mais ao contrário, mas outro benefício aumentou: tornou -se muito mais fácil construir declarações programaticamente, subclasse -as e criar suas próprias abstrações etc.

O que estou mexendo é que usar uma nova interface para um determinado idioma, se bem feito, pode provar que vale a pena, mas não pense muito nisso. Em muitos casos, não se tornará mais fácil ler (classes de personagens de subtração aninhadas, capturas em aparência, se ramos, para nomear alguns conceitos avançados que serão difíceis de combinar fluentemente). Mas, em tantos casos, os benefícios de maior flexibilidade superam a sobrecarga adicional da complexidade da sintaxe.

Para adicionar à sua lista de possíveis abordagens alternativas e retirar isso do contexto de apenas Java, considere a sintaxe do LINQ. Aqui está como poderia ser (um pouco artificial) (from, where e select são palavras -chave no LINQ):

// for ^str(aa|bb){3}
from part in mystring
where part startswith "str"
and part hasgroups "aa" or "bb" as first    /* "aa" or "bb" in group 'first' */
and part repeats first 3                    /* repeat group 'first' 3 times */
select part + "extra"                       /* can contain complete statement block */

Apenas uma ideia difícil, eu sei. O bom do LINQ é que ele é verificado pelo compilador, um tipo de idioma em um idioma. Por padrão, o LINQ também pode ser expresso como sintaxe de encadeamento fluente, o que o torna, se bem projetado, compatível com outros idiomas OO.

Eu digo que vá em frente, tenho certeza de que é divertido de implementar.

Sugiro usar um modelo de consulta (semelhante ao jQuery, Django ORM), onde cada função retorna um objeto de consulta, para que você possa acorrentá -lo.

any("a").some("b").one("@").some(chars).one(".").some(chars) //a*b+@\w+\.\w+

Onde chars é predefinido para se adequar a qualquer personagem.

or pode ser alcançado usando opções:

any("a").choice("x", "z") // a(x|z)

O argumento para cada função pode ser uma string ou outra consulta. Por exemplo, o chars A variável mencionada acima pode ser definida como uma consulta:

//this one is ascii only
chars = raw("a-zA-Z0-9")

E assim, você pode ter uma função "cru" que aceita uma string regex como entrada se parecer complicada usar o sistema de consulta fluente.

Na verdade, essas funções podem apenas retornar o Raw Regex, e o encadeamento é simplesmente concatenando essas cordas regex cruas.

any("a") ---> "a*"
raw("b+") ----> "b+"
one(".") ---> "\."
choice("a", "b") ----> (a|b)

Não tenho certeza de que a substituição do Regexp por uma API fluente traria muito.

Observe que eu não sou um assistente regexp (tenho que reler o documento quase toda vez que preciso criar um regexp).

Uma API fluente tornaria qualquer regexp de complexidade média (digamos ~ 50 caracteres) ainda mais complexo do que o necessário e não é mais fácil de ler no final, embora possa melhorar o criação de um regexp em um IDE, graças à conclusão do código. Mas a manutenção do código geralmente representa um custo mais alto que o desenvolvimento do código.

Na verdade, nem tenho certeza de que seria possível ter uma API inteligente o suficiente para realmente fornecer orientação suficiente ao desenvolvedor ao criar um novo Regexp, não falando de casos ambíguos, como mencionado em uma resposta anterior.

Você mencionou um exemplo regexp para um RFC. Tenho 99% de certeza (ainda há 1% de esperança ;-)) que qualquer API não tornaria esse exemplo mais simples, mas, inversamente, isso só tornaria mais complexo de ler! Esse é um exemplo típico em que você não deseja usar o Regexp de qualquer maneira!

Mesmo em relação à criação do REGEXP, devido ao problema dos padrões ambíguos, é provável que, com uma API fluente, você nunca viria com a expressão certa da primeira vez, mas teria que mudar várias vezes até conseguir o que realmente deseja.

Não se engane, eu amo interfaces fluentes; Eu desenvolvi algumas bibliotecas que as usam e uso várias bibliotecas de terceiros com base nelas (por exemplo, Fest para testes de Java). Mas não acho que eles possam ser o martelo de ouro para qualquer problema.

Se considerarmos Java exclusivamente, acho que o principal problema com regexps é o Escaping necessário de barras de barriga em java string constantes. Esse é um ponto que torna incrivelmente difícil criar e entender o Regexp em Java. Portanto, o primeiro passo para melhorar o java regexp seria, para mim, uma mudança de idioma, a la groovy, onde as constantes de cordas não precisam escapar de barras de barriga.

Até agora, essa seria minha única proposta de melhorar o Regexp em Java.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top