python - 为什么 readlines() 读取的内容比 sizehint 多得多?

标签 python parsing readlines

背景

我正在 Python 2.7.6 中解析非常大的文本文件(30GB+)。为了稍微加快这个过程,我将文件分成 block ,并使用多处理库将它们分配给子进程。为此,我在主进程中迭代文件,记录要分割输入文件的字节位置并将这些字节位置传递给子进程,然后子进程打开输入文件并使用 file.readlines(chunk_size) 读取其 block 。 。但是,我发现读入的 block 似乎比 sizehint 大得多(4 倍)。争论。

问题

为什么不注意尺寸提示?

示例代码

以下代码演示了我的问题:

import sys

# set test chunk size to 2KB
chunk_size = 1024 * 2

count = 0
chunk_start = 0
chunk_list = []

fi = open('test.txt', 'r')
while True:
    # increment chunk counter
    count += 1

    # calculate new chunk end, advance file pointer
    chunk_end = chunk_start + chunk_size
    fi.seek(chunk_end)

    # advance file pointer to end of current line so chunks don't have broken 
    # lines
    fi.readline() 
    chunk_end = fi.tell()

    # record chunk start and stop positions, chunk number
    chunk_list.append((chunk_start, chunk_end, count))

    # advance start to current end
    chunk_start = chunk_end

    # read a line to confirm we're not past the end of the file
    line = fi.readline()
    if not line:
        break

    # reset file pointer from last line read
    fi.seek(chunk_end, 0)

fi.close()

# This code represents the action taken by subprocesses, but each subprocess
# receives one chunk instead of iterating the list of chunks itself.
with open('test.txt', 'r', 0) as fi:
    # iterate over chunks
    for chunk in chunk_list:
        chunk_start, chunk_end, chunk_num = chunk

        # advance file pointer to chunk start
        fi.seek(chunk_start, 0)

        # print some notes and read in the chunk
        sys.stdout.write("Chunk #{0}: Size: {1} Start {2} Real Start: {3} Stop {4} "
              .format(chunk_num, chunk_end-chunk_start, chunk_start, fi.tell(), chunk_end))
        chunk = fi.readlines(chunk_end - chunk_start)
        print("Real Stop: {0}".format(fi.tell()))

        # write the chunk out to a file for examination
        with open('test_chunk{0}'.format(chunk_num), 'w') as fo:
            fo.writelines(chunk)

结果

我使用大约 23.3KB 的输入文件 (test.txt) 运行此代码,并生成以下输出:

Chunk #1: Size: 2052 Start 0 Real Start: 0 Stop 2052 Real Stop: 8193
Chunk #2: Size: 2051 Start 2052 Real Start: 2052 Stop 4103 Real Stop: 10248
Chunk #3: Size: 2050 Start 4103 Real Start: 4103 Stop 6153 Real Stop: 12298
Chunk #4: Size: 2050 Start 6153 Real Start: 6153 Stop 8203 Real Stop: 14348
Chunk #5: Size: 2050 Start 8203 Real Start: 8203 Stop 10253 Real Stop: 16398
Chunk #6: Size: 2050 Start 10253 Real Start: 10253 Stop 12303 Real Stop: 18448
Chunk #7: Size: 2050 Start 12303 Real Start: 12303 Stop 14353 Real Stop: 20498
Chunk #8: Size: 2050 Start 14353 Real Start: 14353 Stop 16403 Real Stop: 22548
Chunk #9: Size: 2050 Start 16403 Real Start: 16403 Stop 18453 Real Stop: 23893
Chunk #10: Size: 2050 Start 18453 Real Start: 18453 Stop 20503 Real Stop: 23893
Chunk #11: Size: 2050 Start 20503 Real Start: 20503 Stop 22553 Real Stop: 23893
Chunk #12: Size: 2048 Start 22553 Real Start: 22553 Stop 24601 Real Stop: 23893

报告的每个 block 大小约为 2KB,所有开始/停止位置均按其应有的方式排列,并且 fi.tell() 报告的实际文件位置似乎是正确的,所以我相当确定我的分块算法是好的。然而,真实的停靠位置显示readlines()阅读的内容远不止尺寸提示。另外,输出文件 #1 - #8 为 8.0KB,远大于大小提示。

即使我尝试只破坏行尾的 block 是错误的,readlines()仍然不必读取超过 2KB + 一行。文件 #9 - #12 变得越来越小,这是有道理的,因为 block 起点越来越接近文件末尾,并且 readlines()不会读取超过文件末尾的内容。

注释

  1. 我的测试输入文件仅在每行上打印“<行号>\n”,1-5000。
  2. 我再次尝试使用不同的 block 和输入文件大小,得到类似的结果。
  3. readlines documentation说读取大小可能会四舍五入为内部缓冲区的大小,因此我尝试在不缓冲的情况下打开文件(如图所示),但没有什么区别。
  4. 我使用这个算法来分割文件,因为我需要能够支持 *.bz2 和 *.gz 压缩文件,而 *.gz 文件无法让我在不解压文件的情况下识别未压缩文件的大小。 *.bz2 文件也没有,但我可以从这些文件末尾查找 0 字节并使用 fi.tell()获取文件大小。请参阅my related question .
  5. 在添加支持压缩文件的要求之前,之前版本的脚本使用 os.path.getsize()作为分块循环的停止条件,并且 readlines 似乎可以很好地使用该方法。

最佳答案

缓冲区readlines文档提到与 open 的第三个参数的缓冲无关。调用控制。缓冲区是 this buffer in file_readlines :

static PyObject *
file_readlines(PyFileObject *f, PyObject *args)
{
    long sizehint = 0;
    PyObject *list = NULL;
    PyObject *line;
    char small_buffer[SMALLCHUNK];

哪里SMALLCHUNK之前已定义:

#if BUFSIZ < 8192
#define SMALLCHUNK 8192
#else
#define SMALLCHUNK BUFSIZ
#endif

我不知道在哪里BUFSIZ来自,但看起来您得到的是 #define SMALLCHUNK 8192案件。无论如何,readlines永远不会使用小于 8 KiB 的缓冲区,因此您应该使 block 大于该值。

关于python - 为什么 readlines() 读取的内容比 sizehint 多得多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25755987/

相关文章:

parsing - 启动服务器时出现 %PARSER_ERROR

python - 检查文本文件中的日期是否过期

Python readline() 不工作?

python - cherrypy:响应包含点的网址?

python - 为非程序员记录 Python 脚本

python - 长度必须匹配才能比较( Pandas 根据两个标准进行选择)

python - open() 中的整数文件描述符 "0"

Java:如何严格解析日期?

python - 解析的部分评估

python - codecs.ascii_decode(输入,self.errors)[0] UnicodeDecodeError : 'ascii' codec can't decode byte 0xc2 in position 318: ordinal not in range(128)