正则表达式拖慢程序 [英] Regular Expressions slowing down the program

查看:138
本文介绍了正则表达式拖慢程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个从游戏中的聊天记录分析数据的程序。到目前为止,我已经设法让程序工作,分析我想要的数据,但我的问题是,该方案被越来越慢。



目前需要5秒解析一个10MB的文本文件,我注意到它滴下来3秒,如果我加RegexOptions.Compiled我的正则表达式。



我相信我已经查明的问题我正则表达式匹配。一号线正在阅读,因为5个正则表达式的5倍,所以当我添加更多的项目会得到更慢。



我应该怎么做,所以我的计划就不会放慢来与多个正则表达式?所有建议,使代码更是值得赞赏的!



 如果(sender.Equals(ButtonParse))
{
VAR totalShots = 0F;
VAR totalHits = 0F;
变种错过= 0;
无功暴击= 0;

变种regDmg =新的正则表达式(@(小于?= \bSystem\b *您造成的)\d + .\d。RegexOptions.Compiled);
变种regMiss =新的正则表达式(@(小于?= \bSystem\b *目标回避攻击),RegexOptions.Compiled);
变种regCrit =新的正则表达式(@(小于= \bSystem\b *重击 - 额外伤害)?,RegexOptions.Compiled);
变种regHeal =新的正则表达式(@(小于?= \bSystem\b *您自己痊愈)\d + .\d。RegexOptions.Compiled);
变种regDmgrec =新的正则表达式(@(小于?= \bSystem\b *你拿)\d + .\d。RegexOptions.Compiled);

变种dmgList =新的List<浮动>(); //新名单损伤值
变种healList =新的List<浮动>(); //新名单医治值
变种dmgRecList =新的List<浮动>(); //新的列表,使用收到的伤害值

(VAR SR =新的StreamReader(TextBox1.Text))
{
,而(!sr.EndOfStream)
{
无功行= sr.ReadLine();

VAR匹配= regDmg.Match(线);
变种match2 = regMiss.Match(线);
变种match3 = regCrit.Match(线);
变种match4 = regHeal.Match(线);
变种match5 = regDmgrec.Match(线);

如果(match.Success)
{
dmgList.Add(float.Parse(match.Value,CultureInfo.InvariantCulture));
totalShots ++;
totalHits ++;
}
如果(match2.Success)
{
遗漏++;
totalShots ++;
}
如果(match3.Success)
{
暴击++;
}
如果(match4.Success)
{
healList.Add(float.Parse(match4.Value,CultureInfo.InvariantCulture));
}
如果(match5.Success)
{
dmgRecList.Add(float.Parse(match5.Value,CultureInfo.InvariantCulture));
}
}
TextBlockTotalShots.Text = totalShots.ToString(); //显示总出手
TextBlockTotalDmg.Text = dmgList.Sum()的ToString(0 ##)。 //显示总伤害造成

TextBlockTotalHits.Text = totalHits.ToString(); //显示总点击数
VAR hitChance = totalHits / totalShots; //计算命中率
TextBlockHitChance.Text = hitChance.ToString(P); //显示命中率

TextBlockTotalMiss.Text = misses.ToString(); //显示总惦记
VAR missChance =遗漏/ totalShots; //计算命中几率
TextBlockMissChance.Text = missChance.ToString(P); //显示命中几率

TextBlockTotalCrits.Text = crits.ToString(); //显示总暴击
VAR critChance =暴击/ totalShots; //计算暴击几率
TextBlockCritChance.Text = critChance.ToString(P); //显示致命一击几率

TextBlockDmgHealed.Text = healList.Sum()的ToString(F1)。 //显示损伤愈合

TextBlockDmgReceived.Text = dmgRecList.Sum()的ToString(F1)。 //显示收到的伤害

VAR pedSpent = dmgList.Sum()/(float.Parse(TextBoxEco.Text,CultureInfo.InvariantCulture)* 100); //计算PED花了
TextBlockPedSpent.Text = pedSpent.ToString +PED(0 ##); //估计PED花了
}
}

这是一个示例文本:

  2014年9月2日23时07分22秒[系统] []你造成伤害的45.2点。 
2014年9月2日23时07分23秒[系统] []你造成伤害的45.4点。
2014年9月2日23时07分24秒[系统] []目标回避攻击。
2014年9月2日23时07分25秒[系统] []你造成伤害的48.4点。
2014年9月2日23时07分26秒[系统] []你造成伤害的48.6点。
二○一四年十月一十五日12时39分55秒[系统] []目标回避攻击。
二○一四年十月一十五日十二时39分58秒[系统] []你造成伤害的56.0点。
二○一四年十月一十五日12时39分59秒[系统] []你造成伤害的74.6点。
二○一四年十月一十五日12时40分02秒[系统] []你造成伤害的78.6点。
二○一四年十月一十五日12点40分04秒[系统] []目标回避攻击。
二○一四年十月一十五日十二时四十分06秒[系统] []你造成伤害的66.9点。
二○一四年十月一十五日十二点40分08秒[系统] []你造成伤害的76.2点。
二○一四年十月一十五日十二时40分12秒[系统] []你受到伤害的18.4分。
二○一四年十月一十五日12点40分14秒[系统] []你造成伤害的76.1点。
二○一四年十月一十五日12时40分十七秒[系统] []你造成伤害的88.5点。
二○一四年十月一十五日12时40分十九秒[系统] []你造成伤害的69.0点。
2014年10月19日5时56分30秒[系统] []致命一击 - 额外伤害!你造成伤害的275.4点。
2014年10月19日5时59分29秒[系统] []你造成伤害的92.8点。
2014年10月19日5时59分31秒[系统] []致命一击 - 额外伤害!你造成伤害的251.5点。
2014年10月19日5时59分35秒[系统] []你受到伤害的59.4点。
2014年10月19日5点59分39秒[系统] []你治好自己84.0分。


解决方案

下面是问题,因为我看到它。




  1. 正如意见建议不具备正则表达式解析器工作方式太多了基本模式的情况。

  2. 为什么数据进行多次分析同一文本?创建一个正则表达式来完成所有的工作,一个扫描过的每一行。

  3. 在WPF撑不起来的GUI线程做的工作,做的工作在后台任务和更新一个视图模型(你使用MVVM吧?),这将传播信息使用INotifyPropertyChanged的事件屏幕。



下面是一正则表达式的解决方案,由线的基础线工作。它的第一个任务是验证 [系统] 包含就行了。如果不是,它在该行中没有匹配。如果它确实有系统,那么它寻找特定关键字和可能的值,并在键/值对形势它们放入正则表达式命名比赛捕获



在正在使用LINQ将总结中发现的值来实现。请注意,我有评论的模式,有正则表达式解析器忽略它。

 字符串模式= @^#线年初至固定它。
(?= + \ [System\])#在这几行文字[系统]'有发生
(?= +#的地方,行搜索中这些关键字:
(<作用>#命名匹配捕获组行动将举行一个关键字
inflicte D#如果线路已造成或造成放入'行动'$ b。? $ b |#或
#回避回避
|采取#或采取
|#你自己或你自己(治愈)

(\s(LT?;价值与GT; [\d] +)))#如果点的存在的价值的地方变成'价值'
+#匹配一个或多个来完成它行
$ #END来。停止;

// IgnorePatternWhiteSpace只允许我们发表评论的格局。不影响处理。
变种令牌=
Regex.Matches(数据,模式,RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline)
.OfType<匹配和GT;()
。选择(MT =>新建{
=行动mt.Groups [行动]。价值
值= mt.Groups [值。成功?double.Parse(mt.Groups [值。值) :0,
计数= 1,
})
.GroupBy(ITM = GT; itm.Action,//每一个动作都将被总结为
ITM分为它的名字= > ITM,//这是价值总结跻身组的个别项目
(动作值)=>新的
{
行动=行动,
计数= values.Sum(ITM = GT; itm.Count),
总= values.Sum(ITM = GT; itm.Value)
}
);



结果



LINQ的结果返回各。的令牌作为总结了所有的值的行动,而且计数的次发生的那些动作的数目的实体





DATA



 字符串数据= @2014年9月2日23时07分22秒[系统] []您造成的伤害。
45.2点,2014年9月2日23 :07:23 [系统] []您造成损坏
45.4点,2014年9月2日23时07分24秒[系统] []目标回避攻击
2014年9月2日23: 7点25分[系统] []您造成的伤害。
48.4点,2014年9月2日23时07分26秒[系统] []您造成的伤害。
二〇一四年十月一十五日48.6点12点39分55秒[系统] []目标回避攻击
2014年10月15日12时39分58秒[系统] []您造成的伤害。
56.0点,2014年10月15日12 :39:59 [系统] []您造成损坏
74.6点,2014年10月15日十二时40分02秒[系统] []你造成伤害的78.6点。
二○一四年十月一十五日12点40分04秒[系统] []目标回避攻击。
二○一四年十月一十五日十二时四十分06秒[系统] []你造成伤害的66.9点。
二○一四年十月一十五日十二点40分08秒[系统] []你造成伤害的76.2点。
二○一四年十月一十五日十二时40分12秒[系统] []你受到伤害的18.4分。
二○一四年十月一十五日12点40分14秒[系统] []你造成伤害的76.1点。
二○一四年十月一十五日12时40分十七秒[系统] []你造成伤害的88.5点。
二○一四年十月一十五日12时40分十九秒[系统] []你造成伤害的69.0点。
2014年10月19日5时56分30秒[系统] []致命一击 - 额外伤害!你造成伤害的275.4点。
2014年10月19日5时59分29秒[系统] []你造成伤害的92.8点。
2014年10月19日5时59分31秒[系统] []致命一击 - 额外伤害!你造成伤害的251.5点。
2014年10月19日5时59分35秒[系统] []你受到伤害的59.4点。
2014年10月19日5点59分39秒[系统] []你治好自己84.0点;


I'm trying to create a program that parses data from game's chat log. So far I have managed to get the program to work and parse the data that I want but my problem is that the program is getting slower.

Currently it takes 5 seconds to parse a 10MB text file and I noticed it drops down to 3 seconds if I add RegexOptions.Compiled to my regex.

I believe I have pinpointed the problem to my regex matches. One line is currently read 5 times because of the 5 regexes so the program would get even slower when I add more later.

What should I do so my program would not slow down with multiple regexes? All suggestions to make the code better are appreciated!

if (sender.Equals(ButtonParse))
        {
            var totalShots = 0f;
            var totalHits = 0f;
            var misses = 0;
            var crits = 0;

            var regDmg = new Regex(@"(?<=\bSystem\b.* You inflicted )\d+.\d", RegexOptions.Compiled);
            var regMiss = new Regex(@"(?<=\bSystem\b.* Target evaded attack)", RegexOptions.Compiled);
            var regCrit = new Regex(@"(?<=\bSystem\b.* Critical hit - additional damage)", RegexOptions.Compiled);
            var regHeal = new Regex(@"(?<=\bSystem\b.* You healed yourself )\d+.\d", RegexOptions.Compiled);
            var regDmgrec = new Regex(@"(?<=\bSystem\b.* You take )\d+.\d", RegexOptions.Compiled);

            var dmgList = new List<float>(); //New list for damage values
            var healList = new List<float>(); //New list for heal values
            var dmgRecList = new List<float>(); //New list for damage received values

            using (var sr = new StreamReader(TextBox1.Text))
            {
                while (!sr.EndOfStream)
                {
                    var line = sr.ReadLine();

                    var match = regDmg.Match(line);
                    var match2 = regMiss.Match(line);
                    var match3 = regCrit.Match(line);
                    var match4 = regHeal.Match(line);
                    var match5 = regDmgrec.Match(line);

                    if (match.Success)
                    {
                        dmgList.Add(float.Parse(match.Value, CultureInfo.InvariantCulture));
                        totalShots++;
                        totalHits++;
                    }
                    if (match2.Success)
                    {
                        misses++;
                        totalShots++;
                    }
                    if (match3.Success)
                    {
                        crits++;
                    }
                    if (match4.Success)
                    {
                        healList.Add(float.Parse(match4.Value, CultureInfo.InvariantCulture));
                    }
                    if (match5.Success)
                    {
                        dmgRecList.Add(float.Parse(match5.Value, CultureInfo.InvariantCulture));
                    }
                }
                TextBlockTotalShots.Text = totalShots.ToString(); //Show total shots
                TextBlockTotalDmg.Text = dmgList.Sum().ToString("0.##"); //Show total damage inflicted

                TextBlockTotalHits.Text = totalHits.ToString(); //Show total hits
                var hitChance = totalHits / totalShots; //Calculate hit chance
                TextBlockHitChance.Text = hitChance.ToString("P"); //Show hit chance

                TextBlockTotalMiss.Text = misses.ToString(); //Show total misses
                var missChance = misses / totalShots; //Calculate miss chance
                TextBlockMissChance.Text = missChance.ToString("P"); //Show miss chance

                TextBlockTotalCrits.Text = crits.ToString(); //Show total crits
                var critChance = crits / totalShots; //Calculate crit chance
                TextBlockCritChance.Text = critChance.ToString("P"); //Show crit chance

                TextBlockDmgHealed.Text = healList.Sum().ToString("F1"); //Show damage healed

                TextBlockDmgReceived.Text = dmgRecList.Sum().ToString("F1"); //Show damage received

                var pedSpent = dmgList.Sum() / (float.Parse(TextBoxEco.Text, CultureInfo.InvariantCulture) * 100); //Calculate ped spent
                TextBlockPedSpent.Text = pedSpent.ToString("0.##") + " PED"; //Estimated ped spent
            }
        }

And here's a sample text:

2014-09-02 23:07:22 [System] [] You inflicted 45.2 points of damage.
2014-09-02 23:07:23 [System] [] You inflicted 45.4 points of damage.
2014-09-02 23:07:24 [System] [] Target evaded attack.
2014-09-02 23:07:25 [System] [] You inflicted 48.4 points of damage.
2014-09-02 23:07:26 [System] [] You inflicted 48.6 points of damage.
2014-10-15 12:39:55 [System] [] Target evaded attack.
2014-10-15 12:39:58 [System] [] You inflicted 56.0 points of damage.
2014-10-15 12:39:59 [System] [] You inflicted 74.6 points of damage.
2014-10-15 12:40:02 [System] [] You inflicted 78.6 points of damage.
2014-10-15 12:40:04 [System] [] Target evaded attack.
2014-10-15 12:40:06 [System] [] You inflicted 66.9 points of damage.
2014-10-15 12:40:08 [System] [] You inflicted 76.2 points of damage.
2014-10-15 12:40:12 [System] [] You take 18.4 points of damage.
2014-10-15 12:40:14 [System] [] You inflicted 76.1 points of damage.
2014-10-15 12:40:17 [System] [] You inflicted 88.5 points of damage.
2014-10-15 12:40:19 [System] [] You inflicted 69.0 points of damage.
2014-10-19 05:56:30 [System] [] Critical hit - additional damage! You inflict 275.4 points of damage.
2014-10-19 05:59:29 [System] [] You inflicted 92.8 points of damage.
2014-10-19 05:59:31 [System] [] Critical hit - additional damage! You inflict 251.5 points of damage.
2014-10-19 05:59:35 [System] [] You take 59.4 points of damage.
2014-10-19 05:59:39 [System] [] You healed yourself 84.0 points.

解决方案

Here are the issues as I see it

  1. As suggested in the comments don't have the regex parser working way too much for a basic pattern situations.
  2. Why parse the data multiple times on the same text? Create one regex pattern to do all the work with one scan over each line.
  3. In WPF don't hold up the GUI thread to do work, do the work in a background task and update a viewmodel (you are using MVVM right?) which will propagate the info to the screen using INotifyPropertyChanged events.

The following is a one regex pattern solution which works on a line by line basis. Its first task is to verify that [System] is contained on the line. If it is not, it does no matching on that line. If it does have system, then it looks for specific keywords and possible values and places them into regex named match captures in a key/value pair situation.

Once that is done using linq it will sum up the values found. Note that I have commented the pattern and had the regex parser ignore it.

string pattern = @"^       # Beginning of line to anchor it.
(?=.+\[System\])           # Within the line a literal '[System]' has to occur
(?=.+                      # Somewhere within that line search for these keywords:
  (?<Action>               # Named Match Capture Group 'Action' will hold a keyword.
          inflicte?d?      # if the line has inflict or inflicted put it into 'Action'
          |                # or
          evaded           # evaded
          | take           # or take
          | yourself       # or yourself (heal)
   )
  (\s(?<Value>[\d.]+))?)   # if a value of points exist place into 'Value'
.+                         # match one or more to complete it.
$                          #end of line to stop on";

 // IgnorePatternWhiteSpace only allows us to comment the pattern. Does not affect processing.
var tokens =
   Regex.Matches(data, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline)
        .OfType<Match>()
        .Select( mt => new {
                            Action = mt.Groups["Action"].Value,
                            Value  = mt.Groups["Value"].Success ? double.Parse(mt.Groups["Value"].Value) : 0,
                            Count  = 1,
                           })
         .GroupBy ( itm => itm.Action,  // Each action will be grouped into its name for summing
                    itm => itm,   // This is value to summed amongst the individual items of the group.
                    (action, values) => new
                            {
                                Action = action,
                                Count  = values.Sum (itm => itm.Count),
                                Total  = values.Sum(itm => itm.Value)
                             }
                         );

Result

The linq result returns each of the tokens as an entity which sums up all the values for the actions, but also counts up the number of times those actions occurred.

DATA

string data=@"2014-09-02 23:07:22 [System] [] You inflicted 45.2 points of damage.
2014-09-02 23:07:23 [System] [] You inflicted 45.4 points of damage.
2014-09-02 23:07:24 [System] [] Target evaded attack.
2014-09-02 23:07:25 [System] [] You inflicted 48.4 points of damage.
2014-09-02 23:07:26 [System] [] You inflicted 48.6 points of damage.
2014-10-15 12:39:55 [System] [] Target evaded attack.
2014-10-15 12:39:58 [System] [] You inflicted 56.0 points of damage.
2014-10-15 12:39:59 [System] [] You inflicted 74.6 points of damage.
2014-10-15 12:40:02 [System] [] You inflicted 78.6 points of damage.
2014-10-15 12:40:04 [System] [] Target evaded attack.
2014-10-15 12:40:06 [System] [] You inflicted 66.9 points of damage.
2014-10-15 12:40:08 [System] [] You inflicted 76.2 points of damage.
2014-10-15 12:40:12 [System] [] You take 18.4 points of damage.
2014-10-15 12:40:14 [System] [] You inflicted 76.1 points of damage.
2014-10-15 12:40:17 [System] [] You inflicted 88.5 points of damage.
2014-10-15 12:40:19 [System] [] You inflicted 69.0 points of damage.
2014-10-19 05:56:30 [System] [] Critical hit - additional damage! You inflict 275.4 points of damage.
2014-10-19 05:59:29 [System] [] You inflicted 92.8 points of damage.
2014-10-19 05:59:31 [System] [] Critical hit - additional damage! You inflict 251.5 points of damage.
2014-10-19 05:59:35 [System] [] You take 59.4 points of damage.
2014-10-19 05:59:39 [System] [] You healed yourself 84.0 points.";

这篇关于正则表达式拖慢程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆