Yinelenen Kelimeler İçin Normal İfade


114

Ben bir normal ifade acemiyim ve aşağıdaki gibi birbirini takip eden yinelenen kelimelerle "eşleşecek" tek bir normal ifadeyi nasıl yazacağımı tam olarak anlayamıyorum:

Paris bahar.

Değil o ilgilidir.

Neden gülüyorsun? Are benim benim normal ifadeler BU kötü ??

Yukarıdaki kalın dizelerin TÜMÜ ile eşleşen tek bir normal ifade var mı?


4
@poly: Bu "suçlama" değil, cevap olarak "hayır" ı mükemmel bir şekilde alabilen sakin, normal bir soruydu. @Joshua: Evet, bazı insanlar (çok az değil) bu sitenin kendileri için ödevlerini yapmasına izin veriyor. Ancak ev ödevi soruları sormak, bu şekilde etiketlendiklerinde SO'da yapılacak kötü bir şey değildir. Genellikle yanıtların tarzı "çözüm burada" dan "işte düşünmediğiniz bazı şeyler" e değişir ve bu iyi bir şeydir. Birinin denemesi ve ayrımı sürdürmesi gerekiyor, onun durumunda o bendim ve başka yerlerde "diğer insanlar" aynı şeyi yapıyor. Bu kadar.
Tomalak

13
"Bu biraz iş yeri sorusuna benziyor. Öyle mi?" Gibi bir soru görmemeyi umuyoruz. ve sonra insanlar stack overflow'un birinin işini yapıp yapmadığını tartışacaklar.
marcio

@Joshua +1, kabul ettiğiniz normal ifade çözümüyle ilgili olarak, eşleşmeleri (kopyaları) çiftin bir öğesiyle (örn. not that that is related-> not that is related) nasıl değiştirebileceğimi söyler misiniz ? Şimdiden teşekkürler
Antoine

@Joshua Sanırım çözümü buldum: ile değiştirmeliyim \1!
Antoine

2
@DavidLeal Nasıl \b(\w+)\s+(\1\s*)+\b?
ytu

Yanıtlar:


141

Bu normal ifadeyi deneyin:

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

İşte \bbir kelime sınırı ve \1ilk grubun yakalanan eşleşmesine atıfta bulunuyor.


1
Merak etmeme neden oluyor; bunu yapmak \0da mümkün mü? ( \0Geçerli noktaya kadar tüm normal \0ifade nerede VEYA tüm normal
ifadeyi

@Pindatjuh: Hayır, öyle düşünmüyorum çünkü bu alt maç da tüm maçın bir parçası olacak.
Gumbo

En azından Eclipse arama / değiştirme iletişim kutusunda kullanılan regex motoru üzerinde çalışır.
Chaos_99

3
Sadece bir uyarı, bu, kesme işaretli veya (Noel'in bahsettiği gibi) hipen içeren kelimeleri işlemez. Mike'ın çözümü bu durumlarda daha iyi çalışıyor

3
Dahası, kopya / üçlülerden biri dizenin sonunda olduğunda değil, üçlüleri (veya daha fazlasını) yakalayamaz
Nico

20

Bu normal ifadenin daha fazla durumu ele aldığına inanıyorum:

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

İyi bir test dizesi seçimi burada bulunabilir: http://callumacrae.github.com/regex-tuesday/challenge1.html


Harika, kesme işaretleri / kısa çizgiler / vb. İle çalışır. çok - teşekkürler!

meydan okuma1 bağlantısı için, gruplanmış kelimeyi kullanmak için değiştirme alanına ne yerleştirirsiniz? Denedim <strong>\0</strong>ama çalışmıyor.
uptownhr

2
Yinelenen / üçlülerden biri dizenin sonunda olduğunda değil, üçlüleri (veya daha fazlasını) yakalayamaz
Nico

@uptownhr Kullanmak istiyorsunuz $1 <strong>$2</strong>. Ancak farklı normal ifadeler de kullanın /\b(\S+) (\1)\b/gi. İşte bir bağlantı: callumacrae.github.io/regex-tuesday/…
dsalaj

ve Belirli bir etiketten ardışık tüm kelimeleri bulmak istersem, örneğin <p class="bebe">bla bla</p>bu normal ifade formülünü nasıl entegre edebilirim?
Just Me

7

Bunu aşağıdaki RE ile deneyin

  • \ b kelime başı kelime sınırı
  • \ W + herhangi bir kelime karakteri
  • \ 1 aynı kelime zaten eşleşti
  • \ b kelimenin sonu
  • () * Tekrar tekrar

    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

Yaygın olarak kullanılan PCRE kitaplığı bu tür durumların üstesinden gelebilir ( yine de POSIX uyumlu regex motorlarıyla aynı şeyi elde edemezsiniz ):

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

İki kelime arasındaki karakterlerle eşleşecek bir şeye ihtiyacın var \W+. \byapmaz, çünkü herhangi bir karakter tüketmez.
Alan Moore

Bu, gibi durumlarda potansiyel olarak yanlış pozitif eşleşmeye neden olacaktır ... the these problems.... Bu çözüm, kelime sınırlarını yeterince uygulayan Gumbo modelinin genel yapısı kadar güvenilir değildir.
mickmackusa

ve Belirli bir etiketten ardışık tüm kelimeleri bulmak istersem, örneğin <p class="bebe">bla bla</p>bu normal ifade formülünü nasıl entegre edebilirim?
Just Me

4

Bu, twitch botumdaki yinelenen cümleleri kaldırmak için kullandığım normal ifade:

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

(\S+\s*) boşluk olmayan herhangi bir karakter dizisini arar, ardından boşluk bırakır.

\1{2,}daha sonra dizede eşleştirmek için bu kelime öbeğinin 2'den fazla örneğini arar. Birbirinin aynısı olan 3 cümle varsa, eşleşir.


Bu cevap yanıltıcıdır. Yinelenenleri avlamaz, 3 veya daha fazla oluşumla alt dizeleri avlar. Ayrıca, \s*yakalama grubundaki nedeniyle çok sağlam değil . Bu gösteri bakın regex101.com/r/JtCdd6/1
mickmackusa

Ayrıca aşırı durumlar (düşük frekanslı metin) yanlış pozitif eşleşmeler üretebilir. Örneğin I said "oioioi" that's some wicked mistressship!üzerinde oioioivesss
mickmackusa

4

Aşağıdaki ifade, herhangi bir sayıda ardışık sözcük bulmak için doğru şekilde çalışmalıdır. Eşleştirme, büyük / küçük harfe duyarlı olmayabilir.

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

Örnek Giriş: Hoşçakal GooDbYe

Örnek Çıktı: Hoşçakal

Açıklama:

Normal ifade ifadesi:

\ b: Bir sözcük sınırının başlangıcı

\ w +: Herhangi bir sayıda kelime karakteri

(\ s + \ 1 \ b) *: Önceki kelimeyle eşleşen ve kelime sınırını sona erdiren kelimenin izlediği herhangi bir sayıda boşluk. * İçine sarılmış olan her şey, birden fazla tekrar bulmaya yardımcı olur.

Gruplama:

m.group (0): Yukarıdaki durumda elveda GooDbYe'deki eşleşen grubu içerecek

m.grup (1): Yukarıdaki Hoşçakal durumunda eşleşen kalıbın ilk kelimesini içerecektir

Değiştirme yöntemi, birbirini izleyen tüm eşleşen kelimeleri, kelimenin ilk geçtiği yerle değiştirecektir.


3

Hayır. Bu düzensiz bir gramerdir. Kullanabileceğiniz motora / dile özgü düzenli ifadeler olabilir, ancak bunu yapabilen evrensel bir normal ifade yoktur.


12
Kesin bir anlamda doğru olsa da, artık ciddi kullanımda olan, gruplamayı ve geriye dönük referansları desteklemeyen bir regex motoru olmadığına inanıyorum.
Tomalak

3

İşte birden çok kelimeyi birden çok kez yakalayan:

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

ve Belirli bir etiketten ardışık tüm kelimeleri bulmak istersem, örneğin <p class="bebe">bla bla</p>bu normal ifade formülünü nasıl entegre edebilirim?
Just Me

Bunun HTML ayrıştırması gerektireceğine inanıyorum. Aramak istediğiniz herhangi bir belirli etiket için, HTML içindeki tüm etiket oluşumlarını bulun ve bu normal ifadeyi her birinde birer birer çalıştırın. Veya tekrarın HTML'de nerede meydana geldiğini umursamıyorsanız, tüm etiket metni özniteliklerini birleştirin ve birleştirilmiş dizede normal
ifadeyi

Kendime cevabı buluyorum<p class="bebe">.*?\b\s+(\w+)\b\K\s+\1\s+\b(?=.*?<\/p>)
Just Me

3

Normalden Sıyırmak 2+ yinelenen sözcük (ardışık / ardışık olmayan sözcükler)

Yinelenen 2 veya daha fazla kelimeyi yakalayabilen ve arkasında yalnızca tek bir kelime bırakan bu normal ifadeyi deneyin. Ve yinelenen kelimelerin ardışık olmasına bile gerek yoktur .

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

Burada, \bKelime Sınırı ?=için kullanılır, pozitif bakış \1için kullanılır ve geriye referans için kullanılır.

Örnek Kaynak


1
Ardışık "the cat sat on the mat"" cat sat on the mat"
olmamak

@Walf True. Yine de, bunun amaçlandığı senaryolar vardır. (örneğin: veri
toplarken

Niçin tekrar regex kırmak sonra bunu düzeltilmiş ? Amacını değiştirdiğimi mi düşündün? Bağladığınız örnekte bile hata yok.
Walf

Evet, bir hataydı, kopya yanlış şeyleri yapıştırdı. Aslında benim örneğimden olanı kopyalamak niyetindeydim. neyse, şimdi çalışıyor! çok iyi! Teşekkürler!
Niket Pathak

2

Javascript'teki örnek: The Good Parts, bunu yapacak şekilde uyarlanabilir:

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

\ b, kelime sınırları için \ w kullanır; burada \ w [0-9A-Z_a-z] ile eşdeğerdir. Bu sınırlamaya aldırmazsanız, kabul edilen cevap iyidir.


2

Bazı geliştiriciler, yalnızca birbirini izleyen boşluk olmayan alt dizeleri aynı zamanda üç kopya ve ötesini ortadan kaldıran bir çözüm arayışı içinde bu sayfaya geldiklerinden, uyarlanmış modeli göstereceğim.

Desen: /(\b\S+)(?:\s+\1\b)+/( Desen Demosu )
Değiştirin:$1 (tam dize eşleşmesini yakalama grubu 1 ile değiştirir)

Bu desen, "tam" boşluk olmayan bir alt dizeyle açgözlülükle eşleşir, daha sonra eşleşen alt dizenin bir veya daha fazla boşluk karakteri (boşluk, sekme, satırsonu, vb.) İle sınırlandırılabilen bir veya daha fazla kopyasını gerektirir.

özellikle:

  • \b (kelime sınırı) karakterleri, kısmi kelimelerin eşleşmemesini sağlamak için çok önemlidir.
  • İkinci parantez, yakalama yapmayan bir gruptur, çünkü bu değişken genişlikteki alt dizenin yakalanması gerekmez - yalnızca eşleşir / emilir.
  • +olmayan yakalama grubuna (bir veya daha fazla nicelik) daha uygun olduğunu *, çünkü *yakalama için normal ifade motoru "rahatsız" ve oluşumları tekil yerini alacak - Bu savurgan desen tasarımdır.

* Cümleler veya noktalama işaretli giriş dizeleriyle uğraşıyorsanız, kalıbın daha da iyileştirilmesi gerekeceğini unutmayın.


@AdamJones bu kalıbı php projenizde kullanır. Nico'nun cevabında bazı gereksiz sözdizimi var.
mickmackusa

1

Bu ifade (yukarıda Mike'tan esinlenmiştir), dizenin sonundakiler de dahil olmak üzere, diğerlerinin çoğunun yapmadığı tüm kopyaları, üçlüleri vb. Yakalıyor gibi görünüyor:

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

Sorunun yalnızca kopyalarla eşleşmesi için sorulduğunu biliyorum , ancak üç kopya, yan yana yalnızca 2 kopya var :)

İlk olarak, (^|\s+)tam bir kelime ile başladığından emin olmak istedim , aksi takdirde "çocuğun bifteği", "çocuk bifteği" ne giderdi ("s" ler eşleşir). Ardından, tüm tam sözcüklerle ( (\b\S+\b)) eşleşir , ardından dize sonuyla ( $) veya birkaç boşlukla ( \s+) eşleşir ; tümü birden fazla tekrarlanır.

Bunu böyle denedim ve iyi çalıştı:

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

Bunu PHP'ye yeniden yazarken sorun yaşıyorum, çok önemli, her yinelenen / üçlü vb. Yinelemenin yerini alan eşleşen yinelenenlerin tek bir kopyasını alıyorum. Şimdiye kadar elimde: preg_replace ('/ (^ | \ s +) (\ S +) ( ($ | \ s +) \ 2) + / im ',' $ 0 ', $ dizge);
AdamJones

Bu en iyi cevap. \bSonuna şu şekilde ekleyerek bir ince ayar yaptım : /(^|\s+)(\S+)(($|\s+)\2)+\b/g, "$1$2")Bu daha sonra şu gibi durumlar için işe yarayacak: Notice the the string String string stringing the the along the the stringolacak . Cevabınızla eşleşir. Teşekkür ederim. the string stringing the along the stringstring stringing
Ste

-1

Yinelenen sözcükler için büyük / küçük harfe duyarlı olmayan denetim yapmak istediğinizde bunu kullanın.

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

Büyük / küçük harfe duyarlı olmayan desen değiştiriciyi kullanmak, deseniniz için işe yaramaz. Bayrağın etkileyeceği harf aralığı yok.
mickmackusa

Bu, kabul edilen cevabın etkili bir şekilde kopyasıdır ve sayfaya hiçbir değer katmaz. Sayfa şişmesini azaltmak için lütfen bu yanıtı kaldırmayı düşünün.
mickmackusa
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.