MatchCollection是否可以在尝试迭代程序时挂起该程序

本文关键字:程序 迭代 挂起 是否 MatchCollection | 更新日期: 2023-09-27 18:26:20

我有一个代码示例,其中MatchCollection在尝试将程序与foreach一起使用时似乎挂起了程序。

我正在使用类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());
        }

程序挂在CSSSParser:中的这一行

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

MatchCollection是否可以在尝试迭代程序时挂起该程序

您的Regex只是效率低下且消耗CPU。您可以通过a)查看使用的CPU时间和b)反复暂停调试器并查看堆栈(将在Regex引擎的内部)来确认这一点。

据我所知,.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*)*'}";

现在这是正则表达式,所以我忽略某些内容的可能性很大。此外,我相信这也会导致一些命名的捕获组中可能有额外的空间(但似乎你无论如何都会修剪它们)。

希望它有用。尽管它仍然需要相当长的时间,但它与您给出的示例相结合。

我将正则表达式从:更改为

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

至:

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

执行时间从22秒下降到1毫秒。