Escritura de expresiones regulares eficaces

Aquí se tratan algunos de los aspectos importantes que debe tener en cuenta cuando se elaboran expresiones regulares eficaces.

Para conocer las construcciones de expresión regular que se pueden utilizar con analizadores, etiquetas y filtros de datos, consulte la documentación de Java Platform Standard Ed. 8.

En el caso de consultas y expresiones regulares de tiempo de consulta, consulte la sintaxis de RE2J en Implantación de Java de RE2.

Clases de caracteres

Las clases de caracteres especifican los caracteres que está intentando hacer coincidir o no. Asegúrese de sustituir . en .*s por un carácter más específico. .* cambiará invariablemente al final de la entrada y, a continuación, retrocederá, es decir, volverá al estado guardado anteriormente para continuar con la búsqueda de una coincidencia. Al usar una clase de carácter específico, tiene el control sobre cuántos caracteres hará el * que el motor de expresiones regulares consuma, permitiendo detener el retroceso incontrolado.

Tenga en cuenta la siguiente expresión regular de ejemplo:

(\d{4})(\d{2})(\d{2}),(\S+),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+)

Para la siguiente entrada:

20150220,201502,16798186260,tvN,Entertainment/Music,Female,67,2,Individual,ollehtv Economic,Commercial Housing,5587,0,2,0,1,1

Como resultado de la expresión regular especificada, se puede buscar la coincidencia con retroceso. Oracle Logging Analytics detecta esta situación y se cancela la operación de coincidencia.

Al cambiar la expresión regular al ejemplo siguiente, puede asegurarse de que la coincidencia termine antes. Observe que [\S\s]* se cambia a [^,], lo que evita el retroceso innecesario.

(\d{4})(\d{2})(\d{2}),(\S+),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+)

Cuantificadores diferidos

En muchas expresiones regulares, los cuantificadores expansivos (.*s) se pueden reemplazar de forma segura con cuantificadores diferidos (.*?s), lo que mejora el rendimiento de la expresión regular sin cambiar el resultado.

Considere la entrada:

Trace file /u01/app/oracle/diag/rdbms/navisdb/NAVISDB/trace/NAVISDB_arc0_3941.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /u01/app/oracle/product/11.2.0/db_1
System name: Linux
Node name: NAVISDB
Release: 2.6.18-308.el5
Version: #1 SMP Fri Jan 27 17:17:51 EST 2012
Machine: x86_64
Instance name: NAVISDB
Redo thread mounted by this instance: 1
Oracle process number: 21
Unix process pid: 3941, image: oracle@NAVISDB (ARC0)

Tenga en cuenta la siguiente expresión regular expansiva para la entrada especificada:

Trace\sfile\s(\S*).*ORACLE_HOME\s*[:=]\s*(\S*).*System\sname:\s*(\S*).*Node\sname:\s*(\S*).*Release:\s*(\S*).*Machine:\s*(\S*).*Instance\sname:\s*(\S*).*Redo\sthread\smounted\sby\sthis\sinstance:\s(\d*).*Oracle\sprocess\snumber:\s*(\d*).*Unix\sprocess\spid:\s(\d*).*image:\s+([^\n\r]*)

El motor de expresiones regulares se dispararía hasta el final de la entrada, cada vez que encuentre .*.. La primera vez que aparece .*, consume todas las entradas y, a continuación, retrocede hasta que llega a ORACLE_HOME. Este método no es eficaz para buscar las coincidencias. La expresión regular diferida alternativa es como la siguiente:

Trace\sfileRelease:\s*(\S*).*?Machine:\s*(\S*).*?Instance\sname:\s*(\S*).*?Redo\sthread\smounted\sby\sthis\sinstance:\s(\d*).*?Oracle\sprocess\snumber:\s*(\d*).*?Unix\sprocess\spid:\s(\d*).*?image:\s+([^\n\r]*)

La expresión regular anterior se consume starting desde el principio de la cadena hasta que alcance ORACLE_HOME, momento en el que puede proceder a la coincidencia con el resto de la cadena.

Nota: Si el campo ORACLE_HOME aparece al principio de la entrada, se debe utilizar el cuantificador expansivo. Si el campo ORACLE_HOME aparece al final, es posible que sea adecuado utilizar el cuantificador expansivo.

Anclajes

Los anclajes indican al motor de expresiones regulares que desea que el cursor esté en un lugar concreto de la entrada. Los anclajes más habituales son ^ y $, lo que indica el principio y el final de la entrada.

Tenga en cuenta las siguientes expresiones regulares para buscar una dirección IPv4:

\d{1,3}\.d{1,3}.\d{1,3}.\d{1,3}
^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}

Observe que la segunda expresión regular comienza con ^ y, por lo tanto, es específica de la dirección IP que aparece al principio de la entrada.

Estamos buscando la expresión regular que, en la entrada, se parezca al siguiente ejemplo:

107.21.20.1 - - [07/Dec/2012:18:55:53 -0500] "GET
      /extension/bsupport/design/cl/images/btn_letschat.png HTTP/1.1" 200
    2144

Una entrada no coincidente sería similar al siguiente ejemplo:

[07/Dec/2012:23:57:13 +0000] 1354924633 GET "/favicon.ico" "" HTTP/1.1 200 82726 "-"
      "ELB-HealthChecker/1.0"

La segunda expresión regular (que comienza con ^) se ejecuta mucho más rápido en la entrada no coincidente, ya que descarta la entrada no coincidente casi de inmediato.

La importancia de la alternancia

El orden de alternancia cuenta, así que coloque las opciones más comunes en la parte delantera para que puedan emparejarse más rápidamente. Si las opciones más raras se colocan en primer lugar, el motor de expresiones regulares perderá tiempo en comprobarlas antes de comprobar las opciones más comunes que es más probable que tengan éxito. Además, intente extraer patrones comunes. Por ejemplo, en lugar de (abcd|abef), utilice ab(cd|ef).

Tenga en cuenta las siguientes expresiones regulares:

{TIMEDATE}\s{0,1}:\s*(?:|\[)(\w+)(?:\:|\])(?:\[|)(\d+)(?:\:|\])(.{1,1000}).*
{TIMEDATE}\s{0,1}:\s*(?:\[|)(\w+)(?:\:|\])(?:\[|)(\d+)(?:\:|\])(.{1,1000}).*

En la siguiente entrada:

2014-06-16 12:13:46.743: [UiServer][1166092608] {0:7:2} Done for
ctx=0x2aaab45d8330

La segunda expresión regular coincide más rápido, ya que la alternancia busca el carácter [ primero, seguido de null. A medida que la entrada tiene [, la coincidencia se ejecuta más rápido.

Expresiones de análisis de ejemplo

Puede consultar las siguientes expresiones de análisis de ejemplo para crear una expresión de análisis adecuada para extraer los valores del archivo log.

Un archivo log incluye entradas que se generan concatenando varios valores de campo. Puede que no necesite ver todos los valores de campo para analizar un archivo log de un formato concreto. Mediante un analizador, solo puede extraer los valores de los campos que desee ver.

Un analizador extrae campos de un archivo log en función de la expresión de análisis definida. Una expresión de análisis se escribe como una expresión regular que define un patrón de búsqueda. En una expresión de análisis, delimita los patrones de búsqueda con paréntesis (), para cada campo coincidente que desee extraer de una entrada de log. No se extraerá ningún valor que coincida con un patrón de búsqueda que esté fuera de los paréntesis.

Ejemplo 1

Si desea analizar las siguientes entradas de log de ejemplo:

Jun 20 15:19:29 hostabc rpc.gssd[2239]: ERROR: can't open clnt5aa9: No such file or directory
Jul 29 11:26:28 hostabc kernel: FS-Cache: Loaded
Jul 29 11:26:28 hostxyz kernel: FS-Cache: Netfs 'nfs' registered for caching

A continuación, debe estar la expresión de análisis:

(\S+)\s+(\d+)\s(\d+):(\d+):(\d+)\s(\S+)\s(?:([^:\[]+)(?:\[(\d+)\])?:\s+)?(.+)

En el ejemplo anterior, algunos de los valores que captura la expresión de análisis son:

  • (\S+): varios caracteres que no sean espacios en blanco para el mes

  • (\d+): varios caracteres que no sean espacios en blanco para el día

  • (?:([^:\[]+): (opcional) todos los caracteres excepto ^, :, \, []; se aplica al nombre del servicio

  • (.+): (opcional) contenido del mensaje principal

Ejemplo 2

Si desea analizar las siguientes entradas de log de ejemplo:

####<Apr 27, 2014 4:01:42 AM PDT> <Info> <EJB> <host> <AdminServer> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <OracleSystemUser> <BEA1-13E2AD6CAC583057A4BD> <b3c34d62475d5b0b:6e1e6d7b:143df86ae85:-8000-000000000000cac6> <1398596502577> <BEA-010227> <EJB Exception occurred during invocation from home or business: weblogic.ejb.container.internal.StatelessEJBHomeImpl@2f9ea244 threw exception: javax.ejb.EJBException: what do i do: seems an odd quirk of the EJB spec. The exception is:java.lang.StackOverflowError>
####<Jul 30, 2014 8:43:48 AM PDT> <Info> <RJVM> <example.com> <> <Thread-9> <> <> <> <1406735028770> <BEA-000570> <Network Configuration for Channel "AdminServer" Listen Address example.com:7002 (SSL) Public Address N/A Http Enabled true Tunneling Enabled false Outbound Enabled false Admin Traffic Enabled true ResolveDNSName Enabled false> 

A continuación, debe estar la expresión de análisis:

####<(\p{Upper}\p{Lower}{2})\s+([\d]{1,2}),\s+([\d]{4})\s+([\d]{1,2}):([\d]{2}):([\d]{2})\s+(\p{Upper}{2})(?:\s+(\w+))?>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<\d{10}\d{3}>\s+<(.*?)>\s+<(.*?)(?:\n(.*))?>\s*

En el ejemplo anterior, algunos de los valores que captura la expresión de análisis son:

  • (\p{Upper}\p{Lower}{2}): abreviatura de 3 letras del mes; con la primera letra en mayúscula seguida de dos letras en minúscula

  • ([\d]{1,2}): día con 1 o 2 dígitos

  • ([\d]{4}): año con 4 dígitos

  • ([\d]{1,2}): hora con 1 o 2 dígitos

  • ([\d]{2}): minuto con 2 dígitos

  • ([\d]{2}): segundo con 2 dígitos

  • (\p{Upper}{2}): AM/PM con 2 letras en mayúscula

  • (?:\s+(\w+)): (opcional, puede que algunas entradas no devuelvan ningún valor para esto) Varios caracteres alfanuméricos para la zona horaria

  • (.*?): (opcional, puede que algunas entradas no devuelvan ningún valor para esto) Uno o varios caracteres para el nivel de gravedad; en este caso, <INFO>

  • (.*): detalles adicionales junto con el mensaje

Patrones de búsqueda

Algunos de los patrones más utilizados se explican en la siguiente tabla:

Patrón Descripción Ejemplo
. Cualquier carácter, excepto el salto de línea d.f coincide con def, daf, dbf, etc.
* Cero o más veces D*E*F* coincide con DDEEFF, DEF, DDFF, EEFF, etc.
? Una vez o ninguna; opcional colou?r coincide tanto con colour como con color
+ Una o más Stage \w-\w+ coincide con Stage A-b1_1, Stage B-a2, etc.
{2} Exactamente dos veces [\d]{2} coincide con 01, 11, 21, etc.
{1,2} Entre dos y cuatro veces [\d]{1,2} coincide con 1, 12, etc.
{3,} Tres o más veces [\w]{3,} coincide con ten, hello, h2134, etc.
[ ... ] Uno de los caracteres entre corchetes [AEIOU] coincide con una vocal en mayúscula
[x-y] Uno de los caracteres del rango de x a y [A-Z]+ coincide con ACT, ACTION, BAT, etc.
[^x] Un carácter que no sea x [^/d]{2} coincide con AA, BB, AC, etc.
[^x-y] Uno de los caracteres no está en el rango entre x e y [^a-z]{2} coincide con A1, BB, B2, etc.
[\d\D] Un carácter que sea un dígito o que no sea un dígito [\d\D]+ coincide con cualquier carácter, incluidas las nuevas líneas, con la que no coincide la expresión regular
\s Un espacio en blanco (\S+)\s+(\d+) coincide con AA 123, a_ 221, etc.
\S Un carácter que no sea un espacio en blanco (\S+) coincide con abcd, ABC, A1B2C3, etc.
\n Una nueva línea (\d)\n(\w) coincide con:

1

A

\w Un carácter alfanumérico [\w-\w\w\w] coincide con a-123, 1-aaa, etc.
\p{Lower} Letras en minúscula \p{Lower}{2} coincide con aa, ab, ac, bb, etc.
\p{Upper} Letras en mayúscula \p{Upper} coincide con A, B, C, etc.
\ seguida de ?, [], *, . Carácter de escape; para utilizar los caracteres después de \ como literales \? devuelve ?