我正在解析一个40MB的CSV文件。
它现在运行得很好,并且它很容易解析,我唯一的问题是性能,这当然是相当慢的。
我想知道是否有一种方法可以改进这一点,因为我只需要通过键找到然后停止循环,所以如果条目位于文件的开头,它会很快完成,但是如果它在最后需要一段时间。
我可以通过给它一个随机起始线来平衡这个,但算法仍然是O(n)......所以我不确定它是否真的值得。
有没有办法可以改进我的顺序解析算法?
答案 0 :(得分:4)
首先:“阅读巨大的CSV文件”和“所以我正在解析一个40MB的CSV文件。”这里有10+ GIGAbyte的空间分隔文件 - 你会怎么称呼它们?
另外:文件的大小无关紧要,无论如何都要逐行处理它们。
我唯一的问题是表现,这当然是相当慢的
定义。你觉得什么慢?如果做得好,解析它们的速度非常快。
我想知道我是否有办法改善这一点,因为我只需要通过钥匙找到我找到的 然后停止循环,所以如果条目是在文件的初始化它 很快完成,但如果它结束需要一段时间。
不要使用CSV文件? 60多年前,人们为此发明了数据库。
有没有办法可以改进我的secuential解析算法?
你的意思是除了将解析拉入一个单独的线程,并使用一个有效的代码(你可能没有 - 没有人知道)。
理论上你可以:
在一个线程上读取,使用一个不错的缓冲区(减去IO =更快)
将字段拆分为线程2(可选)
使用任务来解析字段(每行每个字段一个),以便您使用所有处理器。)
我目前正在处理一些(大约10.000个)文件(大概是两位数的gigabte)并且......我这样做(必须按特定顺序处理它们)才能完全使用我的电脑。
这应该会给你很多 - 而且严肃地说,一个40mb的文件应该在0.x秒(0.5 - 0.6)内加载。
仍然效率很低。你没有像所有人一样将文件加载到数据库中的任何原因吗? CSV作为一种传输格式是好的,它作为数据库很糟糕。
答案 1 :(得分:3)
为什么不将csv转换为普通数据库。即使是sqlexpress也没关系。
答案 2 :(得分:3)
当然。
假设您按字母顺序排序。
然后,从中间开始
每次迭代,移动到顶部或底部的中间;哪个有合适的钥匙。
该算法具有O(log n )。
这被称为“二元搜索”,正是“Mike Christianson”在评论中所暗示的。
答案 3 :(得分:1)
建议您将一个40Mb文件分解为更小的文件。 使用Parallel.ForEach可以提高文件处理性能
答案 4 :(得分:0)
您可以将CSV加载到DataTable中,并使用可能比循环
更快的可用操作将其加载到数据库并对其执行操作是另一种选择
答案 5 :(得分: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,但如果您处理引号,则可以轻松修改我发布的代码以处理修剪。