Regulärer Ausdruck für doppelte Wörter

114

Ich bin ein Neuling mit regulären Ausdrücken, und ich kann nicht genau herausfinden, wie man einen einzelnen regulären Ausdruck schreibt, der mit doppelten aufeinanderfolgenden Wörtern "übereinstimmt", wie z.

Paris in der der Frühling.

Nicht , dass das damit zusammenhängt.

Warum lachst du? Sind meine regulären Ausdrücke so schlecht?

Gibt es einen einzelnen regulären Ausdruck, der mit ALLEN fettgedruckten Zeichenfolgen oben übereinstimmt?

Joshua
quelle
4
@poly: Das war kein "Vorwurf", sondern eine ruhige, normale Frage, die perfekt ein "Nein" als Antwort nehmen kann. @ Joshua: Ja, einige Leute (nicht zu wenige) lassen diese Seite ihre Hausaufgaben für sie machen. Aber Hausaufgaben zu stellen ist bei SO keine schlechte Sache, wenn sie als solche gekennzeichnet sind. Normalerweise ändert sich der Stil der Antworten von "Hier ist die Lösung" zu "Hier sind einige Dinge, über die Sie nicht nachgedacht haben", und das ist gut so. Jemand muss versuchen, die Unterscheidung aufrechtzuerhalten, in seinem Fall war ich es, und anderswo tun "andere Leute" dasselbe. Das ist alles.
Tomalak
13
Ich hoffe, nie eine Frage wie "Das klingt ein bisschen wie eine Arbeitsplatzfrage. Ist es?" Zu sehen. und dann werden die Leute streiten, ob der Stapelüberlauf jemandes Arbeit erledigt.
Marcio
@Joshua +1 in Bezug auf die von Ihnen akzeptierte Regex-Lösung, können Sie mir bitte sagen, wie ich die Übereinstimmungen (Duplikate) durch ein Element des Paares ersetzen kann (z. B. not that that is related-> not that is related)? Vielen Dank im Voraus
Antoine
@ Joshua Ich denke, ich habe die Lösung gefunden: Ich sollte ersetzen durch \1!
Antoine
2
@ DavidLeal Wie wäre es \b(\w+)\s+(\1\s*)+\b?
ytu

Antworten:

140

Versuchen Sie diesen regulären Ausdruck:

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

Hier \bist eine Wortgrenze und \1verweist auf die erfasste Übereinstimmung der ersten Gruppe.

Gumbo
quelle
1
Verwundert mich; ist es auch möglich \0? (Wo \0ist die ganze Regex, bis zum aktuellen Punkt ODER wo \0bezieht sich auf die ganze Regex)
Pindatjuh
@Pindatjuh: Nein, das glaube ich nicht, denn dieses Teilspiel wäre auch Teil des gesamten Spiels.
Gumbo
Funktioniert zumindest mit der Regex-Engine, die im Eclipse-Dialogfeld zum Suchen / Ersetzen verwendet wird.
Chaos_99
3
Nur eine Warnung, dies behandelt keine Wörter mit Apostrophen oder (wie Noel erwähnt) Hypens. Mikes Lösung funktioniert in diesen Fällen besser
3
Darüber hinaus werden keine Triplikate (oder mehr) abgefangen, nicht wenn sich eines der Dup / Triplikate am Ende der Zeichenfolge befindet
Nico
20

Ich glaube, dieser reguläre Ausdruck behandelt mehr Situationen:

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

Eine gute Auswahl an Testzeichenfolgen finden Sie hier: http://callumacrae.github.com/regex-tuesday/challenge1.html

Mike Viens
quelle
Großartig, funktioniert mit Apostrophen / Bindestrichen / etc. auch - danke!
Was platzieren Sie für den Link Challenge1 im Ersetzungsbereich, um das gruppierte Wort zu verwenden? Versucht, <strong>\0</strong>aber nicht funktioniert.
Uptownhr
2
Es wird keine Triplikate (oder mehr) fangen, nicht wenn eines der Dup / Triplikate am Ende der Zeichenfolge ist
Nico
@uptownhr Sie möchten verwenden $1 <strong>$2</strong>. Verwenden Sie aber auch andere Regex /\b(\S+) (\1)\b/gi. Hier ist ein Link: callumacrae.github.io/regex-tuesday/…
dsalaj
und Wenn ich alle aufeinander folgenden Wörter eines bestimmten Tags finden möchte, <p class="bebe">bla bla</p>wie kann ich diese Regex-Formel integrieren?
Nur ich
7

Versuchen Sie dies mit unten RE

  • \ b Beginn der Wortwortgrenze
  • \ W + ein beliebiges Wortzeichen
  • \ 1 dasselbe Wort stimmte bereits überein
  • \ b Wortende
  • () * Wiederholen

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

Die weit verbreitete PCRE - Bibliothek kann solche Situationen bewältigen (Sie werden nicht erreichen , die das gleiche mit POSIX-kompatibelen regex Motoren, obwohl):

(\b\w+\b)\W+\1
Soulmerge
quelle
Sie benötigen etwas, um die Zeichen zwischen den beiden Wörtern abzugleichen, wie z \W+. \bwird es nicht tun, weil es keine Zeichen verbraucht.
Alan Moore
Dies führt möglicherweise zu falsch positiven Übereinstimmungen in Fällen wie ... the these problems.... Diese Lösung ist nicht so zuverlässig wie die allgemeine Struktur von Gumbos Muster, die Wortgrenzen ausreichend implementiert.
Mickmackusa
und Wenn ich alle aufeinander folgenden Wörter eines bestimmten Tags finden möchte, <p class="bebe">bla bla</p>wie kann ich diese Regex-Formel integrieren?
Nur ich
4

Dies ist der reguläre Ausdruck, mit dem ich doppelte Phrasen in meinem zuckenden Bot entferne:

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

(\S+\s*) sucht nach Zeichenfolgen, die keine Leerzeichen sind, gefolgt von Leerzeichen.

\1{2,}sucht dann nach mehr als 2 Instanzen dieser Phrase in der Zeichenfolge, die übereinstimmen. Wenn 3 Sätze identisch sind, stimmt sie überein.

Neceros
quelle
Diese Antwort ist irreführend. Es werden keine Duplikate gesucht, sondern Teilzeichenfolgen mit drei oder mehr Vorkommen. Es ist auch nicht sehr robust wegen der \s*in der Erfassungsgruppe. Siehe diese Demonstration: regex101.com/r/JtCdd6/1
mickmackusa
Darüber hinaus würden Extremfälle (niederfrequenter Text) zu falsch positiven Übereinstimmungen führen. ZB I said "oioioi" that's some wicked mistressship!am oioioiundsss
mickmackusa
3

Nein, das ist eine unregelmäßige Grammatik. Möglicherweise gibt es motor- / sprachspezifische reguläre Ausdrücke, die Sie verwenden können, aber es gibt keinen universellen regulären Ausdruck, der dies kann.

Ignacio Vazquez-Abrams
quelle
12
Obwohl dies im engeren Sinne korrekt ist, glaube ich, dass es keine ernsthafte Regex-Engine mehr gibt, die keine Gruppierung und Rückverweise unterstützt.
Tomalak
3

Hier ist eine, die mehrere Wörter mehrmals fängt:

(\b\w+\b)(\s+\1)+
Synaptikon
quelle
und Wenn ich alle aufeinander folgenden Wörter eines bestimmten Tags finden möchte, <p class="bebe">bla bla</p>wie kann ich diese Regex-Formel integrieren?
Nur ich
Ich glaube, das erfordert HTML-Analyse. Suchen Sie für jedes Tag, das Sie durchsuchen möchten, alle Tag-Vorkommen im HTML-Code und führen Sie diesen regulären Ausdruck nacheinander auf jedem Tag aus. Oder wenn Sie sich nicht darum kümmern, wo im HTML die Wiederholung auftritt, verketten Sie alle Tag-Textattribute und führen Sie den
regulären Ausdruck
Ich finde mir die Antwort<p class="bebe">.*?\b\s+(\w+)\b\K\s+\1\s+\b(?=.*?<\/p>)
Just Me
3

Regex to Strip 2+ doppelte Wörter (aufeinanderfolgende / nicht aufeinanderfolgende Wörter)

Versuchen Sie diesen regulären Ausdruck, der zwei oder mehr doppelte Wörter erfassen und nur ein einziges Wort zurücklassen kann. Und die doppelten Wörter müssen nicht einmal aufeinanderfolgend sein .

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

Hier \bwird für die Wortgrenze verwendet, ?=wird für einen positiven Lookahead verwendet und \1wird für die Rückreferenzierung verwendet.

Beispiel Quelle

Niket Pathak
quelle
1
Nicht aufeinanderfolgend ist eine schlechte Idee: "the cat sat on the mat"->" cat sat on the mat"
Walf
@Walf True. Dennoch gibt es Szenarien, in denen dies beabsichtigt ist. (zum Beispiel: beim Scraping von Daten)
Niket Pathak
Warum hast du deine Regex wieder gebrochen, nachdem ich sie korrigiert habe ? Hast du gedacht, ich hätte seine Absicht geändert? Selbst das von Ihnen verlinkte Beispiel hat keinen Fehler.
Walf
Ja, es war ein Fehler, Kopie hat das falsche Zeug eingefügt. Wollte das aus meinem Beispiel tatsächlich kopieren. sowieso funktioniert es jetzt! also alles gut! Vielen Dank!
Niket Pathak
3

Der folgende Ausdruck sollte korrekt funktionieren, um eine beliebige Anzahl aufeinanderfolgender Wörter zu finden. Bei der Zuordnung kann die Groß- und Kleinschreibung nicht berücksichtigt werden.

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

Beispieleingabe: Auf Wiedersehen Auf Wiedersehen GooDbYe

Beispielausgabe: Auf Wiedersehen

Erläuterung:

Der Regex-Ausdruck:

\ b: Beginn einer Wortgrenze

\ w +: Beliebig viele Wortzeichen

(\ s + \ 1 \ b) *: Beliebige Anzahl von Leerzeichen, gefolgt von Wörtern, die mit dem vorherigen Wort übereinstimmen und die Wortgrenze beenden. Das Ganze in * hilft, mehr als eine Wiederholung zu finden.

Gruppierung:

m.group (0): Enthält im obigen Fall die übereinstimmende Gruppe. Auf Wiedersehen, auf Wiedersehen, GooDbYe

m.group (1): Enthält im obigen Fall das erste Wort des übereinstimmenden Musters. Auf Wiedersehen

Die Ersetzungsmethode ersetzt alle aufeinanderfolgenden übereinstimmenden Wörter durch die erste Instanz des Wortes.

Akriti
quelle
2

Das Beispiel in Javascript: Die guten Teile können dazu angepasst werden:

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

\ b verwendet \ w für Wortgrenzen, wobei \ w [0-9A-Z_a-z] entspricht. Wenn Ihnen diese Einschränkung nichts ausmacht, ist die akzeptierte Antwort in Ordnung.

Daniel
quelle
2

Da einige Entwickler auf dieser Seite nach einer Lösung suchen, die nicht nur doppelte aufeinanderfolgende Nicht-Leerzeichen-Teilzeichenfolgen, sondern auch dreifache und darüber hinaus eliminiert, zeige ich das angepasste Muster.

Pattern: /(\b\S+)(?:\s+\1\b)+/( Pattern Demo )
Replace: $1(ersetzt das Fullstring Match durch Capture Group # 1)

Dieses Muster stimmt gierig mit einem "ganzen" Nicht-Leerzeichen-Teilstring überein und erfordert dann eine oder mehrere Kopien des übereinstimmenden Teilstrings, die durch ein oder mehrere Leerzeichen (Leerzeichen, Tabulator, Zeilenumbruch usw.) begrenzt sein können.

Speziell:

  • \b (Wortgrenzen-) Zeichen sind wichtig, um sicherzustellen, dass Teilwörter nicht übereinstimmen.
  • Die zweite Klammer ist eine nicht erfassende Gruppe, da dieser Teilstring mit variabler Breite nicht erfasst werden muss - nur angepasst / absorbiert.
  • Der +(ein oder mehrere Quantifizierer) in der nicht erfassenden Gruppe ist besser geeignet, als *weil er *die Regex-Engine "stört", um Singleton-Vorkommen zu erfassen und zu ersetzen - dies ist ein verschwenderisches Musterdesign.

* Beachten Sie, dass das Muster weiter verfeinert werden muss, wenn Sie mit Sätzen oder Eingabezeichenfolgen mit Interpunktion arbeiten.

mickmackusa
quelle
@AdamJones verwenden dieses Muster in Ihrem PHP-Projekt. Nicos Antwort enthält eine unnötige Syntax.
Mickmackusa
1

Dieser Ausdruck (inspiriert von Mike oben) scheint alle Duplikate, Triplikate usw. zu erfassen, einschließlich der am Ende der Zeichenfolge, was die meisten anderen nicht tun:

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

Ich kenne die Frage, die gestellt wird, um nur Duplikate abzugleichen, aber ein Dreifach besteht nur aus 2 Duplikaten nebeneinander :)

Zuerst stelle (^|\s+)ich sicher, dass es mit einem vollständigen Wort beginnt, sonst würde "Kindersteak" zu "Kindersteak" gehen (die "s" würden übereinstimmen). Dann stimmt es mit allen vollständigen Wörtern ( (\b\S+\b)) überein , gefolgt von einem Ende von string ( $) oder einer Anzahl von Leerzeichen ( \s+), wobei das Ganze mehr als einmal wiederholt wird.

Ich habe es so versucht und es hat gut funktioniert:

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
quelle
Ich habe Probleme beim Umschreiben in PHP. Es ist wichtig, dass ich eine einzelne Kopie des übereinstimmenden Duplikats erhalte, die jedes Auftreten von Duplikaten / Triplikaten usw. ersetzt. Bisher habe ich: preg_replace ('/ (^ | \ s +) (\ S +) ( ($ | \ s +) \ 2) + / im ',' $ 0 ', $ string);
AdamJones
Dies ist die beste Antwort. Ich habe gerade eine Optimierung vorgenommen, indem ich \bam Ende Folgendes hinzugefügt habe: /(^|\s+)(\S+)(($|\s+)\2)+\b/g, "$1$2")Dies funktioniert dann für Situationen wie diese: the the string String string stringing the the along the the stringwird zur the string stringing the along the stringNotiz string stringing. Es wird mit Ihrer Antwort abgeglichen. Danke dir.
Ste
-1

Verwenden Sie diese Option, wenn Sie nicht zwischen Groß- und Kleinschreibung suchen und nach doppelten Wörtern suchen möchten.

(?i)\\b(\\w+)\\s+\\1\\b
Neelam
quelle
Die Verwendung des Mustermodifikators ohne Berücksichtigung der Groß- und Kleinschreibung ist für Ihr Muster nicht sinnvoll. Es gibt keine Buchstabenbereiche, auf die sich die Flagge auswirken könnte.
Mickmackusa
Dies ist praktisch ein Duplikat der akzeptierten Antwort und fügt der Seite keinen Wert hinzu. Bitte entfernen Sie diese Antwort, um das Aufblähen der Seite zu verringern.
Mickmackusa