一次从文件中读取一个元素Python

时间:2017-03-21 15:50:21

标签: python io

我有一个不是逐行构建的文件,而是包含在下一行中的不同大小的组。我不会详细介绍,因为它并不重要。我只想说线条在结构上没有任何意义。

我的问题是:是否有一种方法可以逐个元素地逐行读取文件,而不是逐行读取?我很确定不逐行进行单调,但我不必阅读每一行并将其与前一行连接,然后处理它。如果有一种简单的方法可以一次读取每个元素,那么事情会变得更容易。对不起,如果之前有人询问,我真的找不到任何东西。谢谢!

编辑:我将添加一个简单的例子

文件如下所示:

1.00 3 4.3 5.6 2.3 4 12.4 0.5 10.2 1.10 8
5.9 11.2 7.3 1.20 8 0.2 1.2 4.2 11 23.1 4.0
7.3 13 4.4 1.7 0.5 (etc.)

这些组以1.00,1.10,1.20开始(总是增加0.1)

4 个答案:

答案 0 :(得分:1)

如果数字不会超过记录中断,那么我认为这可以更简单地完成。这是你的数据。

1.00 3 4.3 5.6 2.3 4 12.4 0.5 10.2 1.10 8
5.9 11.2 7.3 1.20 8 0.2 1.2 4.2 11 23.1 4.0
7.3 13 4.4 1.7 0.5

这是代码。

from decimal import Decimal

def records(currentTime=Decimal('1.00')):
    first = True
    with open('sample.txt') as sample:
        for line in sample.readlines():
            for number in line.split():
                if Decimal(number) == currentTime:
                    if first:
                        first = False
                    else:
                        yield record
                    record = [number]
                    currentTime += Decimal('0.1')
                else:
                    record.append(number)
    yield record

for record in records():
    print (record)

这是输出。

['1.00', '3', '4.3', '5.6', '2.3', '4', '12.4', '0.5', '10.2']
['1.10', '8', '5.9', '11.2', '7.3']
['1.20', '8', '0.2', '1.2', '4.2', '11', '23.1', '4.0', '7.3', '13', '4.4', '1.7', '0.5']

编辑:此版本在相同的行上运行,但不假设数字不能跨越记录中断。它使用流I / O.你要改变的主要是数据的大小,当然还有来源。

from decimal import Decimal
from io import StringIO
sample = StringIO('''1.00 3 4.3 5.6 2.3 4 12.4 0.5 10.2 1.10 8 \n5.9 11.2 7.3 1.20 8\n.15 0.2 1.2 4.2 11 23.1 4.0 \n7.3 13 4.4 1.7 0.5''')

def records(currentTime=Decimal('1.00')):
    first = True
    previousChunk = ''
    exhaustedInput = False
    while True:
        chunk = sample.read(50)
        if not chunk: 
            exhaustedInput = True
            chunk = previousChunk
        else:
            chunk = (previousChunk + chunk).replace('\n', '')
        items = chunk.split()
        for number in items[:len(items) if exhaustedInput else -1]:
            if Decimal(number) == currentTime:
                if first:
                    first = False
                else:
                    yield record
                record = [number]
                currentTime += Decimal('0.1')
            else:
                record.append(number)
        if exhaustedInput:
            yield record
            break
        else:
            previousChunk = chunk.split()[-1]

for record in records():
    print (record)

这是输出。

['1.00', '3', '4.3', '5.6', '2.3', '4', '12.4', '0.5', '10.2']
['1.10', '8', '5.9', '11.2', '7.3']
['1.20', '8.15', '0.2', '1.2', '4.2', '11', '23.1', '4.0', '7.3', '13', '4.4', '1.7', '0.5']

答案 1 :(得分:1)

使用自定义标头方法的生成器解决方案。宽松地基于Finding all Amazon AWS Instances That Do Not Have a Certain Tag

输入:

' 1.00 3 4.3 5.6\n 2.3\n 4 12.4 0.5 10.2 1.10 8 5.9 11.2\n 7.3 1.20 8 0.2 1.2\n 4.2 11 23.1 4.0\n 7.3\n 13 4.4 1.7 0.5'

输出:

['1.00', '3', '4.3', '5.6', '2.3', '4', '12.4', '0.5', '10.2']
['1.10', '8', '5.9', '11.2', '7.3']
['1.20', '8', '0.2', '1.2', '4.2', '11', '23.1', '4.0', '7.3', '13', '4.4', '1.7', '0.5']

来源:

#!/usr/bin/env python3

from contextlib import suppress
from functools import partial

# yields strings from a file based on custom headers
#
# f                      a file like object supporting read(size)
# index_of_next_header   a function taking a string and returning
#                        the position of the next header or raising
#                        (default = group by newline)
# chunk_size             how many bytes to read at a time
def group_file_by_custom_header(f,
                                index_of_next_header=lambda buf: buf.index('\n') + 1,
                                chunk_size=10):
    buf = ''
    for chunk in iter(partial(f.read, chunk_size), ''):
        buf += chunk
        with suppress(ValueError):
            while True:
                pos = index_of_next_header(buf)
                yield buf[:pos]
                buf = buf[pos:]
    if buf:
        yield buf


# Pass an empty list to data
def index_of_next_timestamp(buf, data):
    def next_timestamp(buf):
        next_ts = buf.strip().split(maxsplit=2)
        if len(next_ts) < 2:
            raise ValueError()
        return '{:4.2f}'.format(float(next_ts[0]) + 0.1)

    if not data:
        data.append(next_timestamp(buf))
    pos = buf.index(data[0])
    data[0] = next_timestamp(buf[pos:])
    return pos

def get_dummy_file():
    import io
    data = ' 1.00 3 4.3 5.6\n 2.3\n 4 12.4 0.5 10.2 1.10 8 5.9 11.2\n 7.3 1.20 8 0.2 1.2\n 4.2 11 23.1 4.0\n 7.3\n 13 4.4 1.7 0.5'
    return io.StringIO(data)

data_file = get_dummy_file()

header_fn = partial(index_of_next_timestamp, data=[])
for group in group_file_by_custom_header(data_file, header_fn):
    print(repr(group.split()))

答案 2 :(得分:1)

我不知道为什么以前没有发生这种情况。您可以使用词法扫描程序逐个元素地读取元素。我使用过Python附带的那个,即shlex。它的优点是它可以在流输入上运行,不像一些比较流行的,我明白。这似乎更简单。

from io import StringIO
sample = StringIO('''1.00 3 4.3 5.6 2.3 4 12.4 0.5 10.2 1.10 8 \n5.9 11.2 7.3 1.20 8\n.15 0.2 1.2 4.2 11 23.1 4.0 \n7.3 13 4.4 1.7 0.5''')

from shlex import shlex
lexer = shlex(instream=sample, posix=False)
lexer.wordchars = r'0123456789.\n'
lexer.whitespace = ' '
lexer.whitespace_split = True

from decimal import Decimal

def records(currentTime=Decimal('1.00')):
    first = True
    while True:
        token = lexer.get_token()
        if token:
            token = token.strip()
            if not token:
                break
        else:
            break
        token = token.replace('\n', '')
        if Decimal(token) == currentTime:
            if first:
                first = False
            else:
                yield record
            currentTime += Decimal('0.1')
            record = [float(token)]
        else:
            record.append(float(token))
    yield record

for record in records():
    print (record)

输出是:

[1.0, 3.0, 4.3, 5.6, 2.3, 4.0, 12.4, 0.5, 10.2]
[1.1, 8.0, 5.9, 11.2, 7.3]
[1.2, 8.15, 0.2, 1.2, 4.2, 11.0, 23.1, 4.0, 7.3, 13.0, 4.4, 1.7, 0.5]

答案 3 :(得分:0)

如果是我,我会编写生成器函数包装器以提供所需的详细程度:

def by_spaces(fp):
    for line in fp:
        for word in line.split():
            yield word

def by_numbers(fp):
    for word in by_spaces(fp):
        yield float(word)

def by_elements(fp):
    fp = by_numbers(fp)
    start = next(fp)
    result = [start]
    for number in fp:
        if abs(start+.1-number) > 1e-6:
            result += [number]
        else:
            yield result
            result = [number]
            start = number
    if result:
        yield result

with open('x.in') as fp:
    for element in by_elements(fp):
        print (element)