【问题标题】:Fast/low-memory method to parse first two columns in a large csv file using c#使用 c# 解析大型 csv 文件中前两列的快速/低内存方法
【发布时间】:2011-06-08 18:00:09
【问题描述】:

我正在解析一个大的 csv 文件 - 大约 500 兆(很多行,很多列)。我只需要前两列(所以每行的第二个逗号)。另外,多个线程需要同时访问这个文件,所以我不能采取排他锁。

解决此问题的最快/最少内存消耗方法是什么?我应该看哪些课程/方法?我认为我应该尽可能保持低级 - 逐个字符,逐行阅读?

也许这是一种允许同时访问的方法?

using ( var filestream = new FileStream( filePath , FileMode.Open , FileAccess.Read , FileShare.Read ) )
{
     using ( var reader = new StreamReader( filestream ) )
     {
       ...
     }
}

编辑
决定去看看http://www.codeproject.com/KB/database/CsvReader.aspx 这似乎让我能够只阅读两列然后跳到下一行。 他们还有一些基准测试显示了快速的性能和低内存配置。

【问题讨论】:

  • 由于您需要使用流,因此您至少需要对该行进行锁定。否则:一个线程需要一行开始读取,第二个线程将流移动到新行......等等瞧......
  • @Andreas - 你能看看我发布到原始问题的代码吗?这是否解决了同时访问的问题?
  • 同时访问很大程度上取决于您对文件所做的工作。如果他们都只是阅读,那么你的状态很好。如果其中一个需要写入文件,则存在竞争条件,您需要仔细协调读写操作。
  • @Dan - 谢谢!看来我的状态很好,因为我只从这些文件中读取。

标签: c# .net .net-4.0 csv text-parsing


【解决方案1】:

如果你想要低内存,你可能会使用 StreamReader 和 ReadLine by line。

在前几天的类似案例中,我能够跳过 500 MB 文件中的前 20,000,000 行,并在大约 7 秒内为接下来的 1,000,000 行构建一个字符串(使用 StringBuilder)。

【讨论】:

  • 我建议使用.Read(),因为它不会读取整行 - 只是一个字符一个字符。这对于低内存消耗可能会更好:)
  • @Andreas Niedermair:它也说“快”。 :-(
  • 为此,您需要测量 :) 取决于行的长度,我仍然会给出。阅读更好的更改以赢得性能挑战 :)
  • @Andreas:I/O 和内存使用(大约)相同。 1 行的大小可能微不足道,StreamReader 可能会缓存更多。使用 Read() 会增加一些 CPU 开销。
  • @Henk:感谢您的启发 :) 明天会做一些测量
【解决方案2】:

假设文件包含 ASCII 编码文本(对于 csv 来说是典型的),您最好的选择可能是直接使用 Stream 和 Stream.Read 方法,它允许您读取预分配的缓冲区。这有几个优点:

  1. 您只分配一次缓冲区,而 ReadLine() 将为每一行创建一个新字符串。

  2. 您不必为整行执行 Unicode 转换;您可以仅对第二个逗号之前的部分执行此操作,或者(如果您有严重的时间限制),您可以编写自己的数字解析器,对缓冲区中的 ASCII 字符串数据进行操作(我确定有有据可查的算法。)当然,这是假设您需要数字数据。

您可能需要的其他方法包括 ASCII Encoding 方法,尤其是 Encoding.ASCII.GetString

【讨论】:

  • 您对缓冲区可能是正确的,对编码更是如此。但不清楚的是,如果您使用 ReadLine 并保持字符串的生命周期较短,那么 GC 开销应该很小。对于更复杂的场景,它会容易得多。
  • @Henk,我同意,使用 ReadLine 肯定更简单,并且 GC 在处理 String 实例(以及相关缓冲区,包括用于存储行的原始 ASCII 的缓冲区)中的周转率应该相当有效在被编码为 Unicode 之前)。首先使用 ReadLine 并测量性能可能值得实现;如果还不够,那么这种方法应该会快很多。
  • @Dan - 感谢精彩的文章!我倾向于阅读,因为缓冲区令人信服。就我而言,我实际上可以预测文件前两列的长度(我可能会高估几个字符,但这没什么大不了的)
  • 所以一旦我调用 Read(),我如何进入下一行? =) 希望不要使用 ReadLine 否则会破坏使用 Read 的意义。
  • 附带说明,使用 ReadLine 方法可能会多次触发 GC Gen0 收集,但每次堆都需要最少或不需要压缩(除非 Gen0.0 中的其他线程上有活动。 ) 对于一个 500M 的 ASCII 文件,在处理过程中至少需要 1.5GB,每行有额外的开销(因此,具有更多行的文件将消耗多于一个具有更多列的文件。)
猜你喜欢
  • 2018-12-09
  • 1970-01-01
  • 2019-10-20
  • 2022-07-12
  • 2011-10-14
  • 1970-01-01
  • 2018-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多