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?


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.


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


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

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

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.


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.


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.


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)+

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


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.


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.


@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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.