我需要将一个-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";
}
};
答案 0 :(得分:2)
这是关于您如何调用UI更新,请查看<ul>
以下内容。
AppendText
除了阅读之外,整个文件文本的效率非常低。我宁愿通过块读取块并更新UI。即。
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);
}
}
答案 1 :(得分:1)
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;
});
}
答案 2 :(得分:1)
为什么要分割线并再次加入?
字符串immutable
这意味着无法更改。因此,每次执行Text+= "..."
时,都必须创建新字符串并将其放入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();
StringBuilder的结构是字符列表。 StringBuilder也是Muttable。手段可以改变。
Inside Loop,您可以使用字符串执行任何额外任务,并将最终结果添加到StringBuilder。最后循环之后你的StringBuilder就绪了。您必须将其转换为字符串并将其放入文本中。
答案 3 :(得分:0)
我花了一段时间来指出这个......
测试一个&amp;二:强>
首先我创建了一些干净的数据:
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倍以上的时间,并且在此期间用户界面处于冻结状态。
加载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秒内加载就好了。
最终结论:
所以:如果你真的需要在每一行都有一个链接,那么将分区数据分成更小的部分,并为用户提供一个滚动和放大的界面。搜索这些部分。
当然,逐个追加所有这些行总是太慢,但这已经在评论和其他答案中提到了。