26 de agosto de 2022

Alternancia (O) |

Alternancia es un término en expresión regular que simplemente significa “O”.

En una expresión regular se denota con un carácter de línea vertical |.

Por ejemplo, necesitamos encontrar lenguajes de programación: HTML, PHP, Java o JavaScript.

La expresión regular correspondiente es: html|php|java(script)?.

Un ejemplo de uso:

let regexp = /html|php|css|java(script)?/gi;

let str = "Primera aparición de HTML, luego CSS, luego JavaScript";

alert( str.match(regexp) ); // 'HTML', 'CSS', 'JavaScript'

Ya vimos algo similar: corchetes. Permiten elegir entre varios caracteres, por ejemplo gr[ae]y coincide con gray o grey.

Los corchetes solo permiten caracteres o conjuntos de caracteres. La alternancia permite cualquier expresión. Una expresión regular A|B|C significa una de las expresiones A, B o C.

Por ejemplo:

  • gr(a|e)y significa exactamente lo mismo que gr[ae]y.
  • gra|ey significa gra o ey.

Para aplicar la alternancia a una parte elegida del patrón, podemos encerrarla entre paréntesis:

  • I love HTML|CSS coincide con I love HTML o CSS.
  • I love (HTML|CSS) coincide con I love HTML o I love CSS.

Ejemplo: Expresión regular para el tiempo

En artículos anteriores había una tarea para construir una expresión regular para buscar un horario en la forma hh:mm, por ejemplo 12:00. Pero esta simple expresión \d\d:\d\d es muy vaga. Acepta 25:99 como tiempo (ya que 99 segundos coinciden con el patrón, pero ese tiempo no es válido).

¿Cómo podemos hacer un mejor patrón?

Podemos utilizar una combinación más cuidadosa. Primero, las horas:

  • Si el primer dígito es 0 o 1, entonces el siguiente dígito puede ser cualquiera: [01]\d.
  • De otra manera, si el primer dígito es 2, entonces el siguiente debe ser [0-3].
  • (no se permite ningún otro dígito)

Podemos escribir ambas variantes en una expresión regular usando alternancia: [01]\d|2[0-3].

A continuación, los minutos deben estar comprendidos entre 00 y 59. En el lenguaje de expresiones regulares se puede escribir como [0-5]\d: el primer dígito 0-5, y luego cualquier otro.

Si pegamos minutos y segundos juntos, obtenemos el patrón: [01]\d|2[0-3]:[0-5]\d.

Ya casi terminamos, pero hay un problema. La alternancia | ahora pasa a estar entre [01]\d y 2[0-3]:[0-5]\d.

Es decir: se agregan minutos a la segunda variante de alternancia, aquí hay una imagen clara:

[01]\d  |  2[0-3]:[0-5]\d

Este patrón busca [01]\d o 2[0-3]:[0-5]\d.

Pero eso es incorrecto, la alternancia solo debe usarse en la parte “horas” de la expresión regular, para permitir [01]\d O 2[0-3]. Corregiremos eso encerrando las “horas” entre paréntesis: ([01]\d|2[0-3]):[0-5]\d.

La solución final sería:

let regexp = /([01]\d|2[0-3]):[0-5]\d/g;

alert("00:00 10:10 23:59 25:99 1:2".match(regexp)); // 00:00,10:10,23:59

Tareas

Hay muchos lenguajes de programación, por ejemplo, Java, JavaScript, PHP, C, C ++.

Crea una expresión regular que los encuentre en la cadena Java JavaScript PHP C++ C:

let regexp = /your regexp/g;

alert("Java JavaScript PHP C++ C".match(regexp)); // Java JavaScript PHP C++ C

La primera idea puede ser listar los idiomas con | en el medio.

Pero eso no funciona bien:

let regexp = /Java|JavaScript|PHP|C|C\+\+/g;

let str = "Java, JavaScript, PHP, C, C++";

alert( str.match(regexp) ); // Java,Java,PHP,C,C

El motor de expresiones regulares busca las alternancias una por una. Es decir: primero verifica si tenemos Java, de lo contrario – busca JavaScript y así sucesivamente.

Como resultado, nunca se puede encontrar JavaScript, simplemente porque encuentra primero Java.

Lo mismo con C y C++.

Hay dos soluciones para ese problema:

  1. Cambiar el orden para comprobar primero la coincidencia más larga: JavaScript|Java|C\+\+|C|PHP.
  2. Fusionar variantes con el mismo inicio: Java(Script)?|C(\+\+)?|PHP.

En acción:

let regexp = /Java(Script)?|C(\+\+)?|PHP/g;

let str = "Java, JavaScript, PHP, C, C++";

alert( str.match(regexp) ); // Java,JavaScript,PHP,C,C++

Un “bb-tag” se ve como [tag]...[/tag], donde tag es uno de: b, url o quote.

Por ejemplo:

[b]text[/b]
[url]http://google.com[/url]

BB-tags se puede anidar. Pero una etiqueta no se puede anidar en sí misma, por ejemplo:

Normal:
[url] [b]http://google.com[/b] [/url]
[quote] [b]text[/b] [/quote]

No puede suceder:
[b][b]text[/b][/b]

Las etiquetas pueden contener saltos de línea, eso es normal:

[quote]
  [b]text[/b]
[/quote]

Cree una expresión regular para encontrar todas las BB-tags con su contenido.

Por ejemplo:

let regexp = /your regexp/flags;

let str = "..[url]http://google.com[/url]..";
alert( str.match(regexp) ); // [url]http://google.com[/url]

Si las etiquetas están anidadas, entonces necesitamos la etiqueta externa (si queremos podemos continuar la búsqueda en su contenido):

let regexp = /your regexp/flags;

let str = "..[url][b]http://google.com[/b][/url]..";
alert( str.match(regexp) ); // [url][b]http://google.com[/b][/url]

La etiqueta de apertura es \[(b|url|quote)].

Luego, para encontrar todo hasta la etiqueta de cierre, usemos el patrón.*? con la bandera s para que coincida con cualquier carácter, incluida la nueva línea, y luego agreguemos una referencia inversa a la etiqueta de cierre.

El patrón completo: \[(b|url|quote)\].*?\[/\1].

En acción:

let regexp = /\[(b|url|quote)].*?\[\/\1]/gs;

let str = `
  [b]hello![/b]
  [quote]
    [url]http://google.com[/url]
  [/quote]
`;

alert( str.match(regexp) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote]

Tenga en cuenta que además de escapar [ tuvimos que escapar de una barra para la etiqueta de cierre [\/\1], porque normalmente la barra cierra el patrón.

Crea una expresión regular para encontrar cadenas entre comillas dobles "...".

Las cadenas deben admitir el escape, de la misma manera que lo hacen las cadenas de JavaScript. Por ejemplo, las comillas se pueden insertar como \", una nueva línea como \n, y la barra invertida misma como \\.

let str = "Just like \"here\".";

Tenga en cuenta, en particular, que una comilla escapada \" no termina una cadena.

Por lo tanto, deberíamos buscar de una comilla a otra (la de cierre), ignorando las comillas escapadas en el camino.

Esa es la parte esencial de la tarea, de lo contrario sería trivial.

Ejemplos de cadenas para hacer coincidir:

.. "test me" ..
.. "Say \"Hello\"!" ... (comillas escapadas dentro)
.. "\\" ..  (doble barra invertida dentro)
.. "\\ \"" ..  (doble barra y comilla escapada dentro.)

En JavaScript, necesitamos duplicar las barras para pasarlas directamente a la cadena, así:

let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';

// the in-memory string
alert(str); //  .. "test me" .. "Say \"Hello\"!" .. "\\ \"" ..

La solución: /"(\\.|[^"\\])*"/g.

El paso a paso:

  • Primero buscamos una comilla de apertura "
  • Luego, si tenemos una barra invertida \\ (tenemos que duplicarla en el patrón porque es un carácter especial). Luego, cualquier carácter está bien después de él (un punto).
  • De lo contrario, tomamos cualquier carácter excepto una comilla (que significaría el final de la cadena) y una barra invertida (para evitar barras invertidas solitarias, la barra invertida solo se usa con algún otro símbolo después): [^"\\]
  • …Y así sucesivamente hasta la comilla de cierre.

En acción:

let regexp = /"(\\.|[^"\\])*"/g;
let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';

alert( str.match(regexp) ); // "test me","Say \"Hello\"!","\\ \""

Escriba una expresión regular para encontrar la etiqueta <style...>. Debe coincidir con la etiqueta completa: puede no tener atributos <style> o tener varios de ellos <style type="..." id="...">.

…¡Pero la expresión regular no debería coincidir con <styler>!

Por ejemplo:

let regexp = /your regexp/g;

alert( '<style> <styler> <style test="...">'.match(regexp) ); // <style>, <style test="...">

El inicio del patrón es obvio: <style.

…Pero entonces no podemos simplemente escribir <style.*?>, porque <styler> coincidiría.

Necesitamos un espacio después <style y luego, opcionalmente, algo más o el final >.

En el lenguaje de expresión regular: <style(>|\s.*?>).

En acción:

let regexp = /<style(>|\s.*?>)/g;

alert( '<style> <styler> <style test="...">'.match(regexp) ); // <style>, <style test="...">
Mapa del Tutorial