c# - 有更快的方法来突出显示正则表达式匹配(RichTextBox)

问题描述 投票:3回答:3

我使用RichTextBox来测试REGEX表达式,代码如下:

rtbMain.SelectAll();
rtbMain.SelectionColor = Color.Black;
rtbMain.SelectionBackColor = Color.White;
Regex regex = new Regex(txtRegexPattern.Text, regexOptions);
Match matches = regex.Match(txtTest.Text);
while (matches.Success)
{
    rtbMain.Select(matches.Index, match.Length);
    rtbMain.SelectionColor = Color.Red;
    rtbMain.SelectionBackColor = Color.Black;
}

但是,只要突出显示超过几千(1000+)个字符,此方法就会变得太慢。我知道我可以延迟处理,所以代码让用户有机会输入整个正则表达式,但我认为RichTextBox突出显示工作太慢了。

我在谷歌搜索了加快当前解决方案的不同方法和方法,但我没有运气。我注意到有一些文本编辑器允许“语法高亮”(如ScintillNET,Avalon,...)但他们使用XML作为输入,所以我认为将它们用于我的项目(在每个KeyUp事件上生成XML)将不会是“最佳实践”。

我在这里找到并测试了一个“快速彩色文本框”:https://github.com/PavelTorgashov/FastColoredTextBox ......但是这个问题是它在使用自己的新行和制表符时替换了粘贴内容,我不能在REGEX测试器中使用它。

有没有更快的方法来突出显示所有匹配,可能使用不同的用户控件?

编辑:

方法1:生成基础RTF文档会更快吗?我试过但是在使用特殊字符方面遇到了一些问题,所以我可以测试整个文档的突出显示,但它似乎在一行中使用普通字符的速度非常快。我暂停工作,因为我读到构建RTF可能很难,我想我不能使用现有的RTF库。

方法2:我只能获得RichTextBox的显示部分,所以我想只强调那部分。我想这会显着减少处理(取决于RTB大小),但每次用户滚动时我都需要触发突出显示;我不确定这会运作良好并创造一个体面的用户体验,所以还没有尝试过。

有人会推荐上述任何方法或其他任何方法吗?

c# winforms performance richtextbox highlight
3个回答
2
投票

第一:

RichTextBox有一个固有的问题:它在.NET中非常慢。我找到了一个解决方案,如何使它快120倍。可能你试试看:C# RichEditBox has extremely slow performance (4 minutes loading) SOLVED

第二:

从头开始构建RTF代码是最快的解决方案。看看我在codeproject上的文章。有一个可重用的RTF构建器类:http://www.codeproject.com/Articles/23513/SQL-Editor-for-Database-Developers


1
投票

请在http://www.codeproject.com/Articles/3669/Expresso-A-Tool-for-Building-and-Testing-Regular-E查看Expresso

多年来我一直在使用这个程序来编辑和评估正则表达式。


1
投票

我怀疑你是否以错误的方式设置了While循环。

尝试这样的事情:(未经测试,但会让你知道如何解决这个问题)

rtbMain.SelectAll();
rtbMain.SelectionColor = Color.Black;
rtbMain.SelectionBackColor = Color.White;
Regex regex = new Regex(txtRegexPattern.Text, regexOptions);
MatchCollection matches = regex.Matches(txtTest.Text);

if(matches.Count > 0)
{
   foreach(Match m in matches)
   {
      rtbMain.Select(m.Index, m.Length);
      rtbMain.SelectionColor = Color.Red;
      rtbMain.SelectionBackColor = Color.Black;
   }
}
else
{
   Debug.Print("No matches found"); // See "Output" Window
}

编辑

我做了一些与突出显示RTF文本相关的解决方法,我发现的第一件事是这个过程主要花费的时间是这些行:

  rtbMain.SelectionColor = Color.Red;
  rtbMain.SelectionBackColor = Color.Black;

我尝试使用SelectionStartSelectionEnd属性而不是.Select()选择文本,但是没有观察到任何变化。

关于你构建等效RTF的第一点,我也试过了,但很难构造一个等价的RTF,因为那里有很多需要处理的东西。如果可以完成,则对于超过31k的匹配(对特定样本进行基本测试的结果),处理时间将约为<1.5秒。

所以,我建议你通过THREADING和分割任务分为两个主题:

这是一个示例源代码:(对于最坏的情况,我发现大约31341个匹配,并且过程需要4秒才能突出显示)

    // declare variables either globally or in the same method
    MatchCollection mcoll;
    Stopwatch s;
    int callbackCount = 0;
    List<Match> m1 = null;
    List<Match> m2 = null;

    private void btnHighlight_Click(object sender, EventArgs e)
    {
        //reset any exisiting formatting
        rtbMain.SelectAll();
        rtbMain.SelectionBackColor = Color.White;
        rtbMain.SelectionColor = Color.Black;
        rtbMain.DeselectAll();

        s = new Stopwatch();
        s.Start();

        Regex re = new Regex(@"(.)", RegexOptions.Compiled); // Notice COMPILED option
        mcoll = re.Matches(rtbMain.Text);

        // Break MatchCollection object into List<Matches> which is exactly half in size
        m1 = new List<Match>(mcoll.Count / 2);
        m2 = new List<Match>(mcoll.Count / 2);

        for (int k = 0; k < mcoll.Count; k++)
        {
            if (k < mcoll.Count / 2)
                m1.Add(mcoll[k]);
            else
                m2.Add(mcoll[k]);
        }

        Thread backgroundThread1 = new Thread(new ThreadStart(() => {
            match1(null, null);
        }));
        backgroundThread1.Start();

        Thread backgroundThread2 = new Thread(new ThreadStart(() =>
        {
            match2(null, null);
        }));
        backgroundThread2.Start();
    }

    public void match1(object obj, EventArgs e)
    {
        for (int i=0; i < m1.Count; i += 1)
        {
            if (rtbMain.InvokeRequired)
            {
                EventHandler d = new EventHandler(match1);
                rtbMain.Invoke(d);
            }
            else
            {
                rtbMain.Select(m1[i].Index, m1[i].Length);
                rtbMain.SelectionBackColor = Color.Black;
                rtbMain.SelectionColor = Color.Red;
            }
        }
        stopTimer();
    }

    public void match2(object obj, EventArgs e)
    {
        for (int j=0; j < m2.Count; j += 1)
        {
            if (rtbMain.InvokeRequired)
            {
                EventHandler d = new EventHandler(match2);
                rtbMain.Invoke(d);
            }
            else
            {
                rtbMain.Select(m2[j].Index, m2[j].Length);
                rtbMain.SelectionBackColor = Color.Black;
                rtbMain.SelectionColor = Color.Red;
            }
        }
        stopTimer();
    }

    void stopTimer()
    {
        callbackCount++;

        if (callbackCount == 2) // 2 because I am using two threads.
        {
            s.Stop();
            // Check Output Window
            Debug.Print("Evaluated in : " + s.Elapsed.Seconds.ToString());
        }
    }

因为你发布它需要大约30秒来操作,希望4秒是可以忍受的,用户可以通过一些加载屏幕参与,因为其他在线转换器像RubularDerekSlager's .Net regex tester那样。

别忘了看看Why Regex.Compiled preferred

© www.soinside.com 2019 - 2024. All rights reserved.