什么是pythonic方法从文件中读取一行而不是推进你在文件中的位置?
例如,如果您有
文件cat1
cat2
cat3
你做file.readline()
,你会得到cat1\n
。下一个file.readline()
将返回cat2\n
。
是否有file.some_function_here_nextline()
这样的功能可以获得cat1\n
,然后您可以file.readline()
然后返回cat1\n
?
答案 0 :(得分:32)
据我所知,没有内置功能,但这样的功能很容易编写,因为大多数Python file
对象支持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
答案 1 :(得分: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
答案 2 :(得分: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()
之前查看下一行,以推进可迭代p
。我们现在可以再次致电peek()
查看下一行。
p.peek()
# 'cat2\n'
另请参阅more_itertools
docs,因为peekable
允许您在推进之前将prepend()
个项目转换为可迭代项。
答案 3 :(得分:1)
手动做这件事并不难:
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
希望这有帮助!
答案 4 :(得分:0)
使用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")