28

从文件中读取一行但不推进你在文件中的位置的pythonic方法是什么?

例如,如果您有一个文件

cat1
cat2
cat3

file.readline()会得到cat1\n的。下一个file.readline()将返回cat2\n

是否有一些file.some_function_here_nextline()想要获得的功能,cat1\n然后您可以稍后再做file.readline()并返回cat1\n

4

5 回答 5

42

据我所知,没有内置功能,但这样的功能很容易编写,因为大多数 Pythonfile对象都支持在文件中跳转的方法seek和方法。tell所以,这个过程非常简单:

  • 使用 查找文件中的当前位置tell
  • 执行某种read(或write)操作。
  • seek回到前一个文件指针。

这使您可以做一些不错的事情,例如从文件中读取一大块数据,对其进行分析,然后可能用不同的数据覆盖它。该功能的简单包装器可能如下所示:

def peek_line(f):
    pos = f.tell()
    line = f.readline()
    f.seek(pos)
    return line

print peek_line(f) # cat1
print peek_line(f) # cat1

你可以很容易地为其他read方法实现同样的事情。例如,为 实施相同的事情file.read

def peek(f, length=1):
    pos = f.tell()
    data = f.read(length) # Might try/except this line, and finally: f.seek(pos)
    f.seek(pos)
    return data

print peek(f, 4) # cat1
print peek(f, 4) # cat1
于 2013-05-30T15:56:29.657 回答
5

您可以使用itertools.tee包装文件并取回两个迭代器,请记住文档中所述的注意事项

例如

from itertools import tee
import contextlib
from StringIO import StringIO
s = '''\
cat1
cat2
cat3
'''

with contextlib.closing(StringIO(s)) as f:
  handle1, handle2 = tee(f)
  print next(handle1)
  print next(handle2)

 cat1
 cat1
于 2013-05-30T15:54:46.847 回答
2

手动操作并不难:

f = open('file.txt')
line = f.readline()
print line
>>> cat1
# the calculation is: - (length of string + 1 because of the \n)
# the second parameter is needed to move from the actual position of the buffer
f.seek((len(line)+1)*-1, 1)
line = f.readline()
print line
>>> cat1

您可以将其包装在这样的方法中:

def lookahead_line(file):
    line = file.readline()
    count = len(line) + 1
    file.seek(-count, 1)
    return file, line

并像这样使用它:

f = open('file.txt')
f, line = lookahead_line(f)
print line

希望这可以帮助!

于 2013-05-30T16:03:15.150 回答
2

more_itertools库提供了一个peekable类,允许您peek()在不推进迭代的情况下前进。

with open("file.txt", "r") as f:
    p = mit.peekable(f.readlines())

p.peek()
# 'cat1\n'

next(p)
# 'cat1\n'

next()我们可以在调用推进 iterable之前查看下一行p。我们现在可以通过peek()再次调用来查看下一行。

p.peek()
# 'cat2\n'

另请参阅more_itertoolsdocs,因为peekable它还允许您prepend()在推进之前将项目添加到可迭代对象中。

于 2017-08-30T19:15:48.700 回答
2

带有tell()/的解决方案seek()不适用于stdin和其他迭代器。更通用的实现可以像这样简单:

class lookahead_iterator(object):
    __slots__ = ["_buffer", "_iterator", "_next"]
    def __init__(self, iterable):
        self._buffer = [] 
        self._iterator = iter(iterable)
        self._next = self._iterator.next
    def __iter__(self):
        return self 
    def _next_peeked(self):
        v = self._buffer.pop(0)
        if 0 == len(self._buffer):
            self._next = self._iterator.next
        return v
    def next(self):
        return self._next()
    def peek(self):
        v = next(self._iterator)
        self._buffer.append(v)
        self._next = self._next_peeked
        return v

用法:

with open("source.txt", "r") as lines:
    lines = lookahead_iterator(lines)
    magic = lines.peek()
    if magic.startswith("#"):
        return parse_bash(lines)
    if magic.startswith("/*"):
        return parse_c(lines)
    if magic.startswith("//"):
        return parse_cpp(lines)
    raise ValueError("Unrecognized file")
于 2017-10-21T20:57:07.120 回答