Bir dize eşleşmeyen varsa parantez kontrol etmek için regex?

8 Cevap php

Bir PHP komut dosyası, ben bir dize uyumsuz parantez kontrol etmek için ne regex kullanmalıyım? Ben izin vermek istediğiniz şeyler şunlardır:

  • Bu (Tamam) 'dir
  • Bu (bir) (ok)

Ben engellemek istediğiniz şeyler:

  • Bu () kötü
  • Bu da (kötü
  • Bu (kötü (çok)

Teşekkürler!

Güncelleme: Sen adamlar hepsi kaya. Bir regex ile bunu olması gerekenden daha yanıltıcıdır görünüyordu ve 2. seviye cevapları bu tür stackoverflow güzel kılan. Linkleri ve pseudocode için teşekkürler. Ben cevap vermek kim emin değilim, o yüzden kimin cevaplar ben kabul edemem herkesten özür diliyorum.

8 Cevap

Regex iş için doğru araç değildir. Elle bir dize tarayın.

Pseudo-kod:

depth = 0
for character in some_string:
    depth += character == '('
    depth -= character == ')'
    if depth < 0:
       break

if depth != 0:
   print "unmatched parentheses"

Sen can bir düzenli ifade ile bunu - PCRE'nin, PHP tarafından kullanılan, özyinelemeli desenleri verir. PHP Manual bir example neredeyse tam olarak ne istediğinizi verir:

\(((?>[^()]+)|(?R))*\)

This matches any correctly parenthesised substring as long as it begins and ends with parentheses. If you want to ensure the entire string is balanced, allowing strings like "wiggedy(wiggedy)(wiggedy(wack))", here's what I came up with:

^((?:[^()]|\((?1)\))*+)$

Burada daha fazla şaşırtmalı daha aydınlatıcı olabilir desen bir açıklama var:

^             Beginning of the string
(             Start the "balanced substring" group (to be called recursively)
  (?:         Start the "minimal balanced substring" group
    [^()]     Minimal balanced substring is either a non-paren character
    |         or
    \((?1)\)  a set of parens containing a balanced substring
  )           Finish the "minimal balanced substring" group
  *           Our balanced substring is a maximal sequence of minimal
              balanced substrings
  +           Don't backtrack once we've matched a maximal sequence
)             Finish the "balanced substring" pattern
$             End of the string

Regexes bu tür ile gelip verimlilik ve doğruluğu düşünceler çok sayıda bulunmaktadır. Dikkatli olun.

Bu düzenli ifade ile gerçekleştirmek mümkün değildir. Parantez bir regex mevcut olmayan bir özyinelemeli / sayma özelliği gerektirir. Bunun için bir ayrıştırıcı gerekir.

Daha fazla bilgi burada mevcuttur: http://blogs.msdn.com/jaredpar/archive/2008/10/15/regular-expression-limitations.aspx

JaredPar cevabını uzatmak için, sadece dize her karakteri inceler ve artırım / a azaltır sayacı bir fonksiyon yazmak, bir regex kullanmadan çözmek çok zor değil. Eğer bulursanız, bir "(" bunu artırmak ve bulmak eğer ")", olarak azaltmak. Sayaç hiç 0'ın altına giderse, dize geçersiz, kırabilir. Eğer bütün dize işlenen ettik zaman sayacı 0 değilse, eşsiz bir açık parantez vardı.

Sizin örnekler herhangi bir iç içe parantez ... sen yuva ile ilgili değilse, o zaman bu şu ifadeyi kullanarak yapılabilir dahil değildir:

^[^()]*(?:\([^()]*\)[^()]*)*$

Bu "izin" listesinde tüm dizeleri karşı maç ve "önleme" listesinde dizeleri karşı başarısız olur. Bununla birlikte, it will also fail against any string with nested parentheses. örneğin "Bu ((değil) ok)"

Diğerleri zaten işaret gibi iç içe işlemek gerekiyorsa, düzenli ifadeler doğru aracı değildir.

Bu bir regex ile imkansız olduğunu gerçeği ile katılıyorum. Eğer olsa, aşağıdakileri yapabilirsiniz:

<?php

$testStrings = array( 'This is (ok)', 'This (is) (ok)', 'This is )bad(', 'This is also (bad', 'This is (bad (too)' );

foreach( $testStrings as $string ) {
    $passed = hasMatchedParentheses( $string ) ? 'passed' : 'did not pass';
    echo "The string $string $passed the check for matching parenthesis.\n";
}

function hasMatchedParentheses( $string ) {
    $counter = 0;
    $length = strlen( $string );
    for( $i = 0; $i < $length; $i ++ ) {
    	$char = $string[ $i ];
    	if( $char == '(' ) {
    		$counter ++;
    	} elseif( $char == ')' ) {
    		$counter --;
    	}
    	if( $counter < 0 ) {
    		return false;
    	}
    }
    return $counter == 0;
}

?>

Why it's not possible with a regex

Diğer cevaplar hepsi doğru, ama ben sadece teorik bilgisayar bilimi için bir fiş koymak istiyorum ... Bu teoriyi bilerek gerçek bir pratik avantaj sağlayan bir durumdur.

Bir regex deterministik sonlu otomat (DFA) karşılık, ancak paren eşleşen bir DFA tarafından sonlu otomata (PDA) olarak değil, fark edilebilir bir bağlam serbest dilbilgisi gerektirir.

Bu nedenle, ekstra beyin bir sürü iş olmadan, biz cevap hayır olduğunu biliyorum, ve biz sadece bakan konum bir şey olduğunu endişelenmenize gerek yok. Yani, yukarıdaki cevaplar emin olmak, ve onlar cevap vermek zaman yazarlar sadece bir şey bakan olduğunu merak edebilirsiniz.

Hemen hemen tüm derleyici kitapları bu bahsedeceğiz, burada hızlı bir bakış:

http://books.google.com/books?id=4LMtA2wOsPcC&pg=PA94&lpg=PA94&dq=push-down+finite+automata&source=bl&ots=NisYwNO1r0&sig=ajaSHFXwpPOWG8IfbcfKoqzS5Wk&hl=en&ei=m26cSdf6DZGYsAPB-6SsAg&sa=X&oi=book_result&resnum=6&ct=result

Jared ne dedi ... Bunu yapamam. Ben bile Perl verir # 1 # 2 malzeme ile bunu yapabileceğimi sanmıyorum ...