[英]C# : Fastest way for specific columns in CSV Files
我有一个非常大的CSV文件(数百万条记录)
我开发了一种智能搜索算法,可以在文件中定位特定的行范围,以避免解析整个文件。
现在,我面临一个棘手的问题:我只对特定列的内容感兴趣。
有没有一种聪明的方法来避免在200MB的文件中逐行循环,而仅检索特定列的内容?
您是说要从特定列的每一行获取每个值?
您可能必须走遍每一行。
这个C#CSV阅读库非常快,因此您可以使用它:
我会按照codeulike的建议使用现有的库,出于一个很好的理由,为什么要阅读本文:
除非所有 CSV字段的宽度都固定(除非为空,否则即使周围为n字节,分隔符之间仍然还有n个字节的空白)。
然后,每一行又具有固定的长度,因此您可以直接跳到该列的第一个值,并且,一旦您阅读了它,就可以立即前进到同一字段的下一行的值,而不必阅读任何中间值。
我认为这很简单-但此刻(和午餐时)我正在忙碌着,所以无论如何我都要完成它:)
为此,我们首先要知道每一行的字符长度 (根据Unicode,UTF8等调整字节):
row_len = sum(widths[0..n-1]) + n-1 + row_sep_length
其中n
是每一行的总列数-这是整个文件的常数。 我们添加了一个额外的n-1
来解决列值之间的分隔符。
row_sep_length
是两行之间的分隔符的长度-通常是换行符,或者可能是[回车和换行]对。
列row[r]col[i]
将是距row [r]开头的offset
字符,其中offset
定义为:
offset = i>0 ? sum(widths[0..i-1]) + i) : 0;
//or sum of widths of all columns before col[i]
//plus one character for each separator between adjacent columns
然后, 假设您已读取整个列的值,直到下一个分隔符,则通过从列中减去列的宽度来计算下一个列值的起始字符的偏移量row[r+1]col[i]
行长。 这是文件的另一个常量:
next-field-offset = row_len - widths[i];
//widths[i] is the width of the field you are actually reading.
一直- i
在这个伪代码中从零开始,向量/数组的索引也是如此。
要进行读取,请首先将文件指针前移offset
字符-将您带到所需的第一个值。 您读取该值(将您带到下一个分隔符),然后只需将文件指针前移next-field-offset
字符即可。 如果此时达到EOF
,就完成了。
我可能会错过任何一种字符-因此,如果适用-请检查一下!
仅当您可以保证所有行的所有字段值-甚至为null-都将是相同的长度,并且分隔符始终是相同的长度,并且alll行分隔符是相同的长度时,这才起作用。 如果没有,那么这种方法将行不通。
您将必须以慢速的方式进行操作-在每一行中找到该列,然后执行您需要执行的所有操作。
如果您每次都要对列值进行大量工作,则一种优化方法是首先将所有列值拉到一个列表中(也设置一个已知的初始容量)或某个内容(一次以100,000为上限,这样的东西),然后遍历那些。
如果让每个循环都专注于一个任务,那应该比一个大循环更有效。
同样,一旦批处理了100,000个列值,就可以使用Parallel Linq分发第二个循环(而不是第一个循环,因为没有点并行读取文件)。
如果您可以对数据进行特定限制,则只有快捷方式。
例如,如果您知道文件中没有包含换行符的值,则只能逐行读取文件。 如果不知道,则必须按记录将文件记录解析为流,并且每个记录在没有值的换行符处结束。
但是,除非您知道每一行占用的字节数完全相同,否则除了逐行读取文件外,没有其他方法可以读取文件。 文件中的换行符只是另一对字符,在文本文件中找到一行的唯一方法就是读取该行之前的所有行。
如果您可以在记录中的字段上添加限制,则可以在读取记录时执行类似的快捷方式。 例如,如果您知道要插入的字段左侧的字段都是数字字段,则可以使用一种更简单的解析方法来查找该字段的开头。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.