Expression régulière pour les mots en double


114

Je suis un débutant en expression régulière, et je n'arrive pas à comprendre comment écrire une seule expression régulière qui "correspondrait" à des mots consécutifs en double tels que:

Paris dans le le printemps.

Non pas que cela soit lié.

Pourquoi ris-tu? Sont mes mes expressions régulières mauvais ??

Existe-t-il une seule expression régulière qui correspond à TOUTES les chaînes en gras ci-dessus?


4
@poly: Ce n'était pas une "accusation", mais une question calme et normale qui peut parfaitement prendre un "non" comme réponse. @Joshua: Oui, certaines personnes (pas trop peu nombreuses) laissent ce site faire leurs devoirs à leur place. Mais poser des questions sur les devoirs n'est pas une mauvaise chose à faire sur SO, lorsqu'ils sont étiquetés comme tels. Habituellement, le style des réponses passe de «voici la solution» à «voici certaines choses auxquelles vous n'avez pas pensé», et c'est une bonne chose. Quelqu'un doit essayer de maintenir la distinction, dans son cas, c'était moi, et ailleurs "d'autres personnes" font la même chose. C'est tout.
Tomalak

13
J'espère ne jamais voir une question comme "Cela ressemble un peu à une question sur le lieu de travail. puis les gens se demanderont si le débordement de pile fait le travail de quelqu'un.
marcio

@Joshua +1 en ce qui concerne la solution regex que vous avez acceptée, pouvez-vous me dire comment pourrais-je remplacer les correspondances (doublons) par un élément de la paire (par exemple, not that that is related-> not that is related)? Merci d'avance
Antoine

@Joshua Je pense avoir trouvé la solution: je devrais remplacer par \1!
Antoine

2
@DavidLeal Et pourquoi pas \b(\w+)\s+(\1\s*)+\b?
ytu

Réponses:


141

Essayez cette expression régulière:

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

Voici \bune limite de mot et fait \1référence à la correspondance capturée du premier groupe.


1
Me fait me demander; est-il possible de faire \0aussi? (Où \0est l'expression régulière entière, jusqu'au point actuel OU où se \0réfère à l'expression
régulière

@Pindatjuh: Non, je ne pense pas parce que ce sous-match ferait également partie de tout le match.
Gumbo

Au moins fonctionne sur le moteur regex utilisé dans la boîte de dialogue de recherche / remplacement d'Eclipse.
Chaos_99

3
Juste un avertissement, cela ne gère pas les mots avec des apostrophes ou (comme Noel le mentionne) des hypens. La solution de Mike fonctionne mieux dans ces cas

3
De plus, il n'attrapera pas les triplicats (ou plus), pas quand l'un des dup / triplicata est à la fin de la chaîne
Nico

20

Je crois que cette regex gère plus de situations:

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

Une bonne sélection de chaînes de test peut être trouvée ici: http://callumacrae.github.com/regex-tuesday/challenge1.html


Excellent, fonctionne avec les apostrophes / traits d'union / etc. aussi - merci!

pour le lien challenge1, que placez-vous dans la zone de remplacement pour utiliser le mot groupé? Essayé <strong>\0</strong>mais ne fonctionne pas.
jusqu'au

2
Il n'attrapera pas les triplicats (ou plus), pas quand l'un des doublons / triplicats est à la fin de la chaîne
Nico

@uptownhr Vous souhaitez utiliser $1 <strong>$2</strong>. Mais utilisez également des expressions régulières différentes /\b(\S+) (\1)\b/gi. Voici un lien: callumacrae.github.io/regex-tuesday/…
dsalaj

et si je veux trouver tous les mots consécutifs d'une balise particulière, par exemple, <p class="bebe">bla bla</p>comment puis-je intégrer cette formule regex?
Just Me

7

Essayez ceci avec ci-dessous RE

  • \ b début du mot limite du mot
  • \ W + n'importe quel caractère de mot
  • \ 1 même mot correspondant déjà
  • \ b fin de mot
  • () * Répéter à nouveau

    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();
    }
    

5

La bibliothèque PCRE largement utilisée peut gérer de telles situations (vous n'obtiendrez pas la même chose avec les moteurs de regex compatibles POSIX, cependant):

(\b\w+\b)\W+\1

Vous avez besoin de quelque chose pour faire correspondre les caractères entre les deux mots, comme \W+. \bne le fera pas, car il ne consomme aucun caractère.
Alan Moore

Cela entraînera potentiellement une correspondance faussement positive dans des cas comme ... the these problems.... Cette solution n'est pas aussi fiable que la structure générale du modèle de Gumbo qui implémente suffisamment les limites des mots.
mickmackusa

et si je veux trouver tous les mots consécutifs d'une balise particulière, par exemple, <p class="bebe">bla bla</p>comment puis-je intégrer cette formule regex?
Just Me

4

Voici le regex que j'utilise pour supprimer les phrases en double dans mon bot twitch:

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

(\S+\s*) recherche toute chaîne de caractères qui n'est pas un espace, suivie d'un espace.

\1{2,}recherche ensuite plus de 2 instances de cette phrase dans la chaîne pour correspondre. S'il y a 3 phrases identiques, cela correspond.


Cette réponse est trompeuse. Il ne recherche pas les doublons, il chasse les sous-chaînes avec 3 occurrences ou plus. Il n'est pas non plus très robuste à cause du \s*groupe de capture. Voir cette démonstration: regex101.com/r/JtCdd6/1
mickmackusa

De plus, les cas extrêmes (texte à basse fréquence) produiraient des correspondances faussement positives. Par exemple I said "oioioi" that's some wicked mistressship!, oioioietsss
mickmackusa

4

L'expression ci-dessous devrait fonctionner correctement pour trouver n'importe quel nombre de mots consécutifs. La correspondance peut être insensible à la casse.

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));
}

Exemple d'entrée: au revoir au revoir GooDbYe

Exemple de sortie: au revoir

Explication:

L'expression regex:

\ b: Début d'une limite de mot

\ w +: N'importe quel nombre de caractères de mot

(\ s + \ 1 \ b) *: N'importe quel nombre d'espaces suivi d'un mot qui correspond au mot précédent et termine la limite du mot. Une chose entière enveloppée dans * aide à trouver plus d'une répétition.

Regroupement:

m.group (0): Contiendra le groupe correspondant dans le cas ci-dessus Au revoir au revoir GooDbYe

m.group (1): Contiendra le premier mot du motif correspondant dans le cas ci-dessus Au revoir

La méthode Replace remplacera tous les mots concordants consécutifs par la première instance du mot.


3

Non. C'est une grammaire irrégulière. Il peut y avoir des expressions régulières spécifiques au moteur / au langage que vous pouvez utiliser, mais aucune expression régulière universelle ne peut le faire.


12
Bien qu'étant correct au sens strict, je crois qu'il n'y a plus de moteur d'expression régulière utilisé sérieusement qui ne prend pas en charge le regroupement et les références arrière.
Tomalak

3

En voici un qui capture plusieurs mots plusieurs fois:

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

et si je veux trouver tous les mots consécutifs d'une balise particulière, par exemple, <p class="bebe">bla bla</p>comment puis-je intégrer cette formule regex?
Just Me

Je pense que cela nécessitera une analyse HTML. Pour toute balise donnée que vous souhaitez rechercher, recherchez toutes les occurrences de balises dans le HTML et exécutez cette expression régulière une par une sur chacune. Ou si vous ne vous souciez pas de l'endroit dans le HTML où la répétition se produit, concaténez tous les attributs de texte de balise et exécutez l'expression
régulière

Je me trouve la réponse<p class="bebe">.*?\b\s+(\w+)\b\K\s+\1\s+\b(?=.*?<\/p>)
Just Me

3

Regex pour supprimer plus de 2 mots en double (mots consécutifs / non consécutifs)

Essayez cette expression régulière qui peut attraper 2 mots ou plus en double et ne laisser qu'un seul mot. Et les mots en double n'ont même pas besoin d'être consécutifs .

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

Ici, \best utilisé pour Word Boundary, ?=est utilisé pour une anticipation positive et \1est utilisé pour le back-référencement.

Exemple de source


1
Non-consécutif est une mauvaise idée: "the cat sat on the mat"->" cat sat on the mat"
Walf

@Walf True. Néanmoins, il existe des scénarios où cela est prévu. (par exemple: en grattant des données)
Niket Pathak

Pourquoi avez-vous encore cassé votre regex après que je l'ai corrigé ? Pensiez-vous que j'avais changé son intention? Même l'exemple que vous avez lié n'a pas l'erreur.
Walf

Oui, c'était une erreur, copier-coller le mauvais truc. Destiné à copier celui de mon exemple en fait. de toute façon, ça marche maintenant! tout va bien! Merci!
Niket Pathak

2

L'exemple en Javascript: The Good Parts peut être adapté pour cela:

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

\ b utilise \ w pour les limites des mots, où \ w équivaut à [0-9A-Z_a-z]. Si cela ne vous dérange pas, la réponse acceptée est bonne.


2

Étant donné que certains développeurs viennent sur cette page à la recherche d'une solution qui élimine non seulement les sous-chaînes consécutives en double sans espace, mais également les triples et au-delà, je vais montrer le modèle adapté.

Pattern: /(\b\S+)(?:\s+\1\b)+/( Pattern Demo )
Replace: $1(remplace la correspondance de chaîne complète par le groupe de capture n ° 1)

Ce modèle correspond avec gourmandise à une sous-chaîne "entière" sans espace, puis nécessite une ou plusieurs copies de la sous-chaîne correspondante qui peut être délimitée par un ou plusieurs caractères d'espacement (espace, tabulation, nouvelle ligne, etc.).

Plus précisément:

  • \b Les caractères (limite du mot) sont essentiels pour garantir que les mots partiels ne correspondent pas.
  • La deuxième parenthèse est un groupe non capturant, car cette sous-chaîne de largeur variable n'a pas besoin d'être capturée - seulement mise en correspondance / absorbée.
  • le +(un ou plusieurs quantificateurs) sur le groupe non capturant est plus approprié que *parce *qu'il "dérangera" le moteur d'expression régulière pour capturer et remplacer les occurrences singleton - c'est une conception de modèle inutile.

* notez que si vous avez affaire à des phrases ou à des chaînes d'entrée avec ponctuation, le modèle devra être affiné davantage.


@AdamJones utilise ce modèle dans votre projet php. La réponse de Nico contient une syntaxe inutile.
mickmackusa

1

Cette expression (inspirée de Mike, ci-dessus) semble attraper tous les doublons, triples, etc., y compris ceux à la fin de la chaîne, ce que la plupart des autres ne font pas:

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

Je connais la question posée pour faire correspondre uniquement les doublons , mais un triple n'est que 2 doublons l'un à côté de l'autre :)

D'abord, je mets (^|\s+)pour m'assurer qu'il commence par un mot complet, sinon «steak d'enfant» irait à «steak d'enfant» (les «s» correspondraient). Ensuite, il correspond à tous les mots complets ( (\b\S+\b)), suivis d'une fin de chaîne ( $) ou d'un certain nombre d'espaces ( \s+), le tout répété plusieurs fois.

Je l'ai essayé comme ça et ça a bien fonctionné:

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

J'ai du mal à réécrire ceci en PHP, il est vital que j'obtienne une seule copie du duplicata correspondant remplaçant chaque occurrence de doublons / triplicats etc. Jusqu'à présent, j'ai: preg_replace ('/ (^ | \ s +) (\ S +) ( ($ | \ s +) \ 2) + / im ',' $ 0 ', $ chaîne);
AdamJones

C'est la meilleure réponse. J'ai juste fait un ajustement à cela en ajoutant \bà la fin comme ceci: /(^|\s+)(\S+)(($|\s+)\2)+\b/g, "$1$2")Cela fonctionnera alors pour des situations comme celle-ci: the the string String string stringing the the along the the stringdeviendra the string stringing the along the stringAvis string stringing. Cela correspond à votre réponse. Je vous remercie.
Ste

-1

Utilisez cette option au cas où vous souhaiteriez une vérification insensible à la casse pour les mots en double.

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

L'utilisation du modificateur de motif insensible à la casse ne sert à rien pour votre motif. Il n'y a pas de plages de lettres pour que le drapeau ait un impact.
mickmackusa

Il s'agit en fait d'un double de la réponse acceptée et n'ajoute aucune valeur à la page. Veuillez envisager de supprimer cette réponse pour réduire le gonflement des pages.
mickmackusa
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.