2013-08-02 36 views
7

我有一個代碼示例,其中一個MatchCollection似乎掛起程序時試圖用它與foreach。可以在嘗試迭代它時MatchCollection掛起程序嗎?

我使用的是類CSSParser解析CSS:

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; 
     } 
    } 
} 

,它工作正常,像這樣輸入:

 [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")); 
     } 

但是當我用不含屬性的CSS串運行:

 [TestMethod] 
     public void TestParseNoAttributesStyle() 
     { 
      const string css = @" 
#submenu-container 
{ 
} 
"; 

      var parser = new CSSParser(css); 

      Assert.IsFalse(parser.LookupValues("#submenu-container").Any()); 
     } 

程序掛在CSSParser的這一行上:

foreach (Match item in matches) 

調試器停止標記當前正在執行的行,循環塊本身永遠不會到達。

爲什麼MatchCollection掛我的程序?

出於完整性:

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

我並不是Regex的專家,但是如果Regex引擎必須執行持續的lookahead和lookbehind操作,它可能會卡住或需要很長時間。設計你的正則表達式來最小化這些會有所幫助。也許正則表達式專家可以幫助你解決具體問題。 +1 =] – Sean

+1

暫停調試器並查看堆棧以確定發生了什麼。選中「顯示外部代碼」查看所有內容。 – usr

+1

a)像這樣的語言不應該*真正*用正則表達式解析。 b)爲什麼不[使用現成的東西](http://stackoverflow.com/q/512720/50776)?它可能會更快,更強大,並且讓您在項目中超前,而不是手工完成。 – casperOne

回答

1

您正則表達式就是低效和燃燒CPU。您可以通過以下方式來確認:a)查看使用的CPU時間; b)重複暫停調試器並查看堆棧(將位於正則表達式引擎的內部)。

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

到:

+0

那麼爲什麼只有空的CSS塊難以解析? –

+1

我不知道。修復正則表達式不是問題的一部分。問題是「可以將MatchCollection掛起」。答:不,只是緩慢。 – usr

+1

因此,你的答案將被標記爲解決方案 –

0

我從改變正則表達式

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

和執行時間的推移,22秒下調至1毫秒。

+1

第一個正則表達式與原始文章中的不同 - 在名稱組中有一個「\ s」額外字符。新的正則表達式也會產生與原始正則表達式不同的結果。爲了它的樂趣,你也可以嘗試下面的文本(一個額外的空間已被插入)'h1,h2,h3,h4,h5,h6 {font-fa mily:Georgia,'Times New Roman',serif ; 顏色:#006633; line-height:1.2em; font-weight:normal; }' – Ykok

+0

謝謝,我編輯了帖子,以便顯示正確的信息!屬性名稱中不會有空格。 –

1

據我可以告訴.net進入一個永恆的循環,因爲它嘗試不同的方法與你得到的正則表達式(GroupsPattern之一) - 我相信這是一個錯誤的地方。我看了一下這個正則表達式,並且據我所知,可以輕鬆地刪除\s*中的兩個,即分別位於否定組之前的那些組,分別爲[^,{]+[^}:]+,因爲它們已經佔用空間。

所以說是代替:

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

我有:

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

現在,這是正則表達式,所以我的機會已經被忽視的東西是相當大的。此外,我相信這也會導致一些已知的捕獲組在其中可能具有額外的空間(但似乎無論如何你都要修剪它們)。

希望它是可用的。雖然它還需要相當長的一段時間,但它適用於您提供的示例。

+0

正則表達式匹配器只是緩慢的,沒有永恆的循環。當我做了一些修改後,cssparser可以在眨眼的時候解析一個10k的css文件,這足以滿足我的需求。我會考慮使用你的正則表達式。 –

相關問題