C# 文件的高级读取

C# 文件的高级读取,c#,io,backgroundworker,C#,Io,Backgroundworker,我相信我们都很熟悉,可能会使用书籍、在线等提供的大量代码来使用C#读取文件。简单到 StringBuilder fiContents = new StringBuilder(); using (StreamReader fi = new StreamReader(@"C:\a_file.txt")) { while (!fi.EndOfStream) { fiContents.Append(fi.ReadLine); } } 或者是像…这样短的东西 u

我相信我们都很熟悉,可能会使用书籍、在线等提供的大量代码来使用C#读取文件。简单到

StringBuilder fiContents = new StringBuilder();
using (StreamReader fi = new StreamReader(@"C:\a_file.txt"))
{
    while (!fi.EndOfStream)
    {
        fiContents.Append(fi.ReadLine); 
    }
}
或者是像…这样短的东西

using (StreamReader fi = new StreamReader(@"C:\a_file.txt"))
    fiContents.Append(fi.ReadToEnd());
现在,让我们去Super Saiyan玩一会儿,做一些非常有趣的事情,比如有一个
BackgroundWorker
,它可以让我们显示加载图像(我将使用这个),提供一个进程倒计时计时器或
ProgressBar

public void ReadFile(string filename)
{
    BackgroundWorker procFile = new BackgroundWorker();
    // Progress 1: If we want to show the progress we need to enable the following property
    // procFile.WorkerReportsProgress = true;

    profile.DoWork += new DoWorkEventHandler((object obj, DoWorkEventArgs ev) =>
    {
        StringBuilder fiContents = new StringBuilder();

        using (StreamReader fi = new StreamReader(filename))
        {
            while (!fi.EndOfStream)
            {
                // Progress 2: Report the progress, this will be dealt with by the respective handler (below).
                // procFile.ReportProgress((int)(fi.BaseStream.Length / fi.BaseStream.Position) / 100);

                fiContents.Append(fi.ReadLine);
            }
        }

        ev.Result = fiContents;
    }

    /* Progress 3: The handler below will take care of updating the progress of the file as it's processed. 
    procFile.ProgressChanged += new ProgressChangedEventHandler((object obj, ProgressChangedEventArgs ev) =>
    {
        // Progress 4: Do something with the value, such as update a ProgressBar. 
        // ....
    }
    */

    procFile.RunWorkerCompleted += new RunWorkerCompletedEventHandler((object obj, RunWorkerCompletedEventArgs ev) =>
    {
         // Do something with the result (ev.Result), bearing in mind, it is a StringBuilder and the ev.Result is an object. 
         StringBuilder result = ev.Result as StringBuilder; 

         // ....
    }
}
++++++++++++++++++++++++++

是时候回答实际问题了。。。上述内容是一个热身活动,旨在展示当前的理解水平,因此我不会将其视为未来的答案

我几乎是在做上面给出的最后一个代码示例(即使用
BackgroundWorker
),并将读取的内容转储到
RichTextBox
。简单的东西

然而,我面临的问题是处理大文件(例如~222MB)。该案例只是获取一个.txt,读取它,将通过
StringBuilder
构建的结果推送到
RichTextBox
中。它无法加载文件,我得到一个
OutOfMemoryException
。解决这个问题的一种方法是迭代字符串并从文件
StringBuilder
中添加每个字符(作为
char
),这需要相当多的时间(并且仍然不会加载文件)

public void ReadFile(string filename)
{
    BackgroundWorker procFile = new BackgroundWorker();
    // Progress 1: If we want to show the progress we need to enable the following property
    // procFile.WorkerReportsProgress = true;

    profile.DoWork += new DoWorkEventHandler((object obj, DoWorkEventArgs ev) =>
    {
        StringBuilder fiContents = new StringBuilder();

        using (StreamReader fi = new StreamReader(filename))
        {
            while (!fi.EndOfStream)
            {
                // Progress 2: Report the progress, this will be dealt with by the respective handler (below).
                // procFile.ReportProgress((int)(fi.BaseStream.Length / fi.BaseStream.Position) / 100);

                fiContents.Append(fi.ReadLine);
            }
        }

        ev.Result = fiContents;
    }

    /* Progress 3: The handler below will take care of updating the progress of the file as it's processed. 
    procFile.ProgressChanged += new ProgressChangedEventHandler((object obj, ProgressChangedEventArgs ev) =>
    {
        // Progress 4: Do something with the value, such as update a ProgressBar. 
        // ....
    }
    */

    procFile.RunWorkerCompleted += new RunWorkerCompletedEventHandler((object obj, RunWorkerCompletedEventArgs ev) =>
    {
         // Do something with the result (ev.Result), bearing in mind, it is a StringBuilder and the ev.Result is an object. 
         StringBuilder result = ev.Result as StringBuilder; 

         // ....
    }
}
我总是使用最基本和最直接的方法来读取文件(如上面给出的示例),但是有人对如何改进这一点有任何指导吗?处理超大文件的方法?等等

即使作为一篇讨论文章,我也欢迎你的想法

++++++++++++++++++++++++++

编辑1(@TaW):尝试将
字符串
放入
RichTextBox
时引发异常

FileProcessing.RunWorkerCompleted += new RunWorkerCompletedEventArgs((object obj, RunWorkerCompletedEventArgs e) =>
{
    // 'Code' is the RichTextBox in question...

    Code.Text = "";

    if (e.Result is StringBuilder)
    {
        Code.Text = (e.Result as StringBuilder).ToString();
    }
}
你试过,

它对于处理大文件非常有用

是否有限制要求您使用RichTextBox作为控件来显示内容?此控件未虚拟化,将导致性能(以及从外观上看内存错误)问题


有一系列更适合显示大型文档。根据您的需要存在各种控件(固定、通过页面流动或滚动)。此外,您还可以使用搜索、打印、缩放和其他一些功能查看大型文档。

这不是关于高级阅读,而是关于达到(Winforms)控件的容量限制。也许您可以让它在WPF中工作,但在Winforms中,RichTextBox和TextBox都不能容纳如此多的行/文本


我建议您重新设计它,将数据以较小的块呈现给用户。这并不是说他们想滚动超过100.000行。在内存中处理它们不是问题;这里200MB根本不算大;例如,你可以很容易地在内存中搜索它等等。

就我个人而言,我只使用
File.ReadAllText(filename)
,但我很懒。简单的回答是,要加载一个内存太大的文件,你不能一次加载所有文件。只加载滚动控件中当前显示的部分文件是一种常见的解决方案。读取200MB文件所需的时间应该很短,以至于进度条和后台工作人员的工作量过大。@Will:没问题!;)我认为富文本框不存在虚拟化,因为虚拟化最适合IList,而不是IEnumerable(字符串可以被认为是IEnumerable)。在幕后,虚拟化使用索引器,因此不需要迭代整个集合就可以到达给定位置(我们要显示的部分)。想象一下,尝试在IEnumerable中向后滚动…如果不从头开始,这是不可能的。我实际上正在开发一个脚本编辑器(带有高亮显示和intelli sense),它现在已经完成,并且需要一个RichTextBox,这样我就可以实现高亮显示和其他功能。因此,它们将是纯文本文件。当我通过一个问题(实际上是WinForm控件的局限性)被问到时,我提出的问题是使用C#的文件I/O中的进一步技术和讨论。因此,我提供了许多代码示例,以演示我熟悉的一些技术,以及大多数其他人可能使用的技术。我完全同意你的第二点。