-4

我正在解析一个 40MB 的 CSV 文件。

它现在工作得很好,而且很容易解析,我唯一的问题是性能,这当然很慢。

我想知道是否有一种方法可以改进这一点,因为我只需要通过我找到的键找到然后停止循环,所以如果条目位于文件的开头,它会很快完成,但如果它在结束需要一段时间。

我可以通过给它一个随机的起始线来平衡它,但算法仍然是 O(n) ......所以我不确定它是否真的值得。

有没有办法改进我的顺序解析算法?

4

6 回答 6

4

首先:“阅读巨大的 CSV 文件”和“所以我正在解析一个 40MB 的 CSV 文件。”。我这里有 10+ 千兆字节的空间分隔文件 - 你会怎么称呼它们?

另外:文件的大小无关紧要,您通常会逐行处理它们。

我唯一的问题是性能,这当然很慢

定义。你觉得什么是慢?正确完成后解析它们非常快。

我想知道是否有办法改进这一点,因为我只需要通过我找到的键找到然后停止循环,所以如果条目位于文件的开头,它会很快完成,但如果它在结束需要一段时间。

不要使用 CSV 文件?60 多年前,人们为此发明了数据库。

有没有办法改进我的安全解析算法?

你的意思是除了将解析拉到一个单独的线程中,并使用高效的代码(你可能没有 - 没人知道)。

理论上你可以:

  • 在一个线程上读取,具有不错的缓冲区(更少的 IO = 更快)

  • 将字段拆分为线程 2(可选)

  • 使用任务来解析字段(每行每个字段一个),以便您使用所有处理器)。

我目前正在处理一些(大约 10.000 个)文件(遗憾的是,大小为两位数 gigabte)并且......我采用这种方式(必须以特定顺序处理它们)以充分使用我的计算机。

这应该会给你很多 - 说真的,一个 40mb 的文件应该在 0.x 秒 (0.5 - 0.6) 内加载。

仍然是非常低效的。您没有像所有人一样将文件加载到数据库中的任何原因吗?CSV 作为某种传输格式很好,但它作为数据库很糟糕。

于 2012-05-30T15:44:17.713 回答
3

当然。

假设您按字母顺序排列。
然后,从中间开始。
每次迭代,移动到顶部或底部的中间;哪个有相应的密钥。

这个算法有 O(log n )。

这被称为“二分搜索”,是“Mike Christianson”在他的评论中所建议的。

于 2012-05-30T15:40:27.507 回答
3

为什么不将 csv 转换为普通数据库。即使是 sqlexpress 也可以。

于 2012-05-30T15:39:32.130 回答
1

建议您将一个 40Mb 文件分成较小的几个文件。并且使用 Parallel.ForEach可以提高文件处理性能

于 2012-05-30T15:43:16.123 回答
0

我相信,这是顺序读取 CSV 文件的最快方法。可能还有其他方法可以从 CSV 中提取数据,但如果您仅限于这种方法,那么此解决方案可能适合您。

const int BUFFER_SIZE = 0x8000;  //represents 32768 bytes
public unsafe void parseCSV(string filePath)
{
     byte[] buffer = new byte[BUFFER_SIZE];
     int workingSize = 0; //store how many bytes left in buffer
     int bufferSize = 0; //how many bytes were read by the file stream
     StringBuilder builder = new StringBuilder();
     char cByte; //character representation of byte
     using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
     {
         do
         {
              bufferSize = fs.Read(buffer, 0, BUFFER_SIZE);
              workingSize = bufferSize;
              fixed (byte* bufferPtr = buffer)
              {
                   byte* workingBufferPtr = bufferptr;
                   while (workingSize-- > 0)
                   {
                        switch (cByte = (char)*workingBufferPtr++)
                        {
                            case '\n':
                                break;
                            case '\r':
                            case ',':
                                builder.ToString();
                                builder.Clear();
                                break;
                            default:
                                builder.Append(cByte);
                                break;
                        }
                   }
              }
         } while (bufferSize != 0);
     }
}

解释:

  • 将文件读入字节缓冲区。这将使用基本Filestream类来完成,它可以访问总是快速的Read()
  • 不安全的代码。虽然我通常建议不要使用不安全的代码,但在遍历任何类型的缓冲区时,使用指针可以带来加速。
  • StringBuilder因为我们会将字节连接成可用的字符串来测试密钥。StringBuilder 是迄今为止将字节附加在一起并获得可用字符串的最快方法。

请注意,此方法与RFC 4180相当抱怨,但如果您处理引号,您可以轻松修改我发布的代码以处理修剪。

于 2012-05-30T16:13:07.147 回答
0

您可以将 CSV 加载到 DataTable 并使用比循环更快的可用操作

将其加载到数据库并对其执行操作是另一种选择

于 2012-05-30T15:50:55.697 回答