【问题标题】:How to read tokens without reading whole line or file如何在不读取整行或文件的情况下读取令牌
【发布时间】:2013-11-29 22:24:33
【问题描述】:

是否有一种隐蔽的方式可以从文件或类似文件的对象中读取令牌而不读取整行?我立即拥有的应用程序(其他人的问题,不是我的问题)正在转置一个包含几行很长的行的大矩阵,本质上是在迭代器上执行itertools.izip(),以挑选出单列的元素。这个想法不是在迭代期间没有整个文件在内存中。

行是空格分隔的 ASCII 十进制数字。

使用 Java 的 Scanner 类问题会很简单,但我在 Python 标准库中看不到任何似乎在没有字符串中的整个输入的情况下进行标记的东西。

为了记录,我知道如何自己写这个。我只是想知道是否有我错过的标准工具。可以 EasyInstalled 的 FOSS/libre 也不错,但我在 PYPI 上也看不到任何东西。

完整的问题是获取样本输入:

"123 3 234234 -35434 112312 54 -439 99 0 42\n" +
"13 456 -78 910 333 -44 5555 6 8"

...并产生输出(作为生成器,无需一次将所有很长的行读入内存:

[123, 13], [3, 456], [234234, -78], ...etc

正如我所说,它本质上是 itertools.izip(iterator1, iterator2),将 iterator1 指向文件的开头,而 iterator2 则刚刚超过换行符以读取第二行。

【问题讨论】:

  • 我很确定没有这样的内置可用。
  • 您能说得更具体些吗?您想打印文件中的特定列而不在内存中保留整行吗?
  • @J.F.Sebastian 据说这些行是数百万个数字,我不知道为什么它们在一个文件中作为两个文本行。
  • @stranac 谢谢。我也没有看到任何东西,但这不是我第一次错过任何东西。

标签: python file-io


【解决方案1】:

从一个文件中一个一个地读取令牌;您可以使用re 模块从memory-mapped file 生成令牌:

#!/usr/bin/env python3
import re
import sys
from mmap import ACCESS_READ, mmap    

def generate_tokens(filename, pattern):
    with open(filename) as f, mmap(f.fileno(), 0, access=ACCESS_READ) as mm:
         yield from re.finditer(pattern, mm)

# sum all integers in a file specified at the command-line
print(sum(int(m.group()) for m in generate_tokens(sys.argv[1], br'\d+')))

即使文件不适合内存,它也可以工作。

【讨论】:

  • 看起来很有趣,而且是半便携的。我不太关心隐藏模块名称的“来自”导入,但这是一个小问题。
  • @MikeHousky:它应该可以在 Unix 和 Windows 上正常工作。如果不是这样,请告诉我。
  • 三个有用的答案,全都赞成。我选择了这个来接受,因为它让我注意到了我以前从未见过的 mmap 包。
【解决方案2】:

这是一个生成器,它一次处理一个文件并在遇到空格时生成标记。

def generate_tokens(path):
    with open(path, 'r') as fp:
        buf = []
        while True:
            ch = fp.read(1)
            if ch == '':
                break
            elif ch.isspace():
                if buf:
                    yield ''.join(buf)
                    buf = []
            else:
                buf.append(ch)

if __name__ == '__main__':
    for token in generate_tokens('input.txt'):
        print token

为了更通用,您似乎可以使用此链接中描述的 re 模块。只需使用文件中的生成器提供输入,以避免一次读取整个文件。

Python equivalent of ruby's StringScanner?

【讨论】:

  • 感谢代码和链接。我没有想过要在 Ruby 中寻找类似的东西。对 Java Scanner 替换的类似搜索只给出了完整的字符串解决方案。
  • 在Python中一次累积输入一个字节是very slow
【解决方案3】:

您可以使用file.read(size) 分块读取文件。但是我不建议读取 1 个字节,因为这会极大地影响性能。跟随 sn-p (没有太多测试,使用风险自负)以块的形式读取文件并产生数字。不过,您必须先通读文件以确定行的起始位置。

def values_chunks(file_object, pos_from=0, chunk_size=32*1024):
    file_object.seek(pos_from)
    eol = False
    tail = ''
    while True:
        raw_data = file_object.read(chunk_size)
        raw_data = tail + raw_data
        raw_data = raw_data.split('\n', 1) # to check for eol, split in tuple
        if len(raw_data) > 1:
            eol = True
        raw_data = raw_data[0]
        raw_values = raw_data.split()
        if not eol and raw_data[-1] != ' ':
            tail = raw_values[-1]
            raw_values = raw_values[:-1]
        else:
            tail = ''
        for value in raw_values: # either case we need only first tuple elem
            yield int(value)
        if not raw_data[0] or eol: # eof/eol
            break

>>> with open('test', 'wb') as test:
...     test.write(' '.join(map(str, range(10**5))))
...     test.write('\n')
...     test.write(' '.join(map(str, range(10**4))))
...
>>> values = list(values_chunks(open('test', 'rb')))
>>> len(values)
100000
>>> sum(values)
4999950000L

【讨论】:

  • 这更接近我写的内容,除了可能专门搜索空白而不是使用 split 调用。如果这个青睐的“客户”(如果我没有真正得到报酬,就不是真正的客户,对吧?:^/ )有兆字节的问题,那么标记化可能也值得最小化。感谢您的代码和不同的想法。它们当然可以用于解决不那么奇怪的问题!
  • 你可以看看.readline(limit), .readlines(hint) are implemented。只需将'\n'(换行符)替换为' '(空格)即可。
【解决方案4】:
# python, read token file
# Put token on first line of a token.txt file. 

token = open("token.txt","r").readline()  # I've opted to just save my token to a text file.
token = token.rstrip()  
...

print(token)

【讨论】:

  • 请始终将您的答案放在上下文中,而不仅仅是粘贴代码。有关详细信息,请参阅here
猜你喜欢
  • 2019-07-09
  • 1970-01-01
  • 1970-01-01
  • 2010-09-11
  • 2019-04-05
  • 2011-03-27
  • 2016-07-25
相关资源
最近更新 更多