Regex para obtener un número sólo si no hay un string antes

publicado por: Anonymous

Tengo líneas del siguiente estilo:

fecha vencimiento 20-12-2017
fecha 20-12-2017

Y lo que ando buscando es la forma de hacer una expresión regular que en caso de que antes o después de la fecha aparezca la palabra vencimiento no matchee.

Hasta ahora tengo la siguiente regex para reconocer las fechas:

(b[0-9]{1,2}(/|-)[0-1]([0-2]*)(/|-)[0-9]{4}b)

Pero no encuentro manera de validar lo de la palabra “vencimiento”.

solución

¿Cómo encontrar una fecha, siempre y cuando no se encuentre vencimiento dentro del texto?

La forma que recomendaría es utilizar 2 expresiones:

  1. Buscar que vencimiento no esté en el texto (preferiblemente sin una expresión regular)

  2. Capturar la fecha.

Esto puede ser muchísimo más efectivo que validar todo dentro del mismo regex.


Si aún así se busca realizar todo dentro de una misma expresión:

¿Cómo encontrar una fecha, siempre y cuando no se encuentre vencimiento dentro de la misma línea?

^(?!.*bvencimientob).*b([0-3]?d[-/][01]?d[-/]d{4})b

Como se ve, con ^ estamos anclando la expresión al comienzo de la línea, utilizando el flag Pattern.MULTILINE.

La estructura más importante en esta expresión es el inspección negativa (negative lookahead), de la forma (?!expr) y que básicamente significa “no está seguido por expr. En caso de que expr coincida con el texto, el intento actual falla.
De esta forma, al utilizar ^(?!.*bvencimientob) nos garantizamos que la palabra vencimiento no esté en toda la línea.

Por último, cabe mencionar que modifiqué la parte de la fecha en tu expresión, ya que [0-1]([0-2]*) sólo coincide con números como 00, 11, 112112, etc; pero no coincide con 05.

La parte de la fecha la estamos encerrando en paréntesis, de modo de generar un grupo, y poder obtener sólo esa parte específica con Matcher#group(1).

Es decir, capturamos una fecha por línea (sólo una), siempre que vencimiento no esté en la misma línea.

Código:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
final String regex = "^(?!.*\bvencimiento\b).*\b([0-3]?\d[-/][01]?\d[-/]\d{4})\b";
final String texto = "fecha vencimiento 20-12-2017nfecha 20-12-2017notra: 12/05/2016";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(texto);

if (matcher.find()) {
    do {
        System.out.println("Fecha: " + matcher.group(1));
    } while (matcher.find());
} else {
    System.out.println("No se encontró ninguna fecha");
}

Resultado:

Fecha: 20-12-2017
Fecha: 12/05/2016

Demo:

http://ideone.com/zZxYO3


¿Y cómo encontrar una fecha, siempre y cuando vencimiento no sea la palabra que la precede (con cualquier cantidad de W en el medio)?

En ese caso, utilizaríamos una inspección hacia atrás negativa (negative lookbehind). La construcción (?<!expr) Se podría traducir como “la posición actual no está precedida por expr“.

En resumen, sería:

(?:^W*|b(?<!vencimiento)W+)([0-3]?d[-/][01]?d[-/]d{4}b)

que, en cada límite de palabra, verifica que no esté precedido por vencimiento.

Código:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
final String regex = "(?:^\W*|\b(?<!vencimiento)\W+)([0-3]?\d[-/][01]?\d[-/]\d{4}\b)";
final String texto = "fecha vencimiento 20-12-2017nfecha 20-12-2017notra: 12/05/2016";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(texto);

if (matcher.find()) {
    do {
        System.out.println("Fecha: " + matcher.group(1));
    } while (matcher.find());
} else {
    System.out.println("No se encontró ninguna fecha");
}

Resultado:

Fecha: 20-12-2017
Fecha: 12/05/2016

Demo:

http://ideone.com/ccrt55


Alternativamente, para no estar revisando hacia atrás en cada límite de palabra, podríamos verificar hacia adelante, algo que podría ser un poco más eficiente en textos largos. La estrategia sería: ir consumiendo posición por posición, viendo si la siguiente palabra es vencimiento y, si es, consumir la siguiente palabra de forma posesiva, de modo que no pueda volver hacia atrás.

GW*+(?:(?:vencimientoW+)?+w+W+)*?([0-3]?d[-/][01]?d[-/]d{4}b)

Al usar G, estamos anclando el inicio de cada intento al comienzo del texto o al fin de la coincidencia anterior (si hay más de una fecha en el texto).

Código:

final String regex = "\G\W*+(?:(?:vencimiento\W+)?+\w+\W+)*?([0-3]?\d[-/][01]?\d[-/]\d{4}\b)";
final String texto = "fecha vencimiento 20-12-2017nfecha 20-12-2017notra: 12/05/2016";

final Pattern pattern = Pattern.compile(regex);
final Matcher matcher = pattern.matcher(texto);

if (matcher.find()) {
    do {
        System.out.println("Fecha: " + matcher.group(1));
    } while (matcher.find());
} else {
    System.out.println("No se encontró ninguna fecha");
}

Demo:

http://ideone.com/Gahqjl

Respondido por: Anonymous

Leave a Reply

Your email address will not be published.