【问题标题】:In C#, Loading large file into winform richtextbox在 C# 中,将大文件加载到 winform Richtextbox
【发布时间】:2015-07-03 22:05:00
【问题描述】:

我需要将一个 10MB 范围的文本文件加载到 Winform RichTextBox 中,但我当前的代码正在冻结 UI。我尝试让后台工作人员进行加载,但这似乎也不太好。

这是我尝试过的几个加载代码。有什么方法可以提高它的性能吗?谢谢。

    private BackgroundWorker bw1;
    private string[] lines;
    Action showMethod;
    private void button1_Click(object sender, EventArgs e)
    {
        bw1 = new BackgroundWorker();
        bw1.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw1.RunWorkerCompleted += bw_RunWorkerCompleted;
        string path = @"F:\DXHyperlink\Book.txt";
        if (File.Exists(path))
        {
            string readText = File.ReadAllText(path);
            lines = readText.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            bw1.RunWorkerAsync();

        }
    }

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        Invoke((ThreadStart)delegate()
        {
            for (int i = 0; i < lines.Length; i++)
            {
                richEditControl1.Text += lines[i] + "\n";
            }
        });
    }

我也试试:

Action showMethod = delegate()
            {
                for (int i = 0; i < lines.Length; i++)
            {
                richEditControl1.Text += lines[i] + "\n";
            }
            };

【问题讨论】:

  • 为什么要逐行添加文本?​​??这势必比在一个命令中设置 Text 或 Rtf 慢 LOT
  • 因为我想专门在每一行上创建超链接。
  • 至少使用 AppendText 而不是连接到 previuos Text。
  • 这是一个崇高的事业(即使你隐藏得很好,我仍然看不到你这样做)但没有理由这样做。在将其添加到 RTB 之前准备好一切!为了准备,请使用 StringBuilder!!
  • 重新考虑您的用户界面。没有人可以使用 TextBox 中的 10 MB 文本。围绕某人想要完成的内容重新设计您的 UI。

标签: c# winforms richtextbox backgroundworker


【解决方案1】:

这是关于你如何调用 UI 更新的,请查看下面的AppendText

private BackgroundWorker bw1;

private void button1_Click(object sender, EventArgs e)
{
    bw1 = new BackgroundWorker();
    bw1.DoWork += new DoWorkEventHandler(bw_DoWork);
    bw1.RunWorkerCompleted += bw_RunWorkerCompleted;
    bw1.RunWorkerAsync();
}

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    string path = @"F:\DXHyperlink\Book.txt";
    if (File.Exists(path))
    {
        string readText = File.ReadAllText(path);
        foreach (string line in readText.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
        {
            AppendText(line);
            Thread.Sleep(500);
        }
    }
}

private void AppendText(string line)
{
    if (richTextBox1.InvokeRequired)
    {
        richTextBox1.Invoke((ThreadStart)(() => AppendText(line)));
    }
    else
    {
        richTextBox1.AppendText(line + Environment.NewLine);
    }
}

此外,读取整个文件文本的效率非常低。我宁愿逐块阅读并更新 UI。即

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    string path = @"F:\DXHyperlink\Book.txt";
    const int chunkSize = 1024;
    using (var file = File.OpenRead(path))
    {
        var buffer = new byte[chunkSize];
        while ((file.Read(buffer, 0, buffer.Length)) > 0)
        {
            string stringData = System.Text.Encoding.UTF8.GetString(buffer);

            AppendText(string.Join(Environment.NewLine, stringData.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)));
        }
    }
}

【讨论】:

  • 感谢您的大力支持,再次感谢您对我的帮助。
【解决方案2】:

You don't want to concatenate strings in a loop.

System.String 对象是不可变的。当两个字符串 连接起来,就会创建一个新的 String 对象。迭代字符串 连接会创建多个未引用且必须 被垃圾收集。为了获得更好的性能,请使用 System.Text.StringBuilder 类。

下面的代码效率很低:

for (int i = 0; i < lines.Length; i++)
{
    richEditControl1.Text += lines[i] + "\n";
}

试试吧:

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    // Cpu intensive work happens in the background thread.
    var lines = string.Join("\r\n", lines);

    // The following code is invoked in the UI thread and it only assigns the result.
    // So that the UI is not blocked for long.
    Invoke((ThreadStart)delegate()
    {
        richEditControl1.Text = lines;
    });
}

【讨论】:

  • 它不能解决我的问题。它的工作原理和我的代码一样。
  • @AhmerAliAhsan 当文本加载到richtextbox 时,UI 是否又开始正常工作了,还是仍然不是很灵敏?
  • 独立于所选解决方案,将richEditControl1.Text += s 替换为richEditControl1。 追加文本.
  • 我想对您的一致支持表示衷心的感谢。
【解决方案3】:

为什么要分割线并重新加入它们?

字符串是immutable 女巫意味着无法更改。因此,每次您执行Text+= "..." 时,都必须创建新字符串并将其放入文本中。所以对于 10 mb 的字符串,它不是理想的方式,它可能需要几个世纪才能完成巨大的字符串的任务。

你可以看到What is the difference between a mutable and immutable string in C#?

如果您真的想拆分他们并再次加入他们。那么 StringBuilder 是您的正确选择。

        StringBuilder strb = new StringBuilder();

        for (int i = 0; i < lines.Length; i++)
        {
            strb.Append(lines[i] + "\n");
        }

        richEditControl1.Text = strb.ToString();

你可以看到String vs. StringBuilder

StringBuilder 的结构是字符列表。 StringBuilder 也是可变的。手段可以改变。

在循环内部,您可以使用字符串执行任何额外任务并将最终结果添加到 StringBuilder。最后在循环之后你的 StringBuilder 准备好了。您必须将其转换为字符串并将其放入文本中。

【讨论】:

    【解决方案4】:

    我花了一段时间才确定这个..

    测试一和二:

    首先我创建了一些干净的数据:

    string l10 = " 123456789";
    string l100 = l10 + l10 + l10 + l10 + l10 + l10 + l10 + l10 +l10 + l10;
    string big = "";
    StringBuilder sb = new StringBuilder(10001000);
    for (int i = 1; i <= 100000; i++)
        // this takes 3 seconds to load
        sb.AppendLine(i.ToString("Line 000,000,000 ") + l100 + " www-stackexchange-com "); 
         // this takes 45 seconds to load !!
        //sb.AppendLine(i.ToString("Line 000,000,000 ") + l100 + " www.stackexchange.com "); 
    big = sb.ToString();
    
    Console.WriteLine("\r\nStringLength: " + big.Length.ToString("###,###,##0") + "  ");
    
    richTextBox1.WordWrap = false;
    richTextBox1.Font = new System.Drawing.Font("Consolas", 8f);
    richTextBox1.AppendText(big);
    Console.WriteLine(richTextBox1.Text.Length.ToString("###,###,##0") + " chars in RTB");
    Console.WriteLine(richTextBox1.Lines.Length.ToString("###,###,##0") + " lines in RTB ");
    

    在大约 14MB 中显示 100k 行需要 2-3 秒或 45-50 秒。

    将行数增加到 500k 行会使正常的文本加载时间增加到大约 15-20 秒,而在每行末尾包含(有效)链接的版本会增加到几分钟。

    当我转到 1M 行时,VS 加载会崩溃。

    结论

    • 加载包含链接的文本需要 10 倍以上的时间,在此期间 UI 冻结。

    • 加载 10-15MB 的文本数据本身并不是真正的问题。

    测试三:

    string bigFile = File.ReadAllText("D:\\AllDVDFiles.txt");
    richTextBox1.AppendText(bigFile);
    

    (这实际上是我调查的开始。)这会尝试加载一个 8 MB 的大文件,其中包含来自大量数据 DVD 的目录和文件信息。并且:它也冻结

    正如我们所见,文件大小不是问题。也没有嵌入任何链接。

    乍一看,原因是某些文件名中有有趣的字符。将文件保存为 UTF8 并将读取命令更改为..:

    string bigFile = File.ReadAllText("D:\\AllDVDFiles.txt", Encoding.UTF8);
    

    ..文件在 1-2 秒内加载就好了,正如预期的那样..

    最终结论:

    • 您需要注意错误的编码,因为这些字符可能会在加载期间冻结 RTB。
    • 而且,当您添加 链接 时,您必须预计加载时间会比纯文本长很多 (10-20x)。我试图通过准备 Rtf 字符串来欺骗 RTB,但它没有帮助。似乎分析和存储所有这些链接总是需要很长时间。

    所以:如果您真的需要在每一行都添加一个链接,请将数据分区成更小的部分,并为用户提供一个界面来滚动和搜索这些部分。

    当然,将所有这些行一一添加总是太慢了,但这已经在 cmets 和其他答案中提到了。

    【讨论】:

    • 我想对您的一致支持表示衷心的感谢。
    猜你喜欢
    • 2021-09-04
    • 2011-06-29
    • 1970-01-01
    • 2018-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多