¿Qué significa 'perezoso' y 'codicioso' en el contexto de las expresiones regulares?


Respuestas:


644

Codicioso consumirá tanto como sea posible. En http://www.regular-expressions.info/repeat.html vemos el ejemplo de intentar hacer coincidir las etiquetas HTML con <.+>. Supongamos que tiene lo siguiente:

<em>Hello World</em>

Puede pensar que <.+>( .significa cualquier personaje que no sea de nueva línea y +significa uno o más ) solo coincidiría con el <em>y el </em>, cuando en realidad será muy codicioso, y pasará del primero <al último >. Esto significa que coincidirá en <em>Hello World</em>lugar de lo que quería.

Hacerlo perezoso ( <.+?>) evitará esto. Al agregar el ?después del +, le decimos que se repita la menor cantidad de veces posible , por lo que lo primero >que aparece es dónde queremos detener la coincidencia.

Te animo a que descargues RegExr , una gran herramienta que te ayudará a explorar expresiones regulares. Lo uso todo el tiempo.


2
así que si usa codicioso, ¿tendrá 3 (1 elemento + 2 etiquetas) coincidencias o solo 1 coincidencia (1 elemento)?
ajsie

10
Solo coincidiría 1 vez, comenzando desde el primer < y terminando con el último > .
Sampson

3
Pero hacerlo perezoso coincidiría dos veces, dándonos la etiqueta de apertura y cierre, ignorando el texto intermedio (ya que no se ajusta a la expresión).
Sampson

Otra gran herramienta que siempre uso: debuggex.com También tiene una función "Insertar en StackOverflow".
Ron van der Heijden

8
Solo para agregar que también hay una manera codiciosa de hacerlo: <[^>]+> regex101.com/r/lW0cY6/1
alanbuchanan

302

'Codicioso' significa que coincida la cadena más larga posible

'Perezoso' significa una cadena más corta posible.

Por ejemplo, los codiciosos h.+lpartidos 'hell'en 'hello'pero los perezosos h.+?lpartidos 'hel'.


97
¿Brillante, tan perezoso se detendrá tan pronto como se cumpla la condición, pero codicioso significa que se detendrá solo una vez que la condición ya no se cumpla?
Andrew S

3
Para todas las personas que leen la publicación: los cuantificadores codiciosos o perezosos por sí mismos no coincidirán con la subcadena más larga / más corta posible. Tendría que usar una ficha codiciosa templada o usar enfoques que no sean expresiones regulares.
Wiktor Stribiżew

3
@ Andrew No se confunda con el doble ll en el ejemplo. Es bastante vago coincidirá con la subcadena más corta posible, mientras que codicioso coincidirá con el más largo posible. Greedy h.+lpartidos 'helol'en 'helolo'pero los perezosos h.+?lpartidos 'hel'.
v.shashenko

3
@FloatingRock: No. x?significa que xes opcional pero +?es una sintaxis diferente. Significa dejar de cuidar cuando encuentre algo que coincida: coincidencia perezosa.
slebetman

1
@FloatingRock: En cuanto a cómo diferenciar las diferentes sintaxis, simple: ?significa opcional y +?significa perezoso. Por \+?lo tanto, los medios +son opcionales.
slebetman

114
+-------------------+-----------------+------------------------------+
| Greedy quantifier | Lazy quantifier |        Description           |
+-------------------+-----------------+------------------------------+
| *                 | *?              | Star Quantifier: 0 or more   |
| +                 | +?              | Plus Quantifier: 1 or more   |
| ?                 | ??              | Optional Quantifier: 0 or 1  |
| {n}               | {n}?            | Quantifier: exactly n        |
| {n,}              | {n,}?           | Quantifier: n or more        |
| {n,m}             | {n,m}?          | Quantifier: between n and m  |
+-------------------+-----------------+------------------------------+

Agrega un ? a un cuantificador para que no sea greedy, es decir, vago.

Ejemplo:
cadena de prueba: stackoverflow
expresión cod codiciosa : s.*osalida: stackoverflo w
expresión reg perezosa : s.*?osalida: stacko verflow


2
no es ?? equivalente a ? . Del mismo modo, ¿no es {n}? equivalente a {n}
Número945

55
@BreakingBenjamin: no ?? no es equivalente a?, cuando tiene la opción de devolver 0 o 1, elegirá la alternativa 0 (perezosa). Para ver la diferencia, compare re.match('(f)?(.*)', 'food').groups()con re.match('(f)??(.*)', 'food').groups(). En el último, (f)??no coincidirá con la 'f' principal, aunque podría. Por lo tanto, la 'f' coincidirá con el segundo grupo de captura '. *'. Estoy seguro de que puedes construir un ejemplo con '{n}?' también. Es cierto que estos dos se usan muy raramente.
smci

55

Codicioso significa que su expresión coincidirá con el grupo más grande posible, perezoso significa que coincidirá con el grupo más pequeño posible. Para esta cadena:

abcdefghijklmc

y esta expresión:

a.*c

Una coincidencia codiciosa coincidirá con toda la cadena, y una coincidencia perezosa coincidirá solo con la primera abc.


16

Hasta donde yo sé, la mayoría de los motores de expresiones regulares son codiciosos por defecto. Agregar un signo de interrogación al final del cuantificador habilitará la coincidencia diferida.

Como @Andre S mencionó en el comentario.

  • Codicioso: siga buscando hasta que no se cumpla la condición.
  • Perezoso: deje de buscar una vez que se cumpla la condición.

Consulte el siguiente ejemplo para saber qué es codicioso y qué es perezoso.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test {
    public static void main(String args[]){
        String money = "100000000999";
        String greedyRegex = "100(0*)";
        Pattern pattern = Pattern.compile(greedyRegex);
        Matcher matcher = pattern.matcher(money);
        while(matcher.find()){
            System.out.println("I'm greeedy and I want " + matcher.group() + " dollars. This is the most I can get.");
        }

        String lazyRegex = "100(0*?)";
        pattern = Pattern.compile(lazyRegex);
        matcher = pattern.matcher(money);
        while(matcher.find()){
            System.out.println("I'm too lazy to get so much money, only " + matcher.group() + " dollars is enough for me");
        }
    }
}


El resultado es:

I'm greeedy and I want 100000000 dollars. This is the most I can get.

I'm too lazy to get so much money, only 100 dollars is enough for me

9

Tomado de www.regular-expressions.info

Codicia : los cuantificadores codiciosos primero intentan repetir la ficha tantas veces como sea posible, y gradualmente abandona las coincidencias a medida que el motor retrocede para encontrar una coincidencia general.

Pereza : el cuantificador perezoso primero repite el token tantas veces como sea necesario y expande gradualmente la coincidencia a medida que el motor retrocede a través de la expresión regular para encontrar una coincidencia general.


6

De expresión regular

Los cuantificadores estándar en las expresiones regulares son codiciosos, lo que significa que coinciden tanto como pueden, solo devuelven lo necesario para que coincida con el resto de la expresión regular.

Al usar un cuantificador diferido, la expresión intenta primero la coincidencia mínima.


4

Juego codicioso. El comportamiento predeterminado de las expresiones regulares es ser codicioso. Eso significa que intenta extraer lo más posible hasta que se ajuste a un patrón, incluso cuando una parte más pequeña hubiera sido sintácticamente suficiente.

Ejemplo:

import re
text = "<body>Regex Greedy Matching Example </body>"
re.findall('<.*>', text)
#> ['<body>Regex Greedy Matching Example </body>']

En lugar de coincidir hasta la primera aparición de '>', extrajo la cadena completa. Este es el comportamiento codicioso o "tómalo todo" predeterminado de regex.

La combinación perezosa , por otro lado, "toma lo menos posible". Esto puede realizarse agregando un? al final del patrón.

Ejemplo:

re.findall('<.*?>', text)
#> ['<body>', '</body>']

Si solo desea recuperar la primera coincidencia, utilice el método de búsqueda.

re.search('<.*?>', text).group()
#> '<body>'

Fuente: Python Regex Ejemplos


3

Codicioso significa que consumirá su patrón hasta que no quede ninguno y no pueda buscar más.

Lazy se detendrá tan pronto como encuentre el primer patrón que solicitó.

Un ejemplo común que a menudo encuentro es \s*-\s*?una expresión regular.([0-9]{2}\s*-\s*?[0-9]{7})

El primero \s*se clasifica como codicioso debido a *que buscará tantos espacios en blanco como sea posible después de que se encuentren los dígitos y luego busque un carácter de guión "-". Donde como el segundo \s*?es perezoso debido al presente, *?significa que se verá el primer carácter de espacio en blanco y se detendrá allí.


3

Se muestra mejor con el ejemplo. Cuerda. 192.168.1.1y una expresión regular codiciosa. \b.+\b Puede pensar que esto le daría el primer octeto, pero en realidad coincide con toda la cadena. ¿Por qué? Porque. + Es codicioso y una coincidencia codiciosa coincide con cada personaje 192.168.1.1hasta que llega al final de la cadena. Esta es la parte importante! Ahora comienza a retroceder un personaje a la vez hasta que encuentre una coincidencia para la tercera ficha ( \b).

Si la cadena de un archivo de texto de 4GB y 192.168.1.1 estaba al principio, podría ver fácilmente cómo este retroceso podría causar un problema.

Para hacer una expresión regular no codiciosa (perezosa) ponga un signo de interrogación después de su búsqueda codiciosa, por ejemplo

*?
??
+?

Lo que sucede ahora es que la ficha 2 ( +?) encuentra una coincidencia, la expresión regular se mueve a lo largo de un personaje y luego prueba la siguiente ficha ( \b) en lugar de la ficha 2 ( +?). Entonces se arrastra con cautela.


0

Los cuantificadores codiciosos son como el IRS / ATO: toman tanto como pueden:

Si está allí, vendrán y lo tomarán. Se lo llevarán todo:

Por ejemplo, el IRS coincide con esta expresión regular: .*

$50,000 - El IRS lo tomará todo. Esos codiciosos .*{4}?ERS

Vea aquí para ver un ejemplo: regexr.com/4t27f

Cuantificadores no codiciosos: toman tan poco como pueden

Por otro lado, si solicito un reembolso de impuestos, el IRS de repente se vuelve no codicioso y usan este cuantificador:

(.{2}?)([0-9]*)en contra de esta expresión: $50,000el primer grupo no es necesitado y solo coincide $5, por lo que obtengo un $5reembolso. El resto lo toma el tío Sam para gastarlo malgastamente.

Ver aquí: ejemplo no codicioso .

¿Por qué molestarse?

Se vuelve importante si está tratando de hacer coincidir ciertas partes de una expresión. A veces no quieres igualar todo.


-3

intenta comprender el siguiente comportamiento:

    var input = "0014.2";

Regex r1 = new Regex("\\d+.{0,1}\\d+");
Regex r2 = new Regex("\\d*.{0,1}\\d*");

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // "0014.2"

input = " 0014.2";

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // " 0014"

input = "  0014.2";

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // ""
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.