10 harften oluşan kelimelerin tümü için benzersiz harflerle regex


23

10 karakter uzunluğunda olan tüm kelimeleri gösterecek bir regex yazmaya çalışıyorum ve harflerin hiçbiri tekrar etmiyor.

Şimdiye kadar aldım

grep --colour -Eow '(\w{10})'

Bu sorunun ilk kısmı. "Eşsizliği" kontrol etmeye nasıl giderim? Gerçekten bir ipucum yok, bunun dışında referansları geri kullanmam gerekiyor.


1
Bu bir regex ile yapılmalı?
Hauke,

Regex pratiği yapıyorum, yani tercihen evet :)
Dylan Meeus,

3
Bunu bir bilgisayar bilimi tarzı düzenli ifade ile yapabileceğinizi sanmıyorum: İstediğiniz önceki eşleştirilen karakterlerin ne olduğunu "hafızaya" gerektirir ve normal ifadelerde buna sahip değildir. Bununla birlikte, geri referanslarla ve PCRE tarzı eşleştirmenin yapabileceği düzenli olmayan ifadelerle yapabileceğinizi söyledi.
Bruce Ediger

3
@BruceEdiger, dilde (26) sonlu sayıda karakter olduğu ve dize (10) içindeki harflerden oluşmadığı sürece, oldukça mümkündür. Onun sadece bir çok devleti, fakat onu normal bir dil değil hiçbir şey yapmaz.

1
"Tüm İngilizce kelimeler" i mi demek istiyorsun? Tire ve kesme işaretleri ile yazılanları dahil etmek mi istiyorsunuz, (gelin değil) Kafe, naif, cephe gibi kelimeler eklemek mi istiyorsun?
hippietrail

Yanıtlar:


41
grep -Eow '\w{10}' | grep -v '\(.\).*\1'

İki özdeş karaktere sahip kelimeleri içermez.

grep -Eow '\w{10}' | grep -v '\(.\)\1'

yinelenen karakterleri olanları hariç tutar.

POSIXly:

tr -cs '[:alnum:]_' '[\n*]' |
   grep -xE '.{10}' |
   grep -v '\(.\).*\1'

trssözcük olmayan karakterleri ( calfa-sayısal ve alt çizgi omplementi) herhangi bir denklemini yeni satır karakterine dönüştürerek sözcükleri kendi satırlarına koyar .

Veya biriyle grep:

tr -cs '[:alnum:]_' '[\n*]' |
   grep -ve '^.\{0,9\}$' -e '.\{11\}' -e '\(.\).*\1'

(10'dan az ve 10 karakterden ve karakterleri en az iki kez görünen satırları hariç tutun).

Biri ile grepsadece (GNU grep ile PCRE destek veya pcregrep):

grep -Po '\b(?:(\w)(?!\w*\1)){10}\b'

Yani, bir kelime sınırını ( \b) takip eden 10 kelime karakterinden oluşan bir sekans (her birinin negatif karakterli PCRE operatörünü kullanarak bir kelime karakterleri ve kendilerinin takip etmesi şartıyla (?!...)).

Burada çalıştığı için şanslıyız, çünkü pek çok regexp motoru, tekrar eden parçaların içindeki geri referanslarla çalışmaz.

(GNU grep sürümümle en azından)

grep -Pow '(?:(\w)(?!\w*\1)){10}'

Çalışmıyor

grep -Pow '(?:(\w)(?!\w*\2)){10}'

echo aa | grep -Pw '(.)\2'bir böcek gibi ses çıkarır (as ).

İsteyebilirsin:

grep -Po '(*UCP)\b(?:(\w)(?!\w*\1)){10}\b'

İsterseniz \wveya \bbir kelime bileşeni olarak herhangi bir harf ve ASCII olmayan yerel ayarlarda sadece ASCII olanları dikkate almak.

Başka bir alternatif:

grep -Po '\b(?!\w*(\w)\w*\1)\w{10}\b'

Bu bir kelime sınırıdır (biri tekrar eden bir kelime karakterleri dizisi tarafından takip edilmeyen) ve ardından 10 kelime karakteri.

Muhtemelen birinin zihninde olması gereken şeyler:

  • Karşılaştırma, büyük / küçük harfe duyarlıdır; bu nedenle Babylonish, örneğin, iki Bs, bir küçük ve bir büyük büyük harf olmasına rağmen tüm karakterler farklı olduğundan (örneğin bunu değiştirmek için kullanılır) eşleşir -i.
  • için -w, \wve \b, bir kelime bir harf (ASCII olanlar, sadece GNU içindir grep şimdilik , [:alpha:]yerdeki karakter sınıfı kullanılıyorsa -Pve (*UCP)), ondalık basamak veya alt çizgi .
  • Bu, c'est(bir kelimenin Fransızca tanımına göre iki kelime) veya it's(bir kelimenin bazı İngilizce tanımlarına göre bir kelime) veya rendez-vous(bir kelimenin Fransızca tanımına göre bir kelime) bir kelime olarak kabul edilmez.
  • Bununla birlikte (*UCP), Unicode birleştirme karakterleri kelime bileşenleri olarak kabul edilmez, bu nedenle téléphone( $'t\u00e9le\u0301phone') biri alfa olmayan 10 karakter olarak kabul edilir. défavorisé( $'d\u00e9favorise\u0301') iki olmasına rağmen eşleşir, éçünkü 10 farklı alfa karakterinden sonra akut aksanı birleştirir (alfa olmayan, yani eaksanı ve arasında bir kelime sınırı vardır ).

1
Muhteşem. olsa \weşleşmiyor -.
Graeme

@Stephane Son iki ifadenin kısa bir açıklamasını gönderir misiniz?
mkc

Bazen görünüşe bakılırsa, RE ile imkansız olan her şeyin çözümü gibi görünüyor.
Barmar

1
@Barmar Düzenli İfadeler ile hala imkansız. Bir "Normal İfade", yalnızca belirli yapılara, yani değişmez karakterlere, karakter sınıflarına ve '|', '(...)', '?', '+' Ve '*' operatörlerine açıkça izin veren matematiksel bir yapıdır. Yukarıdakilerden biri olmayan bir işleç kullanan "normal ifade" olarak adlandırılan herhangi bir ifade, aslında Normal İfade değildir.
Jules

1
@Jules Bu unix.stackexchange.com'dur, math.stackexchange.com'dur. Matematiksel
RE'ler

12

Tamam ... işte beş karakterli bir dizgenin parçası:

grep -P '^(.)(?!\1)(.)(?!\1|\2)(.)(?!\1|\2|\3)(.)(?!\1|\2|\3|\4).$'

Bir karakter sınıfına (örneğin [^\1|\2]) bir geri referansı koyamayacağınız için, olumsuz bir görüş belirtmelisiniz - (?!foo). Bu bir PCRE özelliğidir, bu yüzden -Panahtara ihtiyacınız vardır .

10 karakterli bir dizginin deseni elbette çok daha uzun olacaktır, ancak görünümdeki herhangi bir şeyle eşleşen değişken uzunluktaki ('. *') Daha kısa bir yöntem vardır:

grep -P '^(.)(?!.*\1)(.)(?!.*\2)(.)(?!.*\3)(.)(?!.*\4)(.)(?!.*\5).$'

Stephane Chazelas'ın aydınlatıcı cevabını okuduktan sonra, grep'in düğmesiyle kullanılabilen benzer bir basit kalıp olduğunu anladım -v:

    (.).*\1

Kontrol bir defada bir karakter ilerlettiğinden, herhangi bir karakterin sıfır veya daha fazla karakter izlediğini ( .*) ve ardından geri referans için bir eşleşme olup olmadığını göreceksiniz . -vters çevirir, yalnızca bu desene uymayan şeyleri yazdırır . Bu, geri referansları bir karakter sınıfıyla ihmal edilemediklerinden daha faydalı kılar ve önemli ölçüde:

grep -v '\(.\).*\1'

benzersiz karakterlere sahip herhangi bir uzunlukta bir dize tanımlamaya çalışır, oysa

grep -P '(.)(?!.*\1)'

de (örneğin eşsiz karakterlerle soneki maç olacak çünkü olmaz abcabcçünkü maçları abcsonunda ve aaaayüzünden a- dolayısıyla sonunda herhangi dize). Bu, görünümlerin sıfır genişliğinde olmasından kaynaklanan bir komplikasyondur (hiçbir şey tüketmezler).


Aferin! Bu sadece Q'daki ile birlikte çalışacaktır.
Graeme

1
Eğer regex motorunuz değişken uzunluktaki negatif (.)(?!.*\1)(.)(?!.*\2)(.)(?!.*\3)(.)(?!\4).
görünüşe

@ ChristopherCreutzig: Kesinlikle, güzel çağrı. Bunu da
ekledim

6

Her şeyi regex'te yapmanız gerekmiyorsa, iki adımda yaparım: ilk önce 10 harfli sözcüklerin hepsini eşleştirin, sonra benzersiz olmaları için filtreleyin. Bunu nasıl yapacağımı bildiğim en kısa yol Perl'de:

perl -nle 'MATCH:while(/\W(\w{10})\W/g){
             undef %seen;
             for(split//,$1){next MATCH if ++$seen{$_} > 1}
             print
           }' your_file

\WYalnızca tam olarak 10 karakter uzunluğunda olan sözcüklerin eşleşmesini sağlamak için ek bağlantılara dikkat edin.


Teşekkürler, ama bir regex oneliner :) olarak istiyorum
Dylan Meeus

4

Diğerleri, bunun aslında düzenli olmayan belirli düzenli ifade sistemlerinde çeşitli uzantılar olmadan mümkün olmadığını öne sürdü. Ancak, eşlemek istediğiniz dil sınırlı olduğu için açıkça düzenli. 4 harfli bir alfabeden 3 harf için, kolay olurdu:

(abc|abd|acb|acd|bac|bad|bcd|bdc|cab|cad|cbd|cdb|dab|dac|dbc|dcb)

Açıkçası, bu daha fazla harf ve daha büyük alfabe ile aceleyle elden çıkar. :-)


Bunu yükseltmem gerekiyordu çünkü işe yarayacak bir cevaptı. Gerçekte, herhangi birinin regex yazdığı en etkili yöntem olsa da: P
Dylan Meeus

4

GNU seçeneği --perl-regexp(kısa -P), grepileriye dönük modelleri içeren daha güçlü normal ifadeler kullanır. Aşağıdaki örnek, bu harfin kelimenin geri kalanında görünmediği her harfi arar:

grep -Pow '((\w)(?!\w*\g{-1})){10}'

Ancak, çalışma zamanı davranışı oldukça kötü, çünkü \w*neredeyse sonsuz uzunlukta olabilir. Bu \w{,8}, ancak bunlarla sınırlı olabilir , ancak 10 harften oluşan kelime sınırını da kontrol eder. Bu nedenle, aşağıdaki örnek önce doğru sözcük uzunluğunu kontrol eder:

grep -Pow '(?=\w{10}\b)((\w)(?!\w*\g{-1})){10}'

Test dosyası olarak büyük bir MB 500 MB dosya kullandım:

  • İlk desen: ≈ 43 s
  • İkinci desen: ≈ 15 s

Güncelleştirme:

Açgözlü olmayan bir operatör ( \w*?) veya sahiplikçi bir operatör ( (...){10}+) için çalışma zamanı davranışında önemli bir değişiklik bulamadım . Küçük bir parça biraz daha hızlı bir seçenek olarak görünüyor -w:

grep -Po '\b(?=\w{10}\b)((\w)(?!\w*\g{-1})){10}\b'

Grep'in sürüm 2.13'ten 2.18'e güncellenmesi çok daha etkili oldu. Test dosyası sadece s 6 saniye sürdü.


Performans, verinin niteliğine çok bağlı olacaktır. Maden üzerinde testler yaparken, açgözlü olmayan operatörleri ( \w{,8}?) kullanmanın bir tür girdi için yardımcı olduğunu (çok anlamlı olmasa da) buldum . \g{-1}GNU grep böceği çevresinde çalışmak için güzel bir kullanım .
Stéphane Chazelas

@StephaneChazelas: Geri bildiriminiz için teşekkür ederiz. Ayrıca açgözlü ve mülk sahibi operatörleri de denemedim ve çalışma zamanı davranışında önemli bir değişiklik bulamadım (sürüm 2.13). Sürüm 2.18 çok daha hızlı ve en azından küçük bir gelişme görebildim. GNU grep hatası her iki versiyonda da var. Her neyse göreceli referansı tercih ediyorum \g{-1}, çünkü deseni lokasyonda daha bağımsız kılıyor. Bu formda daha büyük bir desenin parçası olarak kullanılabilir.
Heiko Oberdiek

0

Bir Perl çözümü:

perl -lne 'print if (!/(.)(?=$1)/g && /^\w{10}$/)' file

ama işe yaramıyor

perl -lne 'print if (!/(.)(?=\1)/g && /^\w{10}$/)' file

veya

perl -lne 'print if ( /(.)(?!$1)/g && /^\w{10}$/)' file

perl v5.14.2 ve v5.18.2 ile test edilmiştir


1. ve 3. hiçbir şey yapmaz, 2. sırada 10 veya daha fazla karakterden oluşan bir satır çıkar, arka arkaya 2'den fazla boşluk bırakmaz. pastebin.com/eEDcy02D
insan eseri

muhtemelen perl versiyonudur. v5.14.2 ve v5.18.2 ile test edildi

Onları Linux'ta v5.14.1 ve Cygwin'de v5.14.2 ile denedim. Her ikisi de daha önce bağladığım pastebin örneğinde olduğu gibi davrandı.
Manatwork

ilk satır benim için perl ile belirtilen sürümleriyle çalışıyor. bu ikisi çalışmalı, çünkü onlar aynı, fakat işe yaramadılar. Perlre sık sık bazı açgözlü ifadeler oldukça deneysel olduğunu unutmayın.

En son güncellemelerinizle tekrar test edildi. Yalnızca 2. olanı doğru çıktı. (Soru eşleştirme kelimeleri değil, tüm hatları ile ilgili iken Ancak kelime, bir çizgide yalnız olmalıdır.)
manatwork
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.