我觉得我的问题与Why does takewhile() skip the first line?
有关我在那里找不到满意的答案。
以下示例使用以下模块
import csv
from itertools import takewhile
这是我的问题。 我有一个csv文件,我想用itertools解析。
例如,我想将标题与内容分开。 第一列中存在关键字就会发现这一点。
以下是file.csv
示例
a, content
b, content
KEYWORD, something else
c, let's continue
两个第一行组成文件的标题。
KEYWORD
行将其与内容分开:最后一行。
即使如果它不是内容的正确部分,我也想解析分隔行。
with open('file.csv', 'rb') as f:
reader = csv.reader(f)
header = takewhile(lambda x: x[0] != 'KEYWORD', reader)
for row in header:
print(row)
print('End of header')
for row in reader:
print(row)
我没想到会这样,但会跳过KEYWORD
行。
正如您将在以下输出中看到的那样:
['a', ' content']
['b', ' content']
End of header
['c', " let's continue"]
我已经尝试过模拟csv阅读器,看它是否来自那里。 但显然不是。 以下代码产生相同的行为。
l = [['a', 'content'],
['b','content'],
['KEYWORD', 'something else'],
['c', "let's continue"]]
i = iter(l)
header = takewhile(lambda x: x[0] != 'KEYWORD', i)
for row in header:
print(row)
print('End of header')
for row in i:
print(row)
如何使用takewhile的功能,同时防止以下内容跳过未解析的行?
据我所知,第一个for
在迭代器上调用next
来测试其内容。
第二次再次呼叫next
,以收集价值。
因此跳过了分隔行。
答案 0 :(得分:2)
我认为你必须进行重组 - takewhile
并不适合你正在做的事情。问题是takewhile
必须读取从'KEYWORD'
开始的行,以确定它已经到达了它不应该采取的行,并且一旦读取了该行文件"读头"是在下一行的开头。同样,对于iter
,takewhile
已经消耗(但丢弃)当您启动'KEYWORD'
时开始for row in i
的行。
一种替代方案可能是:
header = []
content = []
target = header
for row in reader:
if line.startswith('KEYWORD'):
target = content
target.append(row)
答案 1 :(得分:0)
感谢@jonrsharpe,我开始质疑自己的一些代码技巧。 这是我达到的目的:
class RewindableFile(file):
def __init__(self, *args, **kwargs):
nb_backup = kwargs.pop('nb_backup', 1)
super(RewindableFile, self).__init__(*args, **kwargs)
self._nb_backup = nb_backup
self._backups = []
self._time_anchor = 0
def next(self):
if self._time_anchor >= 0:
item = super(RewindableFile, self).next()
self._backup(item)
return item
else:
item = self._forward()
return item
def rewind(self):
self._time_anchor = self._time_anchor - 1
time_bound = min(self._nb_backup, len(self._backups))
if self._time_anchor < -time_bound:
raise Exception('You have gone too far in history...')
def __iter__(self):
return self
def _backup(self, row):
self._backups.append(row)
extra_items = len(self._backups) - self._nb_backup
if extra_items > 0:
del self._backups[0:extra_items]
def _forward(self):
item = self._backups[self._time_anchor]
self._time_anchor = self._time_anchor + 1
return item
以及我如何使用它:
with RewindableFile('csv.csv', 'rb') as f:
def test_kwd_and_rewind(x):
if x[0] != 'KEYWORD':
return True
else:
f.rewind()
return False
reader = csv.reader(f)
header = takewhile(test_kwd_and_rewind, reader)
for row in header:
print(row)
print('End of header')
for row in reader:
print(row)
我还可以重载read
和readline
函数来保存jump
。
但我在这里不需要它们。
答案 2 :(得分:0)
你可以像这样自己写。
def takewhile(predicate, iterable):
for x in iterable:
yield x
if not predicate(x):
break
试验:
>>> list(takewhile(lambda x:x!=3, range(10)))
[0, 1, 2, 3]
答案 3 :(得分:0)
jonrsharpe说得对。这不是一个需要的工作。 itertools还有一个groupby函数,可以更轻松地处理拆分。下面的LastHeader
类保留了通过check
方法传递的最后一个标题行的记录,并在每次调用check
时返回对它的引用。
这使您可以一次浏览文件,而无需回溯任何文件。
class LastHeader():
"""Checks for new header strings. For use with groupby"""
def __init__(self, sentinel='#'):
self.sentinel = sentinel
self.lastheader = ''
def check(self, line):
if line.startswith(self.sentinel):
self.lastheader = line
return self.lastheader
with open(fname, 'r') as fobj:
lastheader = LastHeader(sentinel)
for headerline, readlines in groupby(fobj, lastheader.check):
foo(headerline)
for line in readlines:
bar(line)
其中foo
和bar
是您需要对标头和数据进行的任何处理。