Main Content

Expresiones regulares dinámicas

Introducción

En una expresión dinámica, puede hacer que el patrón que desea que coincida con regexp dependa del contenido del texto de entrada. De esta forma, puede hacer que los patrones de entrada que varían coincidan en mayor medida en el texto que se analiza. También puede utilizar expresiones dinámicas en los términos de sustitución para utilizarlos con la función regexprep. Esto le proporciona la capacidad de adaptar el texto de sustitución a la entrada analizada.

Puede incluir cualquier número de expresiones dinámicas en los argumentos match_expr o replace_expr de estos comandos:

regexp(text, match_expr)
regexpi(text, match_expr)
regexprep(text, match_expr, replace_expr)

Como ejemplo de expresión dinámica, el siguiente comando regexprep sustituye correctamente al término internationalization por su forma abreviada, i18n. Sin embargo, para utilizarla en un término diferente, como globalization, tiene que utilizar una expresión de sustitución diferente:

match_expr = '(^\w)(\w*)(\w$)';

replace_expr1 = '$118$3';
regexprep('internationalization', match_expr, replace_expr1)
ans =

    'i18n'
replace_expr2 = '$111$3';
regexprep('globalization', match_expr, replace_expr2)
ans =

    'g11n'

El uso de una expresión dinámica ${num2str(length($2))} le permite basar la expresión de sustitución en el texto de entrada para que no tenga que cambiar la expresión cada vez. Este ejemplo utiliza la sintaxis de sustitución dinámica ${cmd}.

match_expr = '(^\w)(\w*)(\w$)';
replace_expr = '$1${num2str(length($2))}$3';

regexprep('internationalization', match_expr, replace_expr)
ans =

    'i18n'
regexprep('globalization', match_expr, replace_expr)
ans =

    'g11n'

Al analizarse, una expresión dinámica debe corresponderse con una expresión regular válida y completa. Además, las expresiones de coincidencia dinámicas que utilizan el carácter de escape de barra invertida (\) requieren dos barras invertidas: una para el análisis inicial de la expresión y otra para la coincidencia completa. Los paréntesis que delimitan expresiones dinámicas no crean un grupo de captura.

Existen tres formas de expresiones dinámicas que puede utilizar en las expresiones de coincidencia y una forma para las expresiones de sustitución, como se describe en las siguientes secciones

Expresiones de coincidencia dinámicas: (??expr)

El operador (??expr) analiza la expresión expr e inserta de nuevo los resultados en la expresión de coincidencia. MATLAB® evalúa la expresión de coincidencia modificada.

Este es un ejemplo del tipo de expresión que puede utilizar con este operador:

chr = {'5XXXXX', '8XXXXXXXX', '1X'};
regexp(chr, '^(\d+)(??X{$1})$', 'match', 'once');

El propósito de este comando en particular es localizar una serie de caracteres X en cada uno de los vectores de caracteres almacenados en el arreglo de celdas de entrada. Sin embargo, tenga en cuenta que el número de X varía en cada vector de caracteres. Si la cuenta no ha variado, podría utilizar la expresión X{n} para indicar que desea que n de estos caracteres coincida. Pero un valor constante de n no funciona en este caso.

La solución utilizada es capturar el número de la cuenta principal (p. ej., 5 en el primer vector de caracteres del arreglo de celda) en un token y después utilizar esa cuenta en una expresión dinámica. La expresión dinámica de este ejemplo es (??X{$1}), donde $1 es el valor que captura el token \d+. El operador {$1} crea un cuantificador del valor de ese token. Ya que la expresión es dinámica, el mismo patrón funciona en los tres vectores de entrada en el arreglo de celdas. Con el primer vector de caracteres de entrada regexp busca cinco caracteres X, con el segundo busca ocho y con el tercero busca solo uno:

regexp(chr, '^(\d+)(??X{$1})$', 'match', 'once')
ans =

  1×3 cell array

    {'5XXXXX'}    {'8XXXXXXXX'}    {'1X'}

Comandos que modifican la expresión de la coincidencia: (??@cmd)

MATLAB utiliza el operador (??@cmd) para incluir los resultados de un comando de MATLAB en la expresión de coincidencia. Este comando debe devolver un término que se puede utilizar en la expresión de coincidencia.

Por ejemplo, utilice la expresión dinámica (??@flilplr($1)) para localizar un palíndromo, "La ruta natural", que se ha integrado en un vector de caracteres mayor.

Primero, cree la cadena de entrada. Asegúrese de que todas las letras están en minúscula y retire todos los caracteres que no sean palabras.

chr = lower(...
  'Find the palindrome Never Odd or Even in this string');

chr = regexprep(chr, '\W*', '')
chr =

    'findthepalindromeneveroddoreveninthisstring'

Localice el palíndromo en el vector de caracteres que utiliza la expresión dinámica:

palindrome = regexp(chr, '(.{3,}).?(??@fliplr($1))', 'match')
palindrome =

  1×1 cell array

    {'neveroddoreven'}

La expresión dinámica invierte el orden de las letras que componen el vector de caracteres y luego intenta hacer coincidir tanto del vector de orden invertido como sea posible. Esto requiere una expresión dinámica porque el valor de $1 se basa en el valor del token (.{3,}).

Las expresiones dinámicas de MATLAB tienen acceso al área de trabajo activa actualmente. Esto significa que puede cambiar cualquiera de las funciones o variables utilizadas en una expresión dinámica simplemente cambiando las variables en el área de trabajo. Repita el último comando del ejemplo anterior, pero esta vez defina la función para que se llame en la expresión que utiliza un identificador de función guardado en el área de trabajo base:

fun = @fliplr;

palindrome = regexp(chr, '(.{3,}).?(??@fun($1))', 'match')
palindrome =

  1×1 cell array

    {'neveroddoreven'}

Comandos que cumplen un propósito funcional: (?@cmd)

El operador (?@cmd) especifica un comando de MATLAB que regexp o regexprep ejecutarán mientras analizan la expresión de coincidencia general. A diferencia de otras expresiones dinámicas de MATLAB, este operador no altera los contenidos de la expresión en la que se utiliza. Es más, puede utilizar esta funcionalidad para conseguir que MATLAB informe solo de qué pasos toma cuando analiza los contenidos de una de sus expresiones regulares. Esta funcionalidad puede ser útil en el diagnóstico de sus expresiones regulares.

El siguiente ejemplo analiza una palabra para cero o más caracteres seguidos por dos caracteres idénticos seguidos de nuevo por cero o más caracteres:

regexp('mississippi', '\w*(\w)\1\w*', 'match')
ans =

  1×1 cell array

    {'mississippi'}

Para realizar un seguimiento de los pasos exactos que toma MATLAB en la determinación de la coincidencia, el ejemplo inserta un script breve (?@disp($1)) en la expresión para mostrar los caracteres que finalmente constituyen la coincidencia. Debido a que el ejemplo utiliza cuantificadores avariciosos, MATLAB intenta hacer coincidir la mayor cantidad posible del vector de caracteres. Por tanto, aunque MATLAB encuentra una coincidencia en el comienzo de la cadena, continúa buscando más coincidencias hasta que llega al final de la cadena. A partir de aquí, retrocede por las letras i, p y después p, deteniéndose en ese punto porque se cumple la coincidencia:

regexp('mississippi', '\w*(\w)(?@disp($1))\1\w*', 'match')
i
p
p

ans =

  1×1 cell array

    {'mississippi'}

Intente el mismo ejemplo de nuevo, este vez haciendo que el primer cuantificador sea perezoso (*?). Nuevamente, MATLAB hace la misma coincidencia:

regexp('mississippi', '\w*?(\w)\1\w*', 'match')
ans =

  1×1 cell array

    {'mississippi'}

Sin embargo, al insertar un script dinámico, puede ver que esta vez MATLAB ha hecho coincidir el texto de una manera bastante diferente. En este caso, MATLAB utiliza la primera coincidencia que pueda encontrar y no tiene en cuenta el resto del texto:

regexp('mississippi', '\w*?(\w)(?@disp($1))\1\w*', 'match')
m
i
s

ans =

  1×1 cell array

    {'mississippi'}

Para demostrar la versatilidad de este tipo de expresión dinámica, observe el siguiente ejemplo que construye progresivamente un arreglo de celdas a medida que MATLAB analiza el texto de entrada de forma iterativa. El operador (?!) que se encuentra al final de la expresión es en realidad un operador positivo vacío y provoca un error en cada iteración. Este error provocado es necesario si desea seguir los pasos que realiza MATLAB para resolver la expresión.

MATLAB realiza un número de transferencias a lo largo del texto de entrada y cada vez intenta una combinación de letras diferente para ver si se puede encontrar una coincidencia mejor que la anterior. En cualquier transferencia en la que no se encuentre ninguna coincidencia, la prueba deriva en un vector de caracteres vacío. El script dinámico (?@if(~isempty($&))) sirve para omitir los vectores de caracteres vacíos del arreglo de celdas matches:

matches = {};
expr = ['(Euler\s)?(Cauchy\s)?(Boole)?(?@if(~isempty($&)),' ...
   'matches{end+1}=$&;end)(?!)'];

regexp('Euler Cauchy Boole', expr);

matches
matches =

  1×6 cell array

    {'Euler Cauchy Bo…'}    {'Euler Cauchy '}    {'Euler '}    {'Cauchy Boole'}    {'Cauchy '}    {'Boole'}

Los operadores $& (o el equivalente $0), $` y $' se refieren a la parte del texto de entrada que es una coincidencia actualmente, todos los caracteres que preceden a la coincidencia actual y todos los caracteres que siguen a la coincidencia actual, respectivamente. Estos operadores a veces son útiles a la hora de trabajar con expresiones dinámicas, especialmente las que utilizan el operador (?@cmd).

El siguiente ejemplo analiza el texto de entrada para buscar la letra g. En cada iteración que se realiza a través del texto, regexp compara el carácter actual con g y si no lo encuentra, avanza al siguiente carácter. El ejemplo registra el progreso del análisis a lo largo del texto marcando la ubicación actual que se ha analizado con un carácter ^.

(Los operadores $` y capturan esta parte del texto que precede y sigue a la ubicación de análisis actual. Tiene que utilizar dos comillas simples [$''] para expresar la secuencia cuando aparece en el texto).

chr = 'abcdefghij';
expr = '(?@disp(sprintf(''starting match: [%s^%s]'',$`,$'')))g';

regexp(chr, expr, 'once');
starting match: [^abcdefghij]
starting match: [a^bcdefghij]
starting match: [ab^cdefghij]
starting match: [abc^defghij]
starting match: [abcd^efghij]
starting match: [abcde^fghij]
starting match: [abcdef^ghij]

Comandos de las expresiones de sustitución: ${cmd}

El operador ${cmd} modifica el contenido del patrón de sustitución de una expresión regular, lo que hace que este patrón se adapte a los parámetros del texto de entrada que pueden variar de un uso al siguiente. Al igual que con las otras expresiones dinámicas que se utilizan en MATLAB, puede incluir cualquier número de estas expresiones en la expresión de sustitución global.

Los comandos de las expresiones de sustitución solo revisan el área de trabajo local para buscar variables. Las áreas de trabajo del autor de la llamada y global no están disponibles para los comandos en expresiones de sustitución

En la llamada a regexprep que se muestra a continuación, el patrón de sustitución es '${convertMe($1,$2)}'. En este caso, el patrón de sustitución completo es una expresión dinámica:

regexprep('This highway is 125 miles long', ...
          '(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}');

La expresión dinámica le dice a MATLAB que ejecute una función denominada convertMe mediante los dos tokens (\d+\.?\d*) y (\w+), que derivan del texto que coincide, como argumentos de entrada de la llamada a convertMe. El patrón de sustitución requiere una expresión dinámica porque los valores de $1 y $2 se generan en tiempo de ejecución.

El siguiente ejemplo define el archivo denominado convertMe que convierte las medidas de unidades imperiales a métricas.

function valout  = convertMe(valin, units)
switch(units)
    case 'inches'
        fun = @(in)in .* 2.54;
        uout = 'centimeters';
    case 'miles'
        fun = @(mi)mi .* 1.6093;
        uout = 'kilometers';
    case 'pounds'
        fun = @(lb)lb .* 0.4536;
        uout = 'kilograms';
    case 'pints'
        fun = @(pt)pt .* 0.4731;
        uout = 'litres';
    case 'ounces'
        fun = @(oz)oz .* 28.35;
        uout = 'grams';
end
val = fun(str2num(valin));
valout = [num2str(val) ' ' uout];
end

En la línea de comandos, llame a la función convertMe desde regexprep y transfiera los valores de la cantidad que se va a convertir y el nombre de la unidad imperial:

regexprep('This highway is 125 miles long', ...
          '(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')
ans =

    'This highway is 201.1625 kilometers long'
regexprep('This pitcher holds 2.5 pints of water', ...
          '(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')
ans =

    'This pitcher holds 1.1828 litres of water'
regexprep('This stone weighs about 10 pounds', ...
          '(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')
ans =

    'This stone weighs about 4.536 kilograms'

Al igual que ocurre con el operador (??@ ) descrito en una sección anterior, el operador ${ } tiene acceso a las variables del área de trabajo activa actualmente. El siguiente comando regexprep utiliza el arreglo A que se define en el área de trabajo base:

A = magic(3)
A =

     8     1     6
     3     5     7
     4     9     2
regexprep('The columns of matrix _nam are _val', ...
          {'_nam', '_val'}, ...
          {'A', '${sprintf(''%d%d%d '', A)}'})
ans =

    'The columns of matrix A are 834 159 672'

Consulte también

| |

Temas relacionados