2012-05-09 5 views
6

Wyraz:Jak mogę przekształcić wyrażenie formy Backus-Naur w Regex (.Net)?

N | (1 { A | B | C | D | E1 | E2 | E3 }) 

Znaczenie deskryptor „N” lub jednego lub więcej z wymienionych opisów bez powtarzania.

Najlepszym Mam to:

@"^(N|(A|B|C|D|E1|E2|E3){1,})$" 

Ale to nie przeszkadza powtórzeń.

@"^(N|(A{0,1}B{0,1}...)$" 

To zapobiega powtórzeniu, ale potem wymaga specyficznego zamówienia na elementy, co również nie jest w porządku.

Wszelkie pomysły?

(nie jestem pewien, że rzeczywiście sam wyraz BNF uniemożliwia powtórzenie, ale to jest to, czego potrzebuję.)

+0

http://kore-nordmann.de/blog/do_NOT_parse_using_regexp.html Używaj wyrażeń regularnych do rozpoznawania słów, a nie struktur. –

+0

Myślę, że to ma sens. Ale czego użyłbym, aby rozpoznać tę strukturę? @DavidBrabant Zostanie użyty do sprawdzenia danych wejściowych wspomnianego formularza. – Daniel

+0

A może PCRE to sugerowana metoda? – Daniel

Odpowiedz

4

Cóż, można, ale nie jest całkiem:

Regex regexObj = new Regex(
    @"^   # Start of string 
    (?:   # Either match... 
    N   # N 
    |    # or... 
    (?:   # Match one of the following: 
     A(?!.*A) # A unless followed somewhere later by another A 
    |   # or 
     B(?!.*B) # B unless... 
    |   # etc. etc. 
     C(?!.*C) 
    | 
     D(?!.*D) 
    | 
     E1(?!.*E1) 
    | 
     E2(?!.*E2) 
    | 
     E3(?!.*E3) 
    )+   # one or more times 
    )    # End of alternation 
    $    # End of string", 
    RegexOptions.IgnorePatternWhitespace); 

Rozwiązanie to wykorzystuje negative lookahead assertions.

+0

Fajnie, dziękuję! – Daniel

+0

Po prostu pytanie @ TIM. Jaką funkcję ma "?:"? Wydaje się, że działa bez tych dwóch. – Daniel

+0

@Daniel: Jedyna różnica polega na tym, że '(...)' jest grupą * przechwytywania *, co oznacza, że ​​silnik regex przechowuje to, co było dopasowane wewnątrz tej grupy, w referencji zwrotnej, którą możesz później odnieść. Jeśli nie musisz tego robić, '(?: ...)' działa tak samo, ale bez przechowywania tej części meczu. Dlatego są nieco bardziej wydajne. –

1

nie jestem pewien, że to możliwe, nawet dla .NET Regex (który jest bardziej wydajny niż najostrzejsza definicja "języka regularnego"); a poza tym, jeśli nie masz obowiązku korzystania tylko Regex, nie ma nic złego (moim zdaniem) z:

bool IsValid(string input) 
{ 
    var Ns = input.Count(c => c == 'N'); 
    var As = input.Count(c => c == 'A'); 
    // etc 
    var E1s = Regex.Matches(input, "E1").Count 
    // etc 

    var maxDescriptorCount = (new[] { As, ... ,E1s, ... }).Max(); 

    var isValid = 
     ((Ns == 1) && (maxDescriptorCount == 0)) 
     || 
     ((Ns == 0) && (maxDescriptorCount == 1)) 
     ; 

    return isValid; 
} 

Czy to najkrótszy kod, który rozwiązuje ten problem? Nie. Czy jest czytelny i można go konserwować? Chyba tak.

(Można napisać metodę użytkową z podpisem int MaxN(params int[] numbers) jeśli chciał)

Powiązane problemy