使用Python生成器处理大型文本文件

6

我刚开始使用生成器,已经阅读过一些相关资料,但需要一些帮助来以块的方式处理大型文本文件。我知道这个主题已经被讨论过,但示例代码的说明非常有限,如果不理解正在发生什么,则很难修改代码。

我的问题相当简单,我有一系列包含人类基因组测序数据的大型文本文件,格式如下:

chr22   1   0
chr22   2   0
chr22   3   1
chr22   4   1
chr22   5   1
chr22   6   2

这些文件的长度在1GB到20GB之间,太大了无法读入内存。因此,我想一次读取10000行的数据块/分段,以便可以对这些块大小中的最后一列执行计算。
根据这个链接,我编写了以下内容:
def read_large_file(file_object):
    """A generator function to read a large file lazily."""

    bin_size=5000
    start=0
    end=start+bin_size

    # Read a block from the file: data
    while True:
        data = file_object.readlines(end) 
        if not data:
            break
        start=start+bin_size
        end=end+bin_size
        yield data


def process_file(path):

    try:
        # Open a connection to the file
        with open(path) as file_handler:
            # Create a generator object for the file: gen_file
            for block in read_large_file(file_handler):
                print(block)
                # process block

    except (IOError, OSError):
        print("Error opening / processing file")    
    return    

if __name__ == '__main__':
            path='C:/path_to/input.txt'
    process_file(path)

在 'process_block' 中,我期望返回的 'block' 对象是一个包含10000个元素的列表,但实际上它不是吗?第一个列表有843个元素。第二个列表有2394个元素?

我想要从一个块中获取 'N' 行数据,但我对这里发生的事情感到非常困惑?

这个解决方案 这里 看起来可以帮助我,但我仍然不理解如何修改它以一次读取 N 行?

这个 这里 也看起来是一个很好的解决方案,但同样地,没有足够的背景说明让我能够理解并修改代码。

任何帮助都将不胜感激!


1
使用pandas https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html - Dmitrii K
readlines()文档中可以看到:"如果有可选的sizehint参数,那么不是读取到EOF,而是读取总共大约为sizehint字节的整行"。因此,readlines(10000)永远不会让你获得10,000行。 - SiHa
3个回答

22

不要在文件中使用偏移量,而是尝试从循环中构建并生成10000个元素的列表:

def read_large_file(file_handler, block_size=10000):
    block = []
    for line in file_handler:
        block.append(line)
        if len(block) == block_size:
            yield block
            block = []

    # don't forget to yield the last block
    if block:
        yield block

with open(path) as file_handler:
    for block in read_large_file(file_handler):
        print(block)

这非常好!感谢您的解释。我已将其接受为答案,因为它是完整的可行解决方案。虽然我决定采用 Dimitrii K 建议的 pandas 解决方案,因为它非常简洁易懂。下面会发布我的代码。 - user3062260

2

虽然这不是一个完整的答案,但找出其行为原因只需要大约27秒:

(blook)bruno@bigb:~/Work/blookup/src/project$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
pythonrc start
pythonrc done
>>> help(file.readlines)

Help on method_descriptor:

readlines(...)
    readlines([size]) -> list of strings, each a line from the file.

    Call readline() repeatedly and return a list of the lines so read.
    The optional size argument, if given, is an approximate bound on the
    total number of bytes in the lines returned.

我知道这里并不是所有人都是专业程序员 - 当然,文档并不总能解决问题(我很乐意回答那些问题),但确实有太多问题的答案在文档开头就用简单的文字写明了,这让人感到有点烦人。


尊敬的,我已经想到输出可能与函数返回字节而不是行有关,但仅仅知道这一点并不能真正帮助实现函数基于行返回对象,这也是本文的主要目标。 - user3062260

0

如果有人遇到类似的问题,这里是一个基于这里的解决方案,希望能对你有所帮助。

import pandas as pd

def process_file(path,binSize):

    for chunk in pd.read_csv(path, sep='\t', chunksize=binSize):
        print(chunk)
        print(chunk.ix[:,2]) # get 3rd col
        # Do something with chunk....  

if __name__ == '__main__':
    path='path_to/infile.txt'
    binSize=5000
    process_file(path,binSize)

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