2011-09-13 14 views
2

私はWebサーバーログのUserAgent列のブラウザ数を分析するためのC#プログラムを作成しています。ブラウザの種類、ブラウザのメジャーバージョン、ヒット数を出力したいと思います。C#でこのUserAgentパーサーをループ用に最適化するにはどうすればよいですか?

これをどのように最適化できますか?

私はregexを使ってUserAgent文字列とあらかじめ定義された文字列を比較して、Firefox、Operaなどをテストしています。次にregexを使用して不一致を取り消します。私は正規表現を使ってメジャーバージョンを入手します。私は、各ブラウザのために、この情報を保持する構造体を使用します。

private struct Browser 
{ 
    public int ID; 
    public string name; 
    public string regex_match; 
    public string regex_not; 
    public string regex_version; 
    public int regex_group; 
} 

私はその後、UserAgentのためのすべてのレコードのブラウザ情報とループをロードします。

Browser[] browsers = new Browser[5]; 
for (int i = 0; i < 5; i++) 
{ 
    browsers[i].ID = i; 
} 
browsers[0].name = "Firefox"; 
browsers[1].name = "Opera"; 
browsers[2].name = "Chrome"; 
browsers[3].name = "Safari"; 
browsers[4].name = "Internet Explorer"; 
browsers[0].regex_match = "(?i)firefox/([\\d\\.]*)"; 
browsers[1].regex_match = "(?i)opera/([\\d\\.]*)"; 
browsers[2].regex_match = "(?i)chrome/([\\d\\.]*)"; 
browsers[3].regex_match = "(?i)safari/([\\d\\.]*)"; 
browsers[4].regex_match = "(?i)msie([+_ ]|)([\\d\\.]*)"; 
browsers[0].regex_not = "(?i)flock"; 
browsers[1].regex_not = ""; 
browsers[2].regex_not = ""; 
browsers[3].regex_not = "(?i)android|arora|chrome|shiira"; 
browsers[4].regex_not = "(?i)webtv|omniweb|opera"; 
browsers[0].regex_version = "(?i)firefox/([\\d\\.]*)"; 
browsers[1].regex_version = "(?i)opera/([\\d\\.]*)"; 
browsers[2].regex_version = "(?i)chrome/([\\d\\.]*)"; 
browsers[3].regex_version = "(?i)version/([\\d\\.]*)"; 
browsers[4].regex_version = "(?i)msie([+_ ]|)([\\d\\.]*)"; 
browsers[0].regex_group = 1; 
browsers[1].regex_group = 1; 
browsers[2].regex_group = 1; 
browsers[3].regex_group = 1; 
browsers[4].regex_group = 2; 
Dictionary<string, int> browser_counts = new Dictionary<string, int>(); 
for (int i = 0; i < 65000; i++) 
{ 
    foreach (Browser b in browsers) 
    { 
     if (Regex.IsMatch(csUserAgent[i], b.regex_match)) 
     { 
      if (b.regex_not != "") 
      { 
       if (Regex.IsMatch(csUserAgent[i], b.regex_not)) 
       { 
        continue; 
       } 
      } 
      string strBrowser = b.name; 
      if (b.regex_version != "") 
      { 
       string strVersion = Regex.Match(csUserAgent[i], b.regex_version).Groups[b.regex_group].Value; 
       int intPeriod = strVersion.IndexOf('.'); 
       if (intPeriod > 0) 
       { 
        strBrowser += " " + strVersion.Substring(0, intPeriod); 
       } 
      } 
      if (!browser_counts.ContainsKey(strBrowser)) 
      { 
       browser_counts.Add(strBrowser, 1); 
      } 
      else 
      { 
       browser_counts[strBrowser]++; 
      } 
      break; 
     } 
    } 
} 

答えて

3

あなたは可能性があり

  • ハッシュテーブルを構築するか、最も頻繁に一致するユーザエージェントを作成し、正規表現のマッチングを避けます。

  • 店単一の正規表現に正規表現を組み合わせて、代わりにIsMatchで二回(一回マッチングのRegexOptions.CompiledとRegexOptions.CultureInvariantIgnoreCaseの

  • を活用するRegex(pattern, RegexOptions.Compiled)新しいだけではなくpattern

  • をコンパイルし、 (Matchesで1回)が一度一致し(Matches)、MatchCollectionが空であるかどうかを確認します。

これが唯一の出発点である - 私は、コードを読んでより多くのアイデアを思い付くかもしれません:)

編集つ以上:他の正規表現とバージョンを解析

  • 避ける - 唯一のSafariは、特別な必要があなたの設定に応じてtreaetment。 browseridと同じ正規表現でバージョンを 'キャッチ'してみてください。例えば

(私は単に今のサファリのための例外を作ると思います)あなたが便利match.Groups["browserid"]match.Groups["version"]を使用して、適切なサブグループにアクセスすることができます

private static readonly Regex _regex = new Regex(
    "(?i)" 
    + "(?<browserid>(?:firefox/|opera/|chrome/|chrome/|safari/|msie[+_ ]?))" 
    + "(?<version>[\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 

:あなたはこのような単一の静的正規表現のインスタンスを持つことができます。これにより、ブラウザ構造体のリストの使用がほぼすべて排除されます。

これは、除外正規表現(regex_not)だけです。私は最初に単一の正の正規表現を使って再プロファイリングすることを提案し、小さな魚を揚げる前にまだパフォーマンスの問題が残っているかどうかを確認します。

ベンチマーク

ベンチマーク(下記参照)を書きました。私は緩やかな興味までこれを段階的に更新しています:)(私のデータセットが代表ではないことは分かっています。あなたは、ファイルをアップロードした場合、私はそのでそれをテストします)

単一静的にコンパイル正規表現で別の正規表現を置き換える
  1. 、14Sから2.1s(6倍の高速化)にスピードアップします。 (それが理にかなっているので、私は、実際のマッチングいるUserAgentを持っていない)これは、最も外側の試合であるだけでは、プリコンパイルされた正規表現によってregex_not/regex_versionは私のテストセットとの違いの多くをしなかった交換

  2. を置き換えます

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


public class Program 
{ 
    private struct Browser 
    { 
     public int ID; 
     public string name; 
     public Regex regex_match, regex_not, regex_version; 
     public int regex_group; 
    } 

    private static readonly Regex _regex = new Regex("(?i)" 
     + "(?<browserid>(?:firefox/|opera/|chrome/|chrome/|safari/|msie[+_ ]?))" 
     + "(?<version>[\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 

    public static void Main(string[] args) 
    { 

     Browser[] browsers = new Browser[5]; 
     for (int i = 0; i < 5; i++) 
     { 
      browsers[i].ID = i; 
     } 
     browsers[0].name = "Firefox"; 
     browsers[1].name = "Opera"; 
     browsers[2].name = "Chrome"; 
     browsers[3].name = "Safari"; 
     browsers[4].name = "Internet Explorer"; 
     browsers[0].regex_match = new Regex("(?i)firefox/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[1].regex_match = new Regex("(?i)opera/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[2].regex_match = new Regex("(?i)chrome/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[3].regex_match = new Regex("(?i)safari/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[4].regex_match = new Regex("(?i)msie([+_ ]|)([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     // OPTIMIZATION #2 
     browsers[0].regex_not = new Regex("(?i)flock", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[1].regex_not = null; 
     browsers[2].regex_not = null; 
     browsers[3].regex_not = new Regex("(?i)android|arora|chrome|shiira", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[4].regex_not = new Regex("(?i)webtv|omniweb|opera", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     // OPTIMIZATION #2 
     browsers[0].regex_version = new Regex("(?i)firefox/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[1].regex_version = new Regex("(?i)opera/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[2].regex_version = new Regex("(?i)chrome/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[3].regex_version = new Regex("(?i)version/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[4].regex_version = new Regex("(?i)msie([+_ ]|)([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     browsers[0].regex_group = 1; 
     browsers[1].regex_group = 1; 
     browsers[2].regex_group = 1; 
     browsers[3].regex_group = 1; 
     browsers[4].regex_group = 2; 
     Dictionary<string, int> browser_counts = new Dictionary<string, int>(); 

     var lookupBrowserId = new Dictionary<string, int> { 
      { "firefox/", 0 }, 
      { "opera/", 1 }, 
      { "chrome/", 2 }, 
      { "safari/", 3 }, 
      { "msie+", 4 }, 
      { "msie_", 4 }, 
      { "msie ", 4 }, 
      { "msie", 4 }, 
     }; 

     for (int i=1; i<20; i++) 
     foreach (var line in System.IO.File.ReadAllLines("/etc/dictionaries-common/words")) 
     { 
      // OPTIMIZATION #1 START 
      Match match = _regex.Match(line); 

      { 
       if (match.Success) 
       { 
        Browser b = browsers[lookupBrowserId[match.Groups["browserid"].Value]]; 
        // OPTIMIZATION #1 END 

        // OPTIMIZATION #2 
        if (b.regex_not != null && b.regex_not.IsMatch(line)) 
          continue; 

        string strBrowser = b.name; 
        if (b.regex_version != null) 
        { 
         // OPTIMIZATION #2 
         string strVersion = b.regex_version.Match(line).Groups[b.regex_group].Value; 
         int intPeriod = strVersion.IndexOf('.'); 
         if (intPeriod > 0) 
         { 
          strBrowser += " " + strVersion.Substring(0, intPeriod); 
         } 
        } 
        if (!browser_counts.ContainsKey(strBrowser)) 
        { 
         browser_counts.Add(strBrowser, 1); 
        } 
        else 
        { 
         browser_counts[strBrowser]++; 
        } 
        break; 
       } 
      } 
     } 
    } 
} 
+0

は、組み合わされたプリコンパイルされた正規表現を書きました。静的なクラスメンバーにします。それは桁違いに速くなければなりません(特に、IsMatch/Matchesで2回のマッチングを避けた場合) – sehe

+0

heh。私はマークアップの失敗を修正しました。それは2011-09-13に明らかに起こりました(マークダウンの実装がその後変更されていない限り...) – sehe

関連する問題