我有一个非常大的CSV文件(数百万条记录)
我开发了一种智能搜索算法来定位文件中的特定行范围,以避免解析整个文件。
现在我面临一个棘手的问题:我只对特定专栏的内容感兴趣 是否有一种智能方法可以避免逐行循环通过200MB文件并仅检索特定列的内容?
答案 0 :(得分:2)
您的意思是从特定列的每一行获取每个值?
你可能不得不访问每一行来做到这一点。
这个C#CSV阅读库非常快,所以您可以使用它:
答案 1 :(得分:2)
我会像codeulike所建议的那样使用现有的库,并且有一个很好的理由为什么阅读这篇文章:
答案 2 :(得分:1)
除非所有 CSV字段都有固定的宽度(即使为空,但在它周围的分隔符之间仍有 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]
的值将是行[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
,则表示您已完成。
我可能在这方面错过了一个角色 - 所以如果它适用 - 请检查它!
这只有在你能保证所有行的所有字段值 - 甚至是空值 - 都是相同的长度,并且分隔符总是相同的长度并且所有的行分隔符是相同的长度。如果不是 - 那么这种方法将不起作用。
你必须以缓慢的方式去做 - 找到每一行中的列并做你需要做的任何事情。
如果您每次都对列值进行大量工作,则一次优化将首先将所有列值拉出到列表中(使用已知的初始容量设置)或者其他内容(批处理为100,000)一段时间或类似的东西),然后迭代这些。
如果你将每个循环都集中在一个任务上,那么它应该比一个大循环更有效。
同样,一旦你批量处理100,000个列值,你就可以使用Parallel Linq来分配第二个循环(不是第一个循环,因为没有点与文件并行读取)。
答案 3 :(得分:0)
如果您可以对数据施加特定限制,则只有快捷方式。
例如,如果您知道文件中没有包含换行符的值,则只能逐行读取文件。如果您不知道这一点,则必须按记录将文件记录解析为流,并且每个记录都会在不在值内的换行符结束。
但是,除非您知道每行占用的字节数完全相同,否则没有其他方法可以读取文件而不是逐行读取。文件中的换行符只是另一对字符,没有其他方法可以在文本文件中找到一行而不是读取它之前的所有行。
如果您可以对记录中的字段进行限制,则可以在读取记录时执行类似的快捷方式。例如,如果您知道您感兴趣的字段左侧的字段都是数字,则可以使用更简单的解析方法来查找字段的开头。