定义 xrange 后的 StopIteration

StopIteration after defining xrange

我编写了以下代码来在文本文件中定义 4 行的块,如果块的第 2 行仅由一种字符组成,则输出该块。假设(并且之前已验证)第二行始终由 36 个字符的字符串组成。

# filter out homogeneous reads

import sys
import collections
from collections import Counter

filename1 = sys.argv[1] # file to process

with open(filename1,'r') as input_file:
    for line1 in input_file:
        line2, line3, line4 = [next(input_file) for line in xrange(3)]
        c = Counter(line2).values() # count characters in line2
        c.sort(reverse=True) # sort values in descending order
        if c[0] < 36:
            print line1 + line2 + line3 + line4.rstrip()

但是,我收到如下 StopIteration 错误。如果有人能告诉我原因,我将不胜感激。

$ python code.py test.file > testout.file
Traceback (most recent call last):
  File "code.py", line 11, in <module>
    line2, line3, line4 = [next(input_file) for line in xrange(3)]
StopIteration

我们将不胜感激任何帮助,尤其是那些解释我的特定代码有什么问题以及如何修复它的帮助。下面是一个输入示例:

@1:1:1323:1032:Y
AGCAGCATTGTACAGGGCTATCATGGAATTCTCGGG
+1:1:1323:1032:Y
HHHBHHBHBHGBGGGH8HHHGGGGFHBHHHHBHHHH
@1:1:1610:1033:Y
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+1:1:1610:1033:Y
HHEHHHHHHHHHHHBGGD>GGD@G8GGGGDHBHH4C
@1:1:1679:1032:Y
CGGTGGATCACTCGGCTCGTGCGTCGATGAAGAACG

如果您的文件中的行数不能除以 4 而没有余数,您将得到这个。然后您将尝试读取不存在的行。您需要计算空行数。

如果行数不足以处理,一个解决方案是停止处理文件:

try:
    line2, line3, line4 = [next(input_file) for line in xrange(3)]
except StopIteration:
    break

这感觉有点干净:

while True:
    try:
        line1, line2, line3, line4 = [next(input_file) for line in xrange(4)]
except StopIteration:
    break

因为您只在一处而不是两处推进迭代器。

您的示例输入已经显示了问题:那里有 10 行,不能被 4 整除。所以当您阅读最后一个块时,您会得到 line1line2 但是对于next() 调用 line3,输入已用完,您什么也得不到。

您的完整输入文件中可能也有同样的问题:行数根本不能被 4 整除。

有几种方法可以克服这个问题。最好的办法可能是修复你的输入,因为你似乎一直期待四行,如果这不是输入文件给出的内容,那么似乎存在内容问题。

另一个非常简单的修复方法是使用 next():

指定默认值
line2, line3, line4 = [next(input_file, '') for line in xrange(3)]

现在,当 next() 失败时,将返回默认值 ''。因此,即使文件已用完,您仍然可以获得一些内容。

然而,一个可能更好的解决方案是修复您迭代文件的方式。您有两个位置可以访问同一个文件迭代器,一次在外部 for 循环中,三次在列表理解中。它可能看起来很简单,所以你不会 运行 陷入其他问题,但你真的应该尝试改变它,这样你只有一个位置可以遍历迭代器;或者只使用 next() 调用,但将它与 for 循环混合似乎是个坏主意。

例如,您可以使用 grouper itertools recipe 以四个一组的方式干净地迭代文件:

with open(filename1, 'r') as input_file:
    for line1, line2, line3, line4 in grouper(input_file, 4, fillvalue=''):
        # do things with the lines

您有 10 行,因此它可以迭代 2 次,然后 2 行短缺。这是 Python 无法读取足够行并抛出 StopIteration 的地方。

检查这段代码,我稍微更新了它:

import sys
import collections
from collections import Counter

filename1 = sys.argv[1] # file to process

with open(filename1,'r') as input_file:
    while True:
        try:
            line1, line2, line3, line4 = [next(input_file) for line in xrange(4)]
        except StopIteration:
            print "Not enough lines to read!"
            break

        c = Counter(line2).values() # count characters in line2
        c.sort(reverse=True) # sort values in descending order
        if c[0] < 36:
            print line1 + line2 + line3 + line4.rstrip()
        else:
            print "Skipping 4 lines since less than 36 characters"