Por que minha expressão regular funciona em X, mas não em Y?


77

Eu escrevi uma expressão regular que funciona bem em um determinado programa (grep, sed, awk, perl, python, ruby, ksh, bash, zsh, find, emacs, vi, vim, gedit, ...). Mas quando eu o uso em um programa diferente (ou em uma variante unix diferente), ele para de corresponder. Por quê?

Respostas:


103

Infelizmente, por razões históricas, ferramentas diferentes têm sintaxe de expressão regular um pouco diferente e, algumas vezes, algumas implementações têm extensões que não são suportadas por outras ferramentas. Embora exista um terreno comum, parece que todo escritor de ferramentas fez algumas escolhas diferentes.

A conseqüência é que, se você possui uma expressão regular que funciona em uma ferramenta, pode ser necessário modificá-la para funcionar em outra ferramenta. As principais diferenças entre ferramentas comuns são:

  • se os operadores +?|(){}exigem uma barra invertida;
  • quais extensões são suportadas além do básico .[]*^$e geralmente+?|()

Nesta resposta, listo os principais padrões . Verifique a documentação das ferramentas que você está usando para obter detalhes.

A comparação da Wikipedia de mecanismos de expressão regular possui uma tabela que lista os recursos suportados por implementações comuns.

Expressões regulares básicas (BRE)

Expressões regulares básicas são codificadas pelo padrão POSIX . É a sintaxe usada por grep, sede vi. Essa sintaxe fornece os seguintes recursos:

  • ^e $corresponda apenas no início e no final de uma linha.
  • . corresponde a qualquer caractere (ou qualquer caractere, exceto uma nova linha).
  • […]corresponde a qualquer caractere listado entre colchetes (conjunto de caracteres). Se o primeiro caractere após o colchete de abertura for a ^, os caracteres que não estão listados serão correspondidos. Para incluir a ], coloque-o imediatamente após a abertura [(ou depois [^se for um conjunto negativo). Se -estiver entre dois caracteres, indica um intervalo; para incluir um literal -, coloque-o onde não possa ser analisado como um intervalo.
  • Barra invertida antes de qualquer uma das ^$.*\[aspas do próximo caractere.
  • * corresponde ao caractere ou subexpressão anterior 0, 1 ou mais vezes.
  • \(…\)é um grupo sintático, para uso com o *operador ou com referências e \DIGITsubstituições.
  • Backreferences \1, \2... coincidir com o texto exato acompanhado pelo grupo correspondente, por exemplo, \(fo*\)\(ba*\)\1partidas foobaafoo, mas não foobaafo. Não existe uma maneira padrão de se referir ao décimo grupo e além (o significado padrão de \10é o primeiro grupo seguido por a 0).

Os seguintes recursos também são padrão, mas faltam em algumas implementações restritas:

  • \{m,n\}corresponde ao caractere ou subexpressão anterior entre m e n vezes; n ou m pode ser omitido e significa exatamente m .\{m\}
  • Entre colchetes, classes de caracteres podem ser usadas, por exemplo, [[:alpha:]]corresponde a qualquer letra. Implementações modernas de expressões de colchetes ) também incluem recolha de elementos como [.ll.]e classes de equivalência como [=a=].

A seguir, são extensões comuns (especialmente nas ferramentas GNU), mas elas não são encontradas em todas as implementações. Verifique o manual da ferramenta que você está usando.

  • \|para alternância: foo\|barcombina fooou bar.
  • \?(abreviação de \{0,1\}) e \+(abreviação de \{1,\}) correspondem ao caractere ou subexpressão anterior no máximo 1 vez ou no mínimo 1 vez, respectivamente.
  • \ncorresponde a uma nova linha, a \tuma guia etc.
  • \wcorresponde a qualquer constituinte de palavras (abreviação de [_[:alnum:]]mas com variação quando se trata de localização) e \Wcorresponde a qualquer caractere que não seja um constituinte de palavras.
  • \<e \>corresponda à string vazia apenas no início ou no final de uma palavra, respectivamente; \bcorresponde a e \Bcorresponde a onde \bnão.

Observe que as ferramentas sem o \|operador não têm todo o poder das expressões regulares. As referências anteriores permitem algumas coisas extras que não podem ser feitas com expressões regulares no sentido matemático.

Expressões regulares estendidas (ERE)

Expressões regulares estendidas são codificadas pelo padrão POSIX . Sua principal vantagem sobre o BRE é a regularidade: todos os operadores padrão são caracteres de pontuação simples, uma barra invertida antes que um caractere de pontuação sempre o cite. É a sintaxe utilizada por awk, grep -Eou egrep, GNU sed -r, e da festa de=~ operador. Essa sintaxe fornece os seguintes recursos:

  • ^e $corresponda apenas no início e no final de uma linha.
  • . corresponde a qualquer caractere (ou qualquer caractere, exceto uma nova linha).
  • […]corresponde a qualquer caractere listado entre colchetes (conjunto de caracteres). A complementação com uma inicial ^e intervalos funciona como no BRE (veja acima). Classes de caracteres podem ser usadas, mas faltam em algumas implementações. Implementações modernas também suportam classes de equivalência e elementos de intercalação. Uma barra invertida entre colchetes cita o próximo caractere em algumas implementações, mas não em todas; use \\para significar uma barra invertida para portabilidade.
  • (…)é um grupo sintático, para uso *ou \DIGITsubstituição.
  • |para alternância: foo|barcombina fooou bar.
  • *, +E ?coincide com o carácter anterior ou sub-expressão de um número de vezes: 0 ou mais de *, um ou mais de +, 0 ou 1 para ?.
  • A barra invertida cita o próximo caractere se não for alfanumérico.
  • {m,n}corresponde ao caractere ou subexpressão anterior entre m e n vezes (ausente em algumas implementações); n ou m pode ser omitido e significa exatamente m .{m}
  • Algumas extensões comuns como no BRE: referências anteriores (notavelmente ausentes no awk, exceto na implementação do busybox onde você pode usar ); caracteres especiais , etc .; limites de palavras e , constituintes de palavras e …\DIGIT$0 ~ "(...)\\1"\n\t\b\B\b\B

PCRE (expressões regulares compatíveis com Perl)

PCRE são extensões do ERE, originalmente introduzidas pelo Perl e adotadas pelo GNU grep -Pe muitas ferramentas e linguagens de programação modernas , geralmente através da biblioteca PCRE . Veja a documentação do Perl para obter uma boa formatação com exemplos. Nem todos os recursos da versão mais recente do Perl são suportados pelo PCRE (por exemplo, a execução do código Perl é suportada apenas no Perl). Consulte o manual do PCRE para obter um resumo dos recursos suportados. As principais adições ao ERE são:

  • (?:…)é um grupo que não captura: like (…), mas não conta para referências anteriores.
  • (?=FOO)BAR(lookahead) corresponde BAR, mas somente se houver também uma correspondência para FOOcomeçar na mesma posição. Isso é mais útil para ancorar uma correspondência sem incluir o seguinte texto na correspondência: foo(?=bar)corresponde, foomas somente se for seguido por bar.
  • (?!FOO)BAR(negativo) BAR, mas também não existe FOOna mesma posição. Por exemplo, (?!foo)[a-z]+corresponde a qualquer palavra em minúscula que não começa com foo; [a-z]+(?![0-9)corresponde a qualquer palavra em minúscula que não é seguida por um dígito (portanto foo123, corresponde, fomas não foo).
  • (?<=FOO)BAR(lookbehind) corresponde BAR, mas somente se for imediatamente precedido por uma correspondência para FOO. FOOdeve ter um comprimento conhecido (você não pode usar operadores de repetição como *). Isso é mais útil para ancorar uma correspondência sem incluir o texto anterior na correspondência: (?<=^| )foocorresponde, foomas apenas se for precedido por um espaço ou pelo início da sequência.
  • (?<!FOO)BAR(lookbehind negativo) corresponde BAR, mas somente se não for imediatamente precedido por uma correspondência para FOO. FOOdeve ter um comprimento conhecido (você não pode usar operadores de repetição como *). Isso é mais útil para ancorar uma correspondência sem incluir o texto anterior na correspondência: (?<![a-z])foocorresponde, foomas apenas se não for precedida por uma letra minúscula.

Emacs

A sintaxe do Emacs é intermediária entre BRE e ERE. Além do Emacs, é a sintaxe padrão para a localização -regexno GNU. O Emacs oferece os seguintes operadores:

  • ^, $, ., […], *, +, ?Como em ERE
  • \(…\), \|, \{…\}, Como em BRE\DIGIT
  • mais seqüências de letras invertidas ; \<e \>para limites de palavras; e mais nas versões recentes do Emacs, que geralmente não são suportadas em outros mecanismos com uma sintaxe semelhante ao Emacs.

Globs de concha

Os globs do shell (curingas) executam a correspondência de padrões com uma sintaxe completamente diferente das expressões regulares e menos poderosa. Além dos shells, esses curingas estão disponíveis com outras ferramentas, como os find -namefiltros e rsync. Os padrões POSIX incluem os seguintes recursos:

  • ? corresponde a qualquer caractere único.
  • […]é um conjunto de caracteres como nas sintaxes comuns de expressão regular. Alguns shells não suportam classes de caracteres. Algumas conchas exigem, em !vez de ^negar, o conjunto.
  • *corresponde a qualquer sequência de caracteres (geralmente, exceto /ao corresponder aos caminhos do arquivo; se /for excluído *, **às vezes inclui /, mas consulte a documentação da ferramenta).
  • A barra invertida cita o próximo caractere.

O Ksh oferece recursos adicionais que dão ao seu padrão o poder total das expressões regulares. Esses recursos também estão disponíveis no bash após a execução shopt -s extglob. O Zsh tem uma sintaxe diferente, mas também pode suportar a sintaxe do ksh depois setopt ksh_glob.


Outras REs ricas que você pode querer mencionar são vimlibast da AT&T (como em ksh93).
Stéphane Chazelas

@ StéphaneChazelas Além do vim, que programa usa regexps do vim? Além do ksh, que programa usa libast?
Gilles

todo o conjunto de ferramentas AT & T usa os REs AT & T ( grep, tw, expr...). Exceto por ksh, esse conjunto de ferramentas raramente é encontrado fora da AT&T.
Stéphane Chazelas

De acordo com meu entendimento (e da Wikipedia), seu termo "classe de caracteres" na verdade se refere a "classe de caracteres POSIX" ... no entanto, regex(7)concorda com você e chama [these]"expressões entre colchetes" e (dentro de "expressões entre colchetes") [:these:]"classes de caracteres". Não sei bem como lidar com isso.
Adam Katz

Como você os chama, eles suportam intervalos. Definitivamente, vale a pena notar que -especifica um intervalo e deve ser escapado, primeiro (após o opcional ^) ou por último, se for para ser tomado literalmente. (Eu vi muitos erros decorrentes por exemplo [A-z]-Nota a mudança no caso-, que corresponde caracteres de códigos de 65 a 122 e, acidentalmente, inclui cada um: [\]^_`. Eu também vi o válida ainda confuso [!-~]para coincidir com todos os caracteres imprimíveis em ANSI , que eu prefiro ver como [\x21-\x7e], o que é, pelo menos direto na sua acção, embora confuso em uma dimensão diferente).
Adam Katz
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.