Wyrażenie regularne dla zduplikowanych słów

114

Jestem początkującym użytkownikiem wyrażeń regularnych i nie mogę się do końca dowiedzieć, jak napisać pojedyncze wyrażenie regularne, które „pasowałoby” do wszystkich zduplikowanych następujących po sobie słów, takich jak:

Paryż w tym na wiosnę.

Nie to jest powiązane.

Dlaczego się śmiejesz? Czy moje wyrażenia regularne są TAKIE złe?

Czy istnieje jedno wyrażenie regularne, które będzie pasować do WSZYSTKICH powyższych pogrubionych ciągów?

Joshua
źródło
4
@poly: To nie było „oskarżenie”, ale spokojne, normalne pytanie, na które doskonale można przyjąć „nie” jako odpowiedź. @Joshua: Tak, niektórzy ludzie (nie za mało) pozwalają tej stronie odrobić za nich pracę domową. Ale zadawanie pytań domowych nie jest złą rzeczą w SO, kiedy są oznaczone jako takie. Zwykle styl odpowiedzi zmienia się z „tutaj jest rozwiązanie” na „oto kilka rzeczy, o których nie pomyślałeś” i to dobrze. Ktoś musi starać się utrzymać to rozróżnienie, w jego przypadku byłem to ja, a gdzie indziej „inni ludzie” robią to samo. To wszystko.
Tomalak
13
Mam nadzieję, że nigdy nie zobaczę pytania typu „To brzmi trochę jak pytanie dotyczące miejsca pracy. Czy tak jest?” a potem ludzie będą się spierać, czy przepełnienie stosu wykonuje czyjąś pracę.
marcio
@Joshua +1 w odniesieniu do rozwiązania wyrażenia regularnego, które zaakceptowałeś, czy możesz mi powiedzieć, jak mogę zastąpić dopasowania (duplikaty) jednym elementem pary (np. not that that is related-> not that is related)? Z góry dziękuję
Antoine
@Joshua Myślę, że znalazłem rozwiązanie: powinienem wymienić na \1!
Antoine
2
@DavidLeal A może \b(\w+)\s+(\1\s*)+\b?
ytu

Odpowiedzi:

141

Wypróbuj to wyrażenie regularne:

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

Oto \bgranica słowa i \1odwołuje się do przechwyconego dopasowania z pierwszej grupy.

Gumbo
źródło
1
Zastanawia mnie; czy też można to zrobić \0? (Gdzie \0jest całe wyrażenie regularne, aż do bieżącego punktu LUB gdzie \0odnosi się do całego wyrażenia regularnego)
Pindatjuh
@Pindatjuh: Nie, nie sądzę, ponieważ ten mecz podrzędny również byłby częścią całego meczu.
Gumbo
Przynajmniej działa na silniku regex używanym w oknie dialogowym wyszukiwania / zamiany Eclipse.
Chaos_99
3
Tylko ostrzeżenie, nie dotyczy to słów z apostrofami lub (jak wspomina Noel) myślnikami. Rozwiązanie Mike'a działa lepiej w takich przypadkach
3
Co więcej, nie złapie trzech powtórzeń (lub więcej), nie wtedy, gdy jeden z duplikatów / trzech powtórzeń znajduje się na końcu ciągu
Nico
20

Uważam, że to wyrażenie regularne obsługuje więcej sytuacji:

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

Dobry wybór ciągów testowych można znaleźć tutaj: http://callumacrae.github.com/regex-tuesday/challenge1.html

Mike Viens
źródło
Świetnie, działa z apostrofami / łącznikami / itp. też - dzięki!
w przypadku linku wyzwanie1, co umieszczasz w obszarze zamiany, aby użyć zgrupowanego słowa? Próbowałem, <strong>\0</strong>ale nie działa.
uptownhr
2
Nie złapie trzech powtórzeń (lub więcej), nie wtedy, gdy jeden z duplikatów / trzech powtórzeń znajduje się na końcu struny
Nico
@uptownhr Chcesz użyć $1 <strong>$2</strong>. Ale użyj też innego wyrażenia regularnego /\b(\S+) (\1)\b/gi. Oto link: callumacrae.github.io/regex-tuesday/…
dsalaj
a jeśli chcę znaleźć wszystkie kolejne słowa z określonego tagu, na przykład <p class="bebe">bla bla</p>jak mogę zintegrować tę formułę wyrażenia regularnego?
Just Me
7

Spróbuj tego z poniższym RE

  • \ b początek granicy słowa
  • \ W + dowolny znak słowa
  • \ 1 to samo słowo już dopasowane
  • \ b koniec słowa
  • () * Powtarzam ponownie

    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
źródło
5

Powszechnie używana biblioteka PCRE poradzi sobie z takimi sytuacjami (nie osiągniesz tego samego z silnikami regex zgodnymi z POSIX):

(\b\w+\b)\W+\1
soulmerge
źródło
Potrzebujesz czegoś, co pasuje do znaków między dwoma słowami, na przykład \W+. \bnie zrobi tego, ponieważ nie zużywa żadnych postaci.
Alan Moore
Może to potencjalnie spowodować fałszywie dodatnie dopasowanie w przypadkach takich jak ... the these problems.... To rozwiązanie nie jest tak wiarygodne, jak ogólna struktura wzorca Gumbo, który dostatecznie realizuje granice słów.
mickmackusa
a jeśli chcę znaleźć wszystkie kolejne słowa z określonego tagu, na przykład <p class="bebe">bla bla</p>jak mogę zintegrować tę formułę wyrażenia regularnego?
Just Me
4

Oto wyrażenie regularne, którego używam do usuwania zduplikowanych fraz w moim bocie twitch:

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

(\S+\s*) szuka dowolnego ciągu znaków, który nie jest białą spacją, po którym następuje spacja.

\1{2,}następnie szuka więcej niż 2 wystąpień tej frazy w ciągu do dopasowania. Jeśli istnieją 3 identyczne frazy, pasuje.

Neceros
źródło
Ta odpowiedź jest myląca. Nie poluje na duplikaty, ale na podciągi z 3 lub więcej wystąpieniami. Nie jest też bardzo wytrzymały ze względu na \s*grupę przechwytywania. Zobacz prezentację: regex101.com/r/JtCdd6/1
mickmackusa
Ponadto skrajne przypadki (tekst o niskiej częstotliwości) powodowałyby fałszywie pozytywne dopasowania. Np I said "oioioi" that's some wicked mistressship!na oioioiisss
mickmackusa
4

Poniższe wyrażenie powinno działać poprawnie, aby znaleźć dowolną liczbę kolejnych słów. Dopasowanie może nie uwzględniać wielkości liter.

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

Przykładowe dane wejściowe: Goodbye goodbye GooDbYe

Przykładowe wyjście: Do widzenia

Wyjaśnienie:

Wyrażenie regularne:

\ b: Początek granicy słowa

\ w +: Dowolna liczba znaków słowa

(\ s + \ 1 \ b) *: Dowolna liczba spacji, po której następuje słowo, które pasuje do poprzedniego słowa i kończy granicę słowa. Całość opakowana * pomaga znaleźć więcej niż jedno powtórzenie.

Grupowanie:

m.group (0): Powinien zawierać dopasowaną grupę w powyższym przypadku Goodbye goodbye GooDbYe

m.group (1): Musi zawierać pierwsze słowo dopasowanego wzorca w powyższym przypadku Goodbye

Metoda Replace powoduje zastąpienie wszystkich kolejnych dopasowanych słów pierwszym wystąpieniem danego słowa.

Akriti
źródło
3

Nie. To jest nieregularna gramatyka. Mogą istnieć wyrażenia regularne specyficzne dla silnika / języka, których można użyć, ale nie ma uniwersalnego wyrażenia regularnego, które to umożliwia.

Ignacio Vazquez-Abrams
źródło
12
Chociaż jest to poprawne w ścisłym sensie, uważam, że nie ma już poważnego silnika regex, który nie obsługuje grupowania i odwołań wstecznych.
Tomalak
3

Oto taki, który wielokrotnie łapie wiele słów:

(\b\w+\b)(\s+\1)+
synaptikon
źródło
a jeśli chcę znaleźć wszystkie kolejne słowa z określonego tagu, na przykład <p class="bebe">bla bla</p>jak mogę zintegrować tę formułę wyrażenia regularnego?
Just Me
Uważam, że będzie to wymagało analizy HTML. Dla dowolnego tagu, który chcesz przeszukać, znajdź wszystkie wystąpienia tagów w kodzie HTML i uruchom to wyrażenie regularne jedno po drugim na każdym z nich. Lub jeśli nie dbasz o to, gdzie w
kodzie
Znajduję odpowiedź<p class="bebe">.*?\b\s+(\w+)\b\K\s+\1\s+\b(?=.*?<\/p>)
Just Me
3

Regex to Strip 2+ zduplikowane słowa (kolejne / nie kolejne słowa)

Wypróbuj to wyrażenie regularne, które może wychwycić 2 lub więcej zduplikowanych słów i pozostawić tylko jedno słowo. A zduplikowane słowa nie muszą nawet następować po sobie .

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

Tutaj \bjest używany do granicy słowa, ?=jest używany do pozytywnego wyprzedzania i \1jest używany do odwoływania się wstecz.

Przykładowe źródło

Niket Pathak
źródło
1
"the cat sat on the mat"" cat sat on the mat"
Nieskolejne
@Walf True. Niemniej jednak istnieją scenariusze, w których jest to zamierzone. (na przykład: podczas pobierania danych)
Niket Pathak
Dlaczego ponownie złamałeś swoje wyrażenie regularne po poprawieniu go ? Myślisz, że zmieniłem jego zamiary? Nawet przykład, który podałeś, nie zawiera błędu.
Walf
Tak, to był błąd, kopia wkleiła niewłaściwe rzeczy. Zamierzałem skopiować ten z mojego przykładu. tak czy owak, teraz działa! więc wszystko dobrze! Dzięki!
Niket Pathak,
2

Przykład w Javascript: The Good Parts można dostosować do tego:

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

\ b używa \ w dla granic słów, gdzie \ w jest równoważne z [0-9A-Z_a-z]. Jeśli nie masz nic przeciwko temu ograniczeniu, zaakceptowana odpowiedź jest w porządku.

Daniel
źródło
2

Ponieważ niektórzy programiści przychodzą na tę stronę w poszukiwaniu rozwiązania, które nie tylko eliminuje zduplikowane następujące po sobie podciągi niebędące białymi znakami, ale także potrójne powtórzenia i więcej, pokażę dostosowany wzorzec.

Wzorzec: /(\b\S+)(?:\s+\1\b)+/( Demonstracja wzorca )
Zastąp: $1(zastępuje dopasowanie pełnego ciągu grupą przechwytywania nr 1)

Ten wzorzec zachłannie dopasowuje „cały” podciąg niebędący białymi znakami, a następnie wymaga jednej lub więcej kopii dopasowanego podciągu, który może być oddzielony jednym lub większą liczbą białych znaków (spacja, tabulator, nowa linia itp.).

Konkretnie:

  • \b Znaki (granica słowa) są niezbędne, aby zapewnić, że częściowe słowa nie zostaną dopasowane.
  • Drugi nawias to grupa nieprzechwytywana, ponieważ ten podciąg o zmiennej szerokości nie musi być przechwytywany - tylko dopasowany / wchłonięty.
  • +(jeden lub więcej kwantyfikator) w grupie non-przechwytywania jest bardziej odpowiednie niż *ponieważ *będzie „przeszkadza” silnik regex do przechwytywania i zastąpić Singleton zdarzeń - jest marnotrawstwem wzornictwo.

* uwaga, jeśli masz do czynienia ze zdaniami lub ciągami wejściowymi z interpunkcją, wówczas wzorzec będzie wymagał dalszego dopracowania.

mickmackusa
źródło
@AdamJones użyj tego wzorca w swoim projekcie php. Odpowiedź Nico zawiera niepotrzebną składnię.
mickmackusa
1

To wyrażenie (zainspirowane przez Mike'a powyżej) wydaje się wychwytywać wszystkie duplikaty, potrójne powtórzenia itp., W tym te na końcu łańcucha, których większość innych nie robi:

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

Wiem, że pytanie zadane, aby dopasować tylko duplikaty , ale potrójne to tylko 2 duplikaty obok siebie :)

Po pierwsze, (^|\s+)upewniłem się, że zaczyna się od pełnego słowa, w przeciwnym razie „stek dziecięcy” trafiłby do „bułki dziecięcej” (litery „s” pasowałyby). Następnie dopasowuje wszystkie pełne słowa ( (\b\S+\b)), po których następuje koniec string ( $) lub liczba spacji ( \s+), całość powtórzona więcej niż raz.

Próbowałem tego w ten sposób i zadziałało dobrze:

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
źródło
Mam problem z przepisaniem tego do PHP, ważne jest, aby uzyskać jedną kopię dopasowanego duplikatu, zastępując każde wystąpienie duplikatów / trzech powtórzeń itp. Do tej pory mam: preg_replace ('/ (^ | \ s +) (\ S +) ( ($ | \ s +) \ 2) + / im ',' $ 0 ', $ string);
AdamJones
To najlepsza odpowiedź. Właśnie poprawiłem to, dodając \bna końcu w ten sposób: /(^|\s+)(\S+)(($|\s+)\2)+\b/g, "$1$2")To będzie działać w takich sytuacjach: the the string String string stringing the the along the the stringstanie się the string stringing the along the stringNotatka string stringing. Zostanie dopasowany do Twojej odpowiedzi. Dziękuję Ci.
Ste
-1

Użyj tego, jeśli chcesz, aby sprawdzanie duplikatów słów nie było uwzględniane.

(?i)\\b(\\w+)\\s+\\1\\b
Neelam
źródło
Używanie modyfikatora wzorca bez rozróżniania wielkości liter nie ma sensu dla twojego wzorca. Nie ma zakresów liter, które mogłyby wpłynąć na flagę.
mickmackusa
W rzeczywistości jest to duplikat zaakceptowanej odpowiedzi i nie dodaje żadnej wartości do strony. Zastanów się nad usunięciem tej odpowiedzi, aby zmniejszyć powiększenie strony.
mickmackusa