¿Por qué mi expresión regular funciona en X pero no en Y?


77

Escribí una expresión regular que funciona bien en cierto programa (grep, sed, awk, perl, python, ruby, ksh, bash, zsh, find, emacs, vi, vim, gedit, ...). Pero cuando lo uso en un programa diferente (o en una variante diferente de Unix), deja de coincidir. ¿Por qué?

Respuestas:


103

Desafortunadamente, por razones históricas, diferentes herramientas tienen una sintaxis de expresión regular ligeramente diferente y, a veces, algunas implementaciones tienen extensiones que no son compatibles con otras herramientas. Si bien hay un terreno común, parece que cada herramienta que escribió tomó algunas decisiones diferentes.

La consecuencia es que si tiene una expresión regular que funciona en una herramienta, es posible que deba modificarla para que funcione en otra herramienta. Las principales diferencias entre las herramientas comunes son:

  • si los operadores +?|(){}requieren una barra invertida;
  • qué extensiones son compatibles más allá de lo básico .[]*^$y generalmente+?|()

En esta respuesta, enumero los principales estándares . Consulte la documentación de las herramientas que está utilizando para obtener detalles.

La comparación de Wikipedia de los motores de expresión regular tiene una tabla que enumera las características compatibles con implementaciones comunes.

Expresiones regulares básicas (BRE)

Las expresiones regulares básicas están codificadas por el estándar POSIX . Es la sintaxis utilizada por grep, sedy vi. Esta sintaxis proporciona las siguientes características:

  • ^y $coinciden solo al principio y al final de una línea.
  • . coincide con cualquier carácter (o cualquier carácter excepto una nueva línea).
  • […]coincide con cualquier carácter enumerado dentro de los corchetes (conjunto de caracteres). Si el primer carácter después del paréntesis de apertura es a ^, los caracteres que no están en la lista coinciden. Para incluir a ], colóquelo inmediatamente después de la apertura [(o después [^si es un conjunto negativo). Si -está entre dos caracteres, denota un rango; para incluir un literal -, colóquelo donde no pueda analizarse como un rango.
  • Barra invertida antes de cualquiera de las ^$.*\[citas del siguiente personaje.
  • * coincide con el carácter anterior o subexpresión 0, 1 o más veces.
  • \(…\)es un grupo sintáctico, para usar con el *operador o referencias y \DIGITreemplazos.
  • Las referencias inversas \1, \2... coinciden con el texto exacto que coincide con el grupo correspondiente, por ejemplo, \(fo*\)\(ba*\)\1coincide foobaafoopero no foobaafo. No hay una forma estándar de referirse al décimo grupo y más allá (el significado estándar de \10es el primer grupo seguido de a 0).

Las siguientes características también son estándar, pero faltan en algunas implementaciones restringidas:

  • \{m,n\}coincide con el carácter o subexpresión anterior entre m y n veces; n o m pueden ser omitidos, y significa exactamente m .\{m\}
  • Dentro de los corchetes, se pueden usar clases de caracteres , por ejemplo, [[:alpha:]]coincide con cualquier letra. Las implementaciones modernas de expresiones de paréntesis ) también incluyen elementos de clasificación como [.ll.]clases de equivalencia como [=a=].

Las siguientes son extensiones comunes (especialmente en herramientas GNU), pero no se encuentran en todas las implementaciones. Consulte el manual de la herramienta que está utilizando.

  • \|para alternar: foo\|barpartidos fooo bar.
  • \?(short for \{0,1\}) y \+(short for \{1,\}) coinciden con el carácter o subexpresión anterior como máximo 1 vez, o al menos 1 vez respectivamente.
  • \ncoincide con una nueva línea, \tcoincide con una pestaña, etc.
  • \wcoincide con cualquier componente de la palabra (abreviatura [_[:alnum:]]pero con variación cuando se trata de localización) y \Wcoincide con cualquier carácter que no sea un componente de la palabra.
  • \<y hacer \>coincidir la cadena vacía solo al principio o al final de una palabra respectivamente; \bcoincide con cualquiera, y \Bcoincide con donde \bno.

Tenga en cuenta que las herramientas sin el \|operador no tienen todo el poder de las expresiones regulares. Las referencias posteriores permiten algunas cosas adicionales que no se pueden hacer con expresiones regulares en el sentido matemático.

Expresiones regulares extendidas (ERE)

Las expresiones regulares extendidas están codificadas por el estándar POSIX . Su principal ventaja sobre BRE es la regularidad: todos los operadores estándar son caracteres de puntuación, una barra diagonal inversa antes de que un carácter de puntuación siempre lo cite. Es la sintaxis utilizada por awk, grep -Eo egrep, GNU sed -r, y el=~ operador de bash . Esta sintaxis proporciona las siguientes características:

  • ^y $coinciden solo al principio y al final de una línea.
  • . coincide con cualquier carácter (o cualquier carácter excepto una nueva línea).
  • […]coincide con cualquier carácter enumerado dentro de los corchetes (conjunto de caracteres). La complementación con una inicial ^y rangos funcionan como en BRE (ver arriba). Se pueden usar clases de caracteres, pero faltan algunas implementaciones. Las implementaciones modernas también admiten clases de equivalencia y elementos de clasificación. Una barra diagonal inversa entre paréntesis cita el siguiente carácter en algunas implementaciones, pero no en todas; se utiliza \\para significar una barra invertida para la portabilidad.
  • (…)es un grupo sintáctico, para uso con *o \DIGITreemplazos.
  • |para alternar: foo|barpartidos fooo bar.
  • *, +y ?coincide con el carácter o subexpresión anterior varias veces: 0 o más para *, 1 o más para +, 0 o 1 para ?.
  • La barra diagonal inversa cita el siguiente carácter si no es alfanumérico.
  • {m,n}coincide con el carácter anterior o subexpresión entre m y n veces (que faltan en algunas implementaciones); n o m pueden ser omitidos, y significa exactamente m .{m}
  • Algunas extensiones comunes como en BRE: referencias posteriores (notablemente ausentes en awk, excepto en la implementación de busybox donde puede usar ); caracteres especiales , etc .; límites de palabras y constituyentes de palabras y ...\DIGIT$0 ~ "(...)\\1"\n\t\b\B\b\B

PCRE (expresiones regulares compatibles con Perl)

PCRE son extensiones de ERE, introducidas originalmente por Perl y adoptadas por GNU grep -Py muchas herramientas modernas y lenguajes de programación , generalmente a través de la biblioteca PCRE . Consulte la documentación de Perl para obtener un formato agradable con ejemplos. PCRE no admite todas las funciones de la última versión de Perl (por ejemplo, la ejecución del código Perl solo es compatible con Perl). Consulte el manual de PCRE para ver un resumen de las funciones compatibles. Las principales adiciones a ERE son:

  • (?:…)es un grupo que no captura: me gusta (…), pero no cuenta para las referencias posteriores.
  • (?=FOO)BAR(búsqueda anticipada) coincide BAR, pero solo si también hay una coincidencia para FOOcomenzar en la misma posición. Esto es más útil para anclar una coincidencia sin incluir el siguiente texto en la coincidencia: foo(?=bar)coincidencias foopero solo si es seguido por bar.
  • (?!FOO)BAR(búsqueda anticipada negativa) coincide BAR, pero tampoco existe una coincidencia FOOen la misma posición. Por ejemplo, (?!foo)[a-z]+coincide con cualquier palabra en minúscula que no comience con foo; [a-z]+(?![0-9)coincide con cualquier palabra en minúscula que no sea seguida por un dígito (por lo tanto foo123, coincide fopero no foo).
  • (?<=FOO)BAR(mirar atrás) coincide BAR, pero solo si está precedido inmediatamente por una coincidencia para FOO. FOOdebe tener una longitud conocida (no puede usar operadores de repetición como *). Esto es más útil para anclar una coincidencia sin incluir el texto anterior en la coincidencia: (?<=^| )foocoincide foopero solo si está precedida por un espacio o por el comienzo de la cadena.
  • (?<!FOO)BAR(retroceso negativo) coincide BAR, pero solo si no está precedido inmediatamente por una coincidencia para FOO. FOOdebe tener una longitud conocida (no puede usar operadores de repetición como *). Esto es más útil para anclar una coincidencia sin incluir el texto anterior en la coincidencia: (?<![a-z])foocoincidencias, foopero solo si no va precedida de una letra minúscula.

Emacs

La sintaxis de Emacs es intermedia entre BRE y ERE. Además de Emacs, es la sintaxis predeterminada para -regexGNU find. Emacs ofrece los siguientes operadores:

  • ^, $, ., […], *, +, ?Como en el ERE
  • \(…\), \|, \{…\}, Como en BRE\DIGIT
  • más secuencias de letras de barra invertida ; \<y \>para límites de palabras; y más en versiones recientes de Emacs, que a menudo no son compatibles con otros motores con una sintaxis similar a Emacs.

Globos de concha

Los globos de shell (comodines) realizan la coincidencia de patrones con una sintaxis que es completamente diferente de las expresiones regulares y menos potente. Además de los shells, estos comodines están disponibles con otras herramientas, como los find -namefiltros rsync. Los patrones POSIX incluyen las siguientes características:

  • ? coincide con cualquier personaje individual.
  • […]es un conjunto de caracteres como en las sintaxis comunes de expresiones regulares. Algunos proyectiles no admiten clases de personajes. Algunos proyectiles requieren en !lugar de ^negar el conjunto.
  • *coincide con cualquier secuencia de caracteres (a menudo, excepto /cuando coinciden las rutas de archivos; si /se excluye *, a **veces se incluye /, pero consulte la documentación de la herramienta).
  • Backslash cita al siguiente personaje.

Ksh ofrece características adicionales que le dan a su patrón que coincide con el poder total de las expresiones regulares. Estas características también están disponibles en bash después de ejecutarse shopt -s extglob. Zsh tiene una sintaxis diferente pero también puede admitir la sintaxis de ksh después setopt ksh_glob.


Otros RE ricos que puede mencionar son vimlos libast de AT&T (como en ksh93).
Stéphane Chazelas 05 de

@ StéphaneChazelas Además de vim, ¿qué programa usa vim regexps? Además de ksh, ¿qué programa usa libast?
Gilles

todo el conjunto de herramientas de AT & T utiliza las ER AT & T ( grep, tw, expr...). Excepto que ksh, ese conjunto de herramientas rara vez se encuentra fuera de AT&T.
Stéphane Chazelas

Según mi entendimiento (y Wikipedia), su término "clase de caracteres" en realidad se refiere a "clase de caracteres POSIX" ... sin embargo, regex(7)está de acuerdo con usted y llama [these]"expresiones de paréntesis" y (dentro de "expresiones de paréntesis") [:these:]"clases de caracteres". No estoy seguro de cómo abordarlo mejor.
Adam Katz

Como sea que los llames, admiten rangos. Definitivamente vale la pena señalar que -especifica un rango y debe ser escapado, primero (después de lo opcional ^), o último si debe tomarse literalmente. (He visto muchos errores derivados de, p. Ej., [A-z]Tenga en cuenta el cambio en mayúsculas y minúsculas), que coincide con los caracteres de los códigos 65 a 122 e incluye accidentalmente cada uno de los siguientes [\]^_`. También he visto el válido pero confuso [!-~]para coincidir con todos los caracteres imprimibles en ANSI , que prefiero ver como [\x21-\x7e], que es al menos directo en su acción, aunque confuso en una dimensión diferente.)
Adam Katz
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.