RegEx'in, nasıl en fazla 3 benzersiz karakter içeren bir satır bulabilirim?

4 Cevap php

Ben büyük bir metin dosyası ve en fazla 3 farklı karakter (bu karakterler, ancak, süresiz tekrar edilebilir) içeren hatlar için arayan im döngü duyuyorum. Bunu yapmak için en iyi yolu düzenli ifadenin bir tür olacağını varsayarak yaşıyorum.

Tüm yardım takdir edilmektedir.

(Eğer yardımcı olur ben, PHP komut dosyası yazıyorum)

4 Cevap

Belki de bu çalışacaktır:

preg_match("/^(.)\\1*(.)?(?:\\1*\\2*)*(.)?(?:\\1*\\2*\\3*)*$/", $string, $matches);
// aaaaa:Pass
// abababcaaabac:Pass
// aaadsdsdads:Pass
// aasasasassa:Pass
// aasdasdsadfasf:Fail

Açıklama:

/
 ^                 #start of string
 (.)               #match any character in group 1
 \\1*              #match whatever group 1 was 0 or more times
 (.)?              #match any character in group 2 (optional)
 (?:\\1*\\2*)*     #match group 1 or 2, 0 or more times, 0 or more times 
                   #(non-capture group)
 (.)?              #match any character in group 3 (optional)
 (?:\\1*\\2*\\3*)* #match group 1, 2 or 3, 0 or more times, 0 or more times
                   #(non-capture group)
 $                 #end of string
/

Bir yararı, $matches[1], [2], [3] İstediğiniz üç karakter içerir. Normal ifade, ilk karakteri arar sonra saklar ve bu karakterin başka bir şeye kadar bulunmuş olduğu kadar maçları, birçok kez olabildiğince bu karakterlerden birini uyan ikinci bir karakter olarak, üçüncü karakterini yakalar yakalar ve maç başarısız veya dize biter ve test geçene kadar üçünü eşleşir.

EDIT

Bu regexp çünkü motoru ayrıştırma ve geriye işleri, açıklama için bobince cevabını okumak yolu çok daha hızlı olacaktır:

/^(.)\\1*(?:(.)(?:\\1|\\2)*(?:(.)(?:\\1|\\2|\\3)*)?)?$/

Çocuklar için Regex optimizasyon eğlenceli zaman egzersiz! Bir başlangıç ​​noktası olarak Gnarf en regex alıyor:

^(.)\1*(.)?(?:\1*\2*)*(.)?(?:\1*\2*\3*)*$

Ben sıralı * s Backtracking bir çok neden olabilir, orada burada iç içe ve fark ettim. 'Abcaaax' Örneğin uzunluğunun 3 bir tek \ 1 *, bir tek \ 1, 2 uzunluğunda ardından \ 1 takip uzunluğunda iki bir \ 1 * olarak yıllardan 'o son dize maç çalışacağız \ 1 *, ya da üç tek maç \ 1s. Daha uzun dizeleri var o zaman sorun nedeniyle regex için \ 2 olarak aynı karakter olmaktan \ 1 durdurma şey var, özellikle, çok daha kötü olur.

^(.)\1*(.)?(?:\1|\2)*(.)?(?:\1|\2|\3)*$

Bu Python PCRE'nin eşleşmenin üzerinde test, iki kat daha hızlı orijinal olarak bitti. (Bu üzgünüm, PHP kurduktan daha hızlıdır.)

Bu hala o (.)? hiçbir maç ve ardından maçın geri kalanı ile taşıyabilen bir sorunu var. \1|\2 Hala maç olacak \ 1 \1|\2 ve \1|\2|\3 maddelerini tanıtmak için çalışırken potansiyel Backtracking sonuçlanan maç için hiçbir \ 2, olsa bile daha önce ne zaman onlar 't maç sonucu. Bu firar hükümlerin tamamının çevresinde ? optionalness hareket ile çözülebilir:

^(.)\1*(?:(.)(?:\1|\2)*(?:(.)(?:\1|\2|\3)*)?)?$

Bu iki kat daha hızlı daha oldu.

O \ 1, \ 2 herhangi bir ve \ 3 ifadesi eşleşmiyor potansiyel olarak daha geriye gidilmiştir neden, aynı karakter olabilir potansiyel bir sorun hala var. Bu bir önceki karakteri uyuşmuyor olumsuz lookahead kullanarak bunu durdurmak istiyoruz:

^(.)\1*(?:(?!\1)(.)(?:\1|\2)*(?:(?!\1|\2)(.)(?:\1|\2|\3)*)?)?$

Ancak Python benim rastgele test verileri ile bu anlamlı bir hıza fark etmedi. Sizin kilometre test verilerine bağımlı PHP değişebilir, ama zaten yeteri kadar iyi olabilir. Bu burada mevcut olsaydı iyelik-eşleştirme (* +) yardımcı olabilir.

Hayır regex daha kolay okunması Python alternatif daha iyi performans:

len(set(s))<=3

PHP benzer yöntem muhtemelen count_chars ile olacaktır:

strlen(count_chars($s, 3))<=3

Ben hızını test değil ama ben çok fazla bu çok olmasının yanı sıra, okumak için çok güzel regex daha hızlı olması beklenir.

Yani temelde ben sadece tamamen regexes uğraşmaktan vaktimi boşa. Regex başvurmadan önce ilk basit dize yöntemler aramak, zaman israf etmeyin!

Downvoted yakalanma riski az, ben düzenli ifadeler, bu durumun üstesinden gelmek demek değildir önerecektir.

Bir karakteri veya karakter kümesi maç, ama sen zaten daha maçından bu dışlamak için bulunmuştur bir dizi ne karakterleri hatırlamıyorum olamaz.

Ben size yeni bir hat ile başlamadan önce bunu sıfırlamak, bir karakter kümesi korumak önermek ve hat üzerinde giderken orada öğeleri ekleyin. En kısa sürede kümedeki elemanların sayısı 3 aştığı gibi, geçerli satır bırakın ve sonraki devam.

Benim için - adil yeterince düzenli ifade bilgisi olan bir programcı olarak bu Regexp sadece kullanarak çözebilir bir sorun gibi değil sesler.

karakter değeri: Her hat için haritayı yeniden, büyük metin dosyasını saymak ve yineleme bir hashmap / dizi veri yapısı anahtarını oluşturmak gerekir olasılığı daha yüksektir. zaten karşılaştı karakter sayısı 2 ise her yeni karakter check, eğer öyleyse, geçerli satır atlayın.

bir deli sıradanifade korsan bir çözüm ile gelip eğer ancak sürpriz için istekli im.