如何在不读取整行或文件的情况下读取令牌

时间:2013-11-16 14:20:42

标签: python file-io

是否有一种隐藏的方法可以从文件或类似文件的对象中读取令牌而不用读取整行?我立即拥有的应用程序(别人的问题,而不是我的)转换了一个带有几个很长行的大矩阵,基本上在迭代器上执行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刚刚超过换行符读取第二行。

4 个答案:

答案 0 :(得分:4)

逐个从文件中读取令牌;您可以使用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+')))

即使文件不适合内存,它仍然有效。

答案 1 :(得分: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?

答案 2 :(得分:2)

您可以使用file.read(size)以块的形式阅读文件。但我不建议按1字节读取,因为这会极大地影响性能。以下片段(没有经过多少测试,请自行承担风险)以块的形式读取文件的产量数字。您必须先读取文件以确定行的起始位置。

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

答案 3 :(得分:0)

# 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)