如何将大量淫秽数据写入文件

本文关键字:数据 文件 | 更新日期: 2023-09-27 18:25:09

我正在开发一个应用程序,该应用程序从巨大的文本文件(~2.5GB)中读取行,将每行操作为特定格式,然后将每行写入文本文件。一旦输出文本文件关闭,程序"大容量插入"(SQL Server)数据到我的数据库。它起作用,只是很慢。

我正在使用StreamReaderStreamWriter

由于我必须如何处理文本,我几乎只能一次读一行;然而,我认为,如果我收集了一些行,大约每1000行写一次,这至少会加快速度。问题是(这可能纯粹是因为我的无知),我不能使用StreamWriter编写string[]。在探索了StackOverflow和互联网的其他部分后,我发现了File.WriteAllLines,它允许我将string[]写入文件,但我认为我的计算机内存无法同时处理2.5 GB的数据。此外,文件是创建、填充和关闭的,所以我必须制作大量较小的文件来分解2GB的文本文件,然后才能将它们插入数据库。所以我宁愿远离这个选择。

我能想到的一个破解工作是制作一个StringBuilder,并使用AppendLine方法添加每一行以制作一个巨大的字符串。然后我可以将StringBuilder转换为字符串并将其写入文件。

但我的猜测已经够多了。我已经实现的方法是有效的,但我想知道是否有人能提出一种更好的方法来将大块数据写入文件?

如何将大量淫秽数据写入文件

有两件事可以提高使用StreamWriter的输出速度。

首先,确保输出文件与输入文件位于不同的物理磁盘上。如果输入和输出在同一个驱动器上,那么通常读取必须等待写入,而写入必须等待读取。磁盘一次只能做一件事。显然,并不是每次读取或写入都等待,因为StreamReader读取缓冲区并解析其中的行,而StreamWriter写入缓冲区,然后在缓冲区满时将其推送到磁盘。由于输入和输出文件位于不同的驱动器上,您的读写操作会重叠。

它们重叠是什么意思?操作系统通常会为您提前读取,因此它可以在您处理文件时缓冲您的文件。当您进行写操作时,操作系统通常会缓冲它,并将其懒洋洋地写入磁盘。因此,异步处理的数量有限

第二件事是增加缓冲区大小。StreamReaderStreamWriter的默认缓冲区大小为4 KB。因此,每次读取或写入4K都会引发一次操作系统调用。而且,很可能是磁盘操作。

如果将缓冲区大小增加到64K,那么操作系统调用和磁盘操作就会减少16倍(严格意义上说不是真的,但接近)。使用64K缓冲区可以减少25%以上的I/O时间,而且做起来非常简单:

const int BufferSize = 64 * 1024;
var reader = new StreamReader(filename, Encoding.UTF8, true, BufferSize);
var writer = new StreamWriter(filename, Encoding.UTF8, BufferSize);

这两件事比你能做的任何事情都更能加快你的I/O速度。试图使用StringBuilder在内存中构建缓冲区是不必要的工作,它会很难复制你通过增加缓冲区大小所能实现的效果,如果做得不正确,很容易使你的程序变得更慢。

我要提醒您不要使用大于64KB的缓冲区。在某些系统上,缓冲区高达256KB时,您会得到稍微好一点的结果,但在其他系统上,您的性能会明显较差——慢50%!我从未见过缓冲区大于256 KB的系统比缓冲区为64 KB的系统性能更好。根据我的经验,64KB是最好的地方。

您还可以使用三个线程:读取器、处理器和编写器。它们与队列通信。这可以将(input-time + process-time + output-time)的总时间减少到非常接近max(input-time, process-time, output-time)的时间。使用.NET,设置起来非常简单。请参阅我的博客文章:简单多线程,第1部分和简单多线程第2部分。

根据文档,默认情况下,StreamWriter不会在每次写入后自动刷新,因此它是缓冲的。

您也可以在File类上使用一些惰性方法,比如:

File.WriteAllLines("output.txt", 
    File.ReadLines("filename.txt").Select(ProcessLine));

其中ProcessLine是这样声明的:

private string ProcessLine(string input) {
    string result =         // do some calculation on input
    return result;
}

由于ReadLines是惰性的,而WriteAllLines具有惰性过载,因此它将流式传输文件,而不是试图读取整个文件。

构建要编写的字符串怎么样?

类似的东西

int cnt = 0;
StringBuilder s = new StringBuilder();
while(line = reader.readLine())
{
  cnt++;
  String x = (manipulate line);
  s.append(x+"'n");
  if(cnt%10000 == 0)
  {
     StreamWriter.write(s);
     s=new StringBuilder();
  }
}

已编辑,因为下面的注释是正确的,应该使用字符串生成器。