Expresión regular para palabras duplicadas

114

Soy un novato en expresiones regulares, y no puedo entender cómo escribir una sola expresión regular que "coincida" con cualquier palabra consecutiva duplicada, como:

París en la primavera.

No es que eso esté relacionado.

¿Por qué te ríes? ¿ Mis expresiones regulares son TAN malas?

¿Existe una sola expresión regular que coincida con TODAS las cadenas en negrita anteriores?

Joshua
fuente
4
@poly: Esa no fue una "acusación", sino una pregunta tranquila y normal que perfectamente puede tomar un "no" como respuesta. @Joshua: Sí, algunas personas (no muy pocas) dejan que este sitio haga los deberes por ellos. Pero hacer preguntas sobre la tarea no es algo malo en SO, cuando están etiquetadas como tales. Por lo general, el estilo de las respuestas cambia de "aquí está la solución" a "aquí hay algunas cosas en las que no ha pensado", y eso es bueno. Alguien tiene que intentar mantener la distinción, en su caso fui yo, y en otros lugares "otras personas" hacen lo mismo. Eso es todo.
Tomalak
13
Espero no ver nunca una pregunta como "Esto suena un poco como una pregunta sobre el lugar de trabajo. ¿Lo es?" y luego la gente discutirá si el desbordamiento de pila está haciendo el trabajo de alguien.
marcio
@Joshua +1 con respecto a la solución de expresiones regulares que aceptó, ¿podría decirme cómo podría reemplazar las coincidencias (duplicados) por un elemento del par (por ejemplo, not that that is related-> not that is related)? Gracias de antemano
Antoine
@Joshua Creo que encontré la solución: ¡debería reemplazar por \1!
Antoine
2
@DavidLeal ¿Qué tal \b(\w+)\s+(\1\s*)+\b?
ytu

Respuestas:

141

Prueba esta expresión regular:

\b(\w+)\s+\1\b

Aquí \bhay un límite de palabras y hace \1referencia a la coincidencia capturada del primer grupo.

Gumbo
fuente
1
Hace que me pregunte; ¿Es posible hacerlo \0también? (Dónde \0está toda la expresión regular, hasta el punto actual O dónde se \0refiere a toda la expresión regular)
Pindatjuh
@Pindatjuh: No, no lo creo porque ese sub-partido también sería parte de todo el partido.
Gumbo
Al menos funciona en el motor de expresiones regulares utilizado en el cuadro de diálogo de búsqueda / reemplazo de Eclipse.
Chaos_99
3
Solo una advertencia, esto no maneja palabras con apóstrofes o (como menciona Noel) guiones. La solución de Mike funciona mejor en estos casos
3
Además, no capturará triplicados (o más), no cuando uno de los duplicados / triplicados esté al final de la cadena
Nico
20

Creo que esta expresión regular maneja más situaciones:

/(\b\S+\b)\s+\b\1\b/

Puede encontrar una buena selección de cadenas de prueba aquí: http://callumacrae.github.com/regex-tuesday/challenge1.html

Mike Viens
fuente
Genial, funciona con apóstrofos / guiones / etc. también - ¡gracias!
para el enlace challenge1, ¿qué coloca en el área de reemplazo para usar la palabra agrupada? Intenté <strong>\0</strong>pero no funcionó.
uptownhr
2
No detectará triplicados (o más), no cuando uno de los duplicados / triplicados esté al final de la cadena
Nico
@uptownhr Quieres usar $1 <strong>$2</strong>. Pero también use diferentes expresiones regulares /\b(\S+) (\1)\b/gi. Aquí hay un enlace: callumacrae.github.io/regex-tuesday/…
dsalaj
y si quiero encontrar todas las palabras consecutivas de una etiqueta en particular, por ejemplo, <p class="bebe">bla bla</p>¿cómo puedo integrar esta fórmula de expresiones regulares?
Just Me
7

Prueba esto con RE a continuación

  • \ b inicio de palabra límite de palabra
  • \ W + cualquier carácter de palabra
  • \ 1 misma palabra ya coincide
  • \ b fin de palabra
  • () * Repitiendo de nuevo

    public static void main(String[] args) {
    
        String regex = "\\b(\\w+)(\\b\\W+\\b\\1\\b)*";//  "/* Write a RegEx matching repeated words here. */";
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE/* Insert the correct Pattern flag here.*/);
    
        Scanner in = new Scanner(System.in);
    
        int numSentences = Integer.parseInt(in.nextLine());
    
        while (numSentences-- > 0) {
            String input = in.nextLine();
    
            Matcher m = p.matcher(input);
    
            // Check for subsequences of input that match the compiled pattern
            while (m.find()) {
                input = input.replaceAll(m.group(0),m.group(1));
            }
    
            // Prints the modified sentence.
            System.out.println(input);
        }
    
        in.close();
    }
    
Faajir
fuente
5

La biblioteca PCRE ampliamente utilizada puede manejar tales situaciones (sin embargo, no logrará lo mismo con los motores de expresiones regulares compatibles con POSIX):

(\b\w+\b)\W+\1
fusión de almas
fuente
Necesita algo para hacer coincidir los caracteres entre las dos palabras, como \W+. \bno lo hará, porque no consume ningún carácter.
Alan Moore
Esto potencialmente resultará en una coincidencia de falsos positivos en casos como ... the these problems.... Esta solución no es tan confiable como la estructura general del patrón de Gumbo que implementa suficientemente los límites de las palabras.
mickmackusa
y si quiero encontrar todas las palabras consecutivas de una etiqueta en particular, por ejemplo, <p class="bebe">bla bla</p>¿cómo puedo integrar esta fórmula de expresiones regulares?
Just Me
4

Esta es la expresión regular que utilizo para eliminar frases duplicadas en mi bot de twitch:

(\S+\s*)\1{2,}

(\S+\s*) busca cualquier cadena de caracteres que no sea un espacio en blanco, seguido de un espacio en blanco.

\1{2,}luego busca más de 2 instancias de esa frase en la cadena para que coincida. Si hay 3 frases idénticas, coincide.

Neceros
fuente
Esta respuesta es engañosa. No busca duplicados, busca subcadenas con 3 o más ocurrencias. Tampoco es muy robusto debido a que está \s*en el grupo de captura. Vea esta demostración: regex101.com/r/JtCdd6/1
mickmackusa
Además, los casos extremos (texto de baja frecuencia) producirían coincidencias falsas positivas. Por ejemplo, I said "oioioi" that's some wicked mistressship!en oioioiysss
mickmackusa
4

La siguiente expresión debería funcionar correctamente para encontrar cualquier número de palabras consecutivas. La coincidencia puede ser insensible a mayúsculas y minúsculas.

String regex = "\\b(\\w+)(\\s+\\1\\b)*";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);

Matcher m = p.matcher(input);

// Check for subsequences of input that match the compiled pattern
while (m.find()) {
     input = input.replaceAll(m.group(0), m.group(1));
}

Entrada de muestra: Adiós, adiós GooDbYe

Salida de muestra: Adiós

Explicación:

La expresión regex:

\ b: Inicio de un límite de palabras

\ w +: cualquier número de caracteres de palabra

(\ s + \ 1 \ b) *: Cualquier número de espacio seguido de una palabra que coincida con la palabra anterior y finalice el límite de la palabra. Todo envuelto en * ayuda a encontrar más de una repetición.

Agrupación:

m.group (0): Debe contener el grupo correspondiente en el caso anterior Adiós, adiós GooDbYe

m.group (1): Debe contener la primera palabra del patrón coincidente en el caso anterior Adiós

El método de reemplazo reemplazará todas las palabras coincidentes consecutivas con la primera instancia de la palabra.

Akriti
fuente
3

No. Esa es una gramática irregular. Puede haber expresiones regulares específicas del lenguaje / motor que puede usar, pero no existe una expresión regular universal que pueda hacer eso.

Ignacio Vázquez-Abrams
fuente
12
Aunque soy correcto en un sentido estricto, creo que ya no hay un motor de expresiones regulares en uso serio que no admita agrupaciones y referencias inversas.
Tomalak
3

Aquí hay uno que captura varias palabras varias veces:

(\b\w+\b)(\s+\1)+
Synaptikon
fuente
y si quiero encontrar todas las palabras consecutivas de una etiqueta en particular, por ejemplo, <p class="bebe">bla bla</p>¿cómo puedo integrar esta fórmula de expresiones regulares?
Just Me
Creo que requerirá el análisis de HTML. Para cualquier etiqueta determinada que desee buscar, busque todas las ocurrencias de etiquetas dentro del HTML y ejecute esta expresión regular una por una en cada una. O si no le importa en qué parte del HTML se produce la repetición, concatenar todos los atributos de texto de la etiqueta y ejecutar la expresión regular en la cadena concatenada
synaptikon
Encuentro la respuesta<p class="bebe">.*?\b\s+(\w+)\b\K\s+\1\s+\b(?=.*?<\/p>)
solo yo
3

Regex to Strip 2+ palabras duplicadas (palabras consecutivas / no consecutivas)

Pruebe esta expresión regular que puede capturar 2 o más palabras duplicadas y solo dejar una sola palabra. Y las palabras duplicadas ni siquiera necesitan ser consecutivas .

/\b(\w+)\b(?=.*?\b\1\b)/ig

Aquí, \bse usa para Word Boundary, ?=se usa para una búsqueda anticipada positiva y \1se usa para referencias hacia atrás.

Fuente de ejemplo

Niket Pathak
fuente
1
No consecutivos es una mala idea: "the cat sat on the mat"->" cat sat on the mat"
Walf
@Walf True. Sin embargo, hay escenarios en los que esto se pretende. (por ejemplo: mientras se
extraen
¿Por qué rompiste tu expresión regular nuevamente después de que la corrigí ? ¿Pensaste que había cambiado su intención? Incluso el ejemplo que vinculó no tiene el error.
Walf
Sí, fue un error, la copia pegó el material equivocado. Tenía la intención de copiar el de mi ejemplo en realidad. de todos modos, ¡ahora funciona! tan bien! ¡Gracias!
Niket Pathak
2

El ejemplo en Javascript: The Good Parts se puede adaptar para hacer esto:

var doubled_words = /([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD]+)\s+\1(?:\s|$)/gi;

\ b usa \ w para los límites de las palabras, donde \ w es equivalente a [0-9A-Z_a-z]. Si no le importa esa limitación, la respuesta aceptada está bien.

Daniel
fuente
2

Dado que algunos desarrolladores están llegando a esta página en busca de una solución que no solo elimine las subcadenas consecutivas duplicadas que no sean espacios en blanco, sino también las triplicadas y más, mostraré el patrón adaptado.

Patrón: /(\b\S+)(?:\s+\1\b)+/( Demostración de patrón )
Reemplazar: $1(reemplaza la coincidencia de cadena completa con el grupo de captura n. ° 1)

Este patrón coincide codiciosamente con una subcadena "completa" que no es un espacio en blanco, luego requiere una o más copias de la subcadena coincidente que puede estar delimitada por uno o más caracteres de espacio en blanco (espacio, tabulación, nueva línea, etc.).

Específicamente:

  • \b Los caracteres (límite de palabras) son vitales para garantizar que las palabras parciales no coincidan.
  • El segundo paréntesis es un grupo que no captura, porque esta subcadena de ancho variable no necesita ser capturada, solo emparejada / absorbida.
  • el +(uno o más cuantificadores) en el grupo de no captura es más apropiado *porque *"molestará" al motor de expresiones regulares para capturar y reemplazar ocurrencias de singleton - este es un diseño de patrón derrochador.

* tenga en cuenta que si se trata de frases o cadenas de entrada con puntuación, el patrón deberá perfeccionarse aún más.

mickmackusa
fuente
@AdamJones usa este patrón en su proyecto php. La respuesta de Nico tiene una sintaxis innecesaria.
mickmackusa
1

Esta expresión (inspirada en Mike, arriba) parece capturar todos los duplicados, triplicados, etc., incluidos los que están al final de la cadena, lo que la mayoría de los demás no hacen:

/(^|\s+)(\S+)(($|\s+)\2)+/g, "$1$2")

Sé que la pregunta se hace para hacer coincidir solo los duplicados , pero un triplicado son solo 2 duplicados uno al lado del otro :)

Primero, me (^|\s+)aseguro de que comience con una palabra completa, de lo contrario "filete de niño" iría a "filete de niño" (las "s" coincidirían). Luego, coincide con todas las palabras completas ( (\b\S+\b)), seguidas de un final de cadena ( $) o varios espacios ( \s+), el conjunto se repite más de una vez.

Lo probé así y funcionó bien:

var s = "here here here     here is ahi-ahi ahi-ahi ahi-ahi joe's joe's joe's joe's joe's the result result     result";
print( s.replace( /(\b\S+\b)(($|\s+)\1)+/g, "$1"))         
--> here is ahi-ahi joe's the result
Nico
fuente
Tengo problemas para reescribir esto en PHP, es vital que obtenga una única copia del duplicado coincidente reemplazando cada aparición de duplicados / triplicados, etc. Hasta ahora tengo: preg_replace ('/ (^ | \ s +) (\ S +) ( ($ | \ s +) \ 2) + / im ',' $ 0 ', $ cadena);
AdamJones
Esta es la mejor respuesta. Solo hice un ajuste a eso agregando \bal final así: /(^|\s+)(\S+)(($|\s+)\2)+\b/g, "$1$2")Esto funcionará para situaciones como esta: the the string String string stringing the the along the the stringse convertirá en the string stringing the along the stringAviso string stringing. Se empareja con tu respuesta. Gracias.
Ste
-1

Use esto en caso de que desee una verificación que no distinga entre mayúsculas y minúsculas para detectar palabras duplicadas.

(?i)\\b(\\w+)\\s+\\1\\b
Neelam
fuente
Usar el modificador de patrón que no distingue entre mayúsculas y minúsculas no sirve de nada para su patrón. No hay rangos de letras para que la bandera impacte.
mickmackusa
Esto es efectivamente un duplicado de la respuesta aceptada y no agrega valor a la página. Considere eliminar esta respuesta para reducir la hinchazón de la página.
mickmackusa