使用Python计算一个30GB以上的CSV文件中双引号外的新行数

3

我有一个超大文件(30GB+)的csv文件,我正在逐个块地解析该文件以计算换行符的数量

使用以下函数:

def yieldblocks(file, size=1024*1024):
    while True:
        blocks = file.read(size)
        if not blocks: break
        yield blocks

并这样调用它,

sum(bl.count("\n") for bl in blocks(txtfile))

我可以在一个小时内计算出换行符的数量(我很惊讶这是我能做到的最好成绩)。

我的问题在于我需要跳过双引号内的换行符,因为某些列有多行内容。

我尝试了下面的方法,但似乎不起作用,处理过程没有结果。

sum(.5 if re.search('^[^"]*"(?=[^"]*(?:"[^"]*")*[^"]*$).*$', bl) else 1 for bl in yieldblocks(txtfile))

这个正则表达式是为了在一行中查找奇数个双引号字符,并适用于小文件。

我使用的是2GB RAM和32位操作系统。

注意:我已经尝试使用CSV模块,但与按块计数相比速度较慢,希望能找到一种方法使其工作。


如果您使用sum(1 for line in file)会发生什么? - mkrieger1
与按块计算换行符相比,速度要慢得多。 - QVSJ
不得不说,这听起来像是C语言的工作。block.count('\n')方法可能很快,但它无法处理引用。因此,您必须逐字节地浏览文件,在每个点上记录您是否在引号内或外部(并在跨块时记住)。这在Python中将非常慢。您可能会发现使用内存映射文件有所帮助。您还可以使用pandas.read_csv(非常快),可能通过StringIO提供块。但是,您将难以知道在哪里断开这些块(回到找到未引用行结尾的原始问题)。 - Matthias Fripp
只是为了澄清:你实际上感兴趣的是文件中记录的数量吗? - mkrieger1
@mkrieger1 好的,请。 - QVSJ
2个回答

1
这可能对你很有帮助。 pandas.read_csv 通常非常快,但我尚未尝试过分块处理。
import pandas as pd
reader = pd.read_csv('file.csv', sep=',', chunksize=10000, low_memory=True)
line_count = sum(len(chunk) for chunk in reader)

文档中有一些更多的信息(不是很多),请参见文档


谢谢Mathias。我尝试了这个。文件中存在一个问题,即某些行的列标题数与实际内容中的列数不匹配,而pandas不喜欢这种情况并忽略这些行(如果您提供忽略选项)。否则,它会完全出错,这将使计算记录数量的目的失去意义。 - QVSJ
你可能需要尝试一下是否有绕过这个问题的方法——也许只请求一列并让它忽略其余的列?否则,你可能需要逐字节处理整个文件。正如@Danny_ds所指出的,这项工作可能受到I/O限制而不是处理器限制,因此你可以在Python中逐字节进行处理。 - Matthias Fripp

0

最简单和最快的方法是使用内存映射,并按照以下方式遍历字节(伪代码):

bool insidequotes = false

for each byte b:
  if b=='"'
    insidequotes = not insidequotes  // false -> true or true -> false

  else if b=='\n' and not insidequotes
    increment recordcount

这应该不会比在磁盘上复制文件(甚至只是读取文件)需要更长的时间 - 不确定 Python 中可能存在的开销。

这也处理了字段内部的转义引号:

123,test,"24"" monitor",456

谢谢Danny。由于我使用的是32位操作系统,而且无法mmap文件,所以这个方法不起作用。 - QVSJ
@QVSJ 也许你可以使用 mmap 来映射 1GB 的块(不确定 Linux 或 Python 是否支持)。或者如果这样不行,就以二进制模式打开文件并循环遍历它。 - Danny_ds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接