6

我的小程序正在检查网站使用什么类型的脚本,它使用超过 80% 的 cpu i7 处理器。我想知道这正常吗?

代码:

public static class EnginesMatcher
{

    public static readonly Regex Drupal1 =
        new Regex(
            @"/misc/drupal\.js|Drupal\.settings|href=""http://drupal\.org""|\?q=node/[0-9]+|/\?q=user/register|/\?q=user/password|/user/register\?destination|<li class=""collapsed""><a href=""/node/add"">|/dpl3/files/|/modules/node/",
            RegexOptions.Compiled);

    public static readonly Regex XE1 =
        new Regex(
        @"XpressEngine|content=""zeroboardXE|content=""xe_board""|var zbxe_session_name|/xe\.css\?2|/xeicon/favicon\.ico""|#xe-editor-container-1|xpress_xeditor",
            RegexOptions.Compiled);

    //and 60 more such regex

在另一个班级

  public async Task<Result> Match(string url)
        {

            if (!await Open(url).ConfigureAwait(false)) return ResultKey;


            if (EnginesMatcher.Drupal1.IsMatch(html))
            {
                return new Result()
                {
                    Key = AResultKey.Success,
                    LogFile = "Drupal.txt",
                    Message = "Drupal",
                    Url = url
                };
            }
            if (EnginesMatcher.XE1.IsMatch(html))
            {
                return new Result()
                {
                    Key = AResultKey.Success,
                    LogFile = "Drupal.txt",
                    Message = "Drupal",
                    Url = url
                };
            }

Match(url) 函数在 ForeachAsyncLoop 中,程序检查大约 4000 个链接/分钟。当我禁用它使用高达 20% cpu 的所有正则表达式时,我可以提高正则表达式的性能吗?强调文本

4

2 回答 2

5

您可能无法使用正则表达式加快速度。但是,通过使用不同的算法,您可能会做得更好。

Aho-Corasick 字符串匹配算法旨在完成您正在做的事情:在大量文本中查找一组固定的字符串。

标准算法不支持正则表达式,但您的正则表达式是逐字字符串。也就是说,您正在寻找this|that|something else|something more. 这与寻找四个不同的字符串相同。

几年前,我在 C# 中发布了Aho-Corasick 算法的实现。通过对您做事的方式进行一些修改,它应该可以正常工作。

首先构建您要查找的字符串和相应网站的字典:

Dictionary<string, string> StringsToSites = new Dictionary<string, string>();

然后,添加字符串。例如,对于 Drupal,您需要添加:

StringsToSites.Add("/misc/drupal\.js", "Drupal");
StringsToSites.Add("Drupal.settings", "Drupal");
StringsToSites.Add(@"href=""http://drupal\.org"", "Drupal");
// problem with this one ...   |\?q=node/[0-9]+
StringsToSites.Add("?q=user/register", "Drupal");
// etc., etc.

请注意,不是逐字字符串的内容存在问题。实现无法匹配“?q=node/[0-9]+/”。我们能做的最好的就是添加字符串“?q=node/”。

然后,创建一个匹配器并填充它:

AhoCorasickStringSearcher matcher = new AhoCorasickStringSearcher();
foreach (var key in StringsToSites.Keys)
{
    matcher.AddItem(key);
}
matcher.CreateFailureFunction();

然后,对于您看到的每个链接,请致电matcher.Search

var Matches = matcher.Search(link);    

这会给你一个StringMatch对象的集合。您可以Text在字典中查找它以查看它与哪个站点匹配。例如:

foreach (var m in Matches)
{
    string site;
    if (StringsToSites.TryGetValue(m.Text, out site))
    {
        Console.WriteLine("Text '{0}' matches site '{1}'.", m.Text, site);
    }
}

我希望这至少比您的正则表达式解决方案快 10 倍,可能快 100 倍。

于 2013-11-13T22:17:16.437 回答
1

你正在做的事情是这样的:

  1. 引擎将尝试从字符串的开头按顺序匹配交替。
  2. 按照您调用正则表达式的顺序。

在尝试完成 1 和 2 时,您可以将所有正则表达式组合成一个正则表达式。我认为这不会在组合它们时导致性能损失。甚至可能更有效。

它值得探索,因为无论如何这可能会成为您代码中的一个缓慢点。

所以,相当于你正在做的是:

  Regex rxAll = new Regex(
      @"
           (?:
                ^ [\S\s]*
                (?<Drupal1>
                     /misc/drupal\.js
                  |  Drupal\.settings
                  |  href=""http://drupal\.org""
                  |  \?q=node/ [0-9]+
                  |  /\?q=user/register
                  |  /\?q=user/password
                  |  /user/register\?destination
                  |  <li\ class=""collapsed""><a\ href=""/node/add"">
                  |  /dpl3/files/
                  |  /modules/node/
                )
             |
                ^ [\S\s]*
                (?<XE1>
                     XpressEngine
                  |  content=""zeroboardXE
                  |  content=""xe_board""
                  |  var\ zbxe_session_name
                  |  /xe\.css\?2
                  |  /xeicon/favicon\.ico""
                  |  \#xe-editor-container-1
                  |  xpress_xeditor
                )
           )
      ", RegexOptions.IgnorePatternWhitespace);

  string html =
      @"
         <a class=""x-fn"" href=""javascript:void(0);"">
         <a class='x-fn' href = ""javascript:void(0); "">
         <a href='javascript:void(0);' class=x-fn >
         <a class=""x-fn"" href=javascript:void(0); >
         <a 'hello' href=javascript:void(0); world class=x-fn content=""xe_board"">
      ";

  Match matches = rxAll.Match( html );
  if (matches.Success)
  {
      if (matches.Groups["Drupal1"].Success)
      {
          Console.WriteLine("Matched Drupal1 -> {0}", matches.Groups["Drupal1"].Value);
          return;
      }
      if (matches.Groups["XE1"].Success)
      {
          Console.WriteLine("Matched XE1 -> {0}", matches.Groups["XE1"].Value);
          return;
      }
  }
  return;

编辑:顺便说一句,我在您的正则表达式上使用了 RegexFormat (.com) 程序。这些 Verbatim 正则表达式是
通过单击几下按钮完成的。因此,您可以使用工具为您完成所有工作。

于 2013-11-13T23:22:23.130 回答