2013-08-02 2 views
7

Ich habe ein Codebeispiel, wo eine MatchCollection das Programm zu hängen scheint, wenn es versucht, es mit Foreach zu verwenden.Kann eine MatchCollection das Programm beim Versuch, es zu iterieren, hängen?

ich css bin Parsen eine Klasse CSSParser mit:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text.RegularExpressions; 
using Helpers.Extensions; 

namespace Helpers.Utils 
{ 
    public class CSSParser 
    { 
     private readonly Dictionary<string, Dictionary<string, string>> 
      _dict = new Dictionary<string, Dictionary<string, string>>(); 

     private const string SelectorKey = "selector"; 
     private const string NameKey = "name"; 
     private const string ValueKey = "value"; 

     private const string GroupsPattern 
      = @"(?<selector>(?:(?:[^,{]+)\s*,?\s*)+)\{(?:(?<name>[^}:]+)\s*:\s*(?<value>[^};]+);?\s*)*\}"; 

     private const string CommentsPattern 
      = @"(?<!"")\/\*.+?\*\/(?!"")"; 

     private readonly Regex _pattern 
      = new Regex(GroupsPattern, RegexOptions.IgnoreCase | RegexOptions.Multiline); 

     public CSSParser(string cssString) 
     { 
      var noCommentsString = Regex.Replace(cssString, CommentsPattern, ""); 
      var matches = _pattern.Matches(noCommentsString); 

      foreach (Match item in matches) 
      { 
       var selector = item.Groups[SelectorKey].Captures[0].Value.Trim(); 

       var selectorParts = selector.Split(',').Select(s=>s.Trim()); 
       foreach(var part in selectorParts) 
       { 
        if (!_dict.ContainsKey(part)) 
         _dict[part] = new Dictionary<string, string>(); 
       } 

       var classNameCaptures = item.Groups[NameKey].Captures; 
       var valueCaptures = item.Groups[ValueKey].Captures; 

       var count = item.Groups[NameKey].Captures.Count; 

       for (var i = 0; i < count; i++) 
       { 
        var className = classNameCaptures[i].Value.TrimIfNotNull(); 
        var value = valueCaptures[i].Value.TrimIfNotNull(); 

        foreach(var part in selectorParts) 
        { 
         _dict[part][className] = value; 
        } 
       } 
      } 
     } 

     public IEnumerable<KeyValuePair<string,string>> LookupValues(string selector) 
     { 
      IEnumerable<KeyValuePair<string,string>> result 
       = new KeyValuePair<string,string>[]{}; 
      if (_dict.ContainsKey(selector)) 
      { 
       var subdict = _dict[selector]; 

       result = subdict.ToList(); 
      } 

      return result; 
     } 

     public string LookupValue(string selector, string style) 
     { 
      string result = null; 
      if (_dict.ContainsKey(selector)) 
      { 
       var subdict = _dict[selector]; 

       if (subdict.ContainsKey(style)) 
        result = subdict[style]; 
      } 

      return result; 
     } 
    } 
} 

und es funktioniert gut mit Eingabe wie folgt aus:

 [TestMethod] 
     public void TestParseMultipleElementNames() 
     { 
      const string css = @"h1, h2, h3, h4, h5, h6 
{ 
    font-family: Georgia, 'Times New Roman', serif; 
    color: #006633; 
    line-height: 1.2em; 
    font-weight: normal; 
} 
"; 

      var parser = new CSSParser(css); 

      Assert.AreEqual("normal", parser.LookupValue("h4", "font-weight")); 
     } 

aber wenn ich laufe es mit einem CSS-String keine Attribute enthält:

Das Programm hängt an dieser Zeile in CSSParser:

Der Debugger stoppt die Markierung der aktuell ausgeführten Zeile, der Schleifenblock selbst wird nie erreicht.

Warum hängt die MatchCollection mein Programm?

Der Vollständigkeit halber:

namespace Helpers.Extensions 
{ 
    public static class StringExtension 
    { 
    public static string TrimIfNotNull(this string input) 
    { 
     return input != null ? input.Trim() : null; 
    } 
    } 
} 
+2

Ich bin kein Experte mit Regex, aber die Regex-Engine kann steckenbleiben oder eine lange Zeit dauern, wenn sie konstante Lookahead- und Lookbehind-Operationen durchführen muss. Entwerfen Sie Ihre Regex, um diese zu minimieren, würde helfen. Vielleicht kann ein Regex-Experte Ihnen bei den Einzelheiten helfen. +1 =] – Sean

+1

Pausieren Sie den Debugger und schauen Sie sich den Stack an, um festzustellen, was passiert.Aktivieren Sie "Zeige externen Code", um alles zu sehen. – usr

+1

a) Sprachen wie diese sollten nicht wirklich mit Regex geparst werden. b) Warum nicht [etwas von der Stange benutzen] (http://stackoverflow.com/q/512720/50776)? Es wird wahrscheinlich viel schneller und robuster sein und Sie in Ihrem Projekt mehr voranbringen, als es von Hand zu tun. – casperOne

Antwort

1

Ihre Regex ist nur ineffizient und Brennen CPU. Sie können dies bestätigen, indem Sie a) die verwendete CPU-Zeit betrachten und b) den Debugger wiederholt pausieren und auf den Stack schauen (er wird sich im Inneren der Regex-Engine befinden).

+0

Warum sind dann nur leere CSS-Blöcke schwer zu parsen? –

+1

Ich weiß es nicht. Das Fixieren der Regex ist nicht Teil der Frage. Die Frage ist "kann eine MatchCollection hängen". Antwort: Nein, nur langsam. – usr

+1

Daher werden Sie als Lösung –

0

änderte ich die regexp aus:

private const string GroupsPattern 
    = @"(?<selector>(?:(?:[^,{]+)\s*,?\s*)+)\{(?:(?<name>[^}:]+)\s*:\s*(?<value>[^};]+);?\s*)*\}"; 

zu:

private const string GroupsPattern 
    = @"(?<selector>(?:(?:[^,{]+)\s*,?\s*)+)\{\s*(?:(?<name>[^}:\s]+)\s*:\s*(?<value>[^};]+);?\s*)*\}"; 

und die Ausführungszeit ging von 22 Sekunden bis 1 ms.

+1

Die erste Regexp ist nicht die gleiche wie im ursprünglichen Post - es gibt ein '\ s' extra in der Namensgruppe. Die neue Regex wird auch andere Ergebnisse als die ursprüngliche ergeben. Zum Spaß können Sie es auch mit dem folgenden Text versuchen (ein extra Leerzeichen wurde eingefügt) 'h1, h2, h3, h4, h5, h6 {font-family: Georgia, 'Times New Roman', serif ; Farbe: # 006633; Zeilenhöhe: 1.2em; Schriftgewicht: normal; } ' – Ykok

+0

Danke, ich habe den Post bearbeitet, damit er die richtigen Informationen anzeigt! Ich werde nie Leerzeichen in Attributnamen haben. –

1

Soweit ich das beurteilen kann .net geht in eine ewige Schleife, weil es versucht verschiedene Ansätze mit der Regex Sie haben (die GroupsPattern eins) - ich glaube, es macht einen Fehler irgendwo. Ich habe mir diesen Regex angeschaut und soweit ich das beurteilen kann, können Sie leicht zwei der \s* entfernen, nämlich diejenigen, die vor den Negationsgruppen [^,{]+ und [^}:]+ stehen, da sie bereits Leerzeichen erfassen.

Also das heißt, statt:

private const string GroupsPattern = @"(?<selector>(?:(?:[^,{]+)\s*,?\s*)+)\{(?:(?<name>[^}:]+)\s*:\s*(?<value>[^};]+);?\s*)*\}"; 

hätte ich:

private const string GroupsPattern = @"(?<selector>(?:(?:[^,{]+),?\s*)+)\{(?:(?<name>[^}:]+):\s*(?<value>[^};]+);?\s*)*\}"; 

Nun ist diese Regex Ausdrücke ist, so dass die Chancen von mir übersehen haben etwas ziemlich groß. Außerdem glaube ich, dass dies auch dazu führt, dass einige der genannten Capture-Gruppen möglicherweise zusätzliche Leerzeichen enthalten (aber es scheint, dass Sie sie trotzdem schneiden).

Ich hoffe, es ist verwendbar. Obwohl es noch einige Zeit dauert, funktioniert es mit dem Beispiel, das Sie gegeben haben.

+0

Die Regexp-Matcher waren nur langsam, keine ewigen Schleifen. Als ich einige Änderungen vorgenommen hatte, konnte der CSS-Parser eine 10k-CSS-Datei mit einem Augenzwinkern analysieren, was für meine Bedürfnisse ausreichend ist. Ich werde in Betracht ziehen, Ihre Regexp zu verwenden. –

Verwandte Themen