【问题标题】:IndexError: list index out of range, but list length OKIndexError:列表索引超出范围,但列表长度正常
【发布时间】:2016-12-08 04:17:57
【问题描述】:

编程新手,希望对正在发生的事情有更深入的了解。

目标:打开一个文件并打印前 10 行。 (类似于 head 命令)

代码:

with open('file') as f: 
      for i in range(0,10): 
          print([line.strip('\n') for line in f][i])

结果:打印第一行正常,然后返回超出范围的错误

文件:是一个20行的简单文本文件,每行不超过50个字符

仅供参考 - 删除范围线并打印类型(列表)和长度(20)。打印特定索引没有问题(除非连续 >1 个)

能够使用不同的代码得到想要的结果,但是尝试使用 with/as 来改进

【问题讨论】:

  • 尝试:with open('file') as f: for line in f.readlines():
  • @eyllanesc 或者只是for line in f

标签: python-3.x list-comprehension


【解决方案1】:

您实际上可以迭代文件。你应该在这里做什么。

with open('file') as f:
    for i, line in enumerate(file, start=1):
        # Get out of the loop if we hit 10 lines
        if i >= 10:
            break
        # Line already has a '\n' at the end
        print(line, end='')

您的代码失败的原因是您的列表理解:

[line.strip('\n') for line in f]

第一次通过循环消耗文件中的所有行。现在你的文件没有更多的行了,所以下一次通过它创建文件中所有行的列表并尝试获取[1]st 元素。但这不存在,因为文件末尾没有行。

如果您希望大部分代码保持原样,您可以这样做

lines = [line.rstrip('\n') for line in f]
for i in range(10):
    print(lines[i])

但这也很愚蠢,因为你可以这样做

lines = f.readlines()

但是如果你只想到第 10 行,那也是 愚蠢的,因为你可以这样做:

with open('file') as f:
    print('\n'.join(f.readlines()[:10]))

一些进一步的解释:

修复代码的最短和最差方法是添加一行代码:

with open('file') as f: 
      for i in range(0,10):
          f.seek(0)  # Add this line
          print([line.strip('\n') for line in f][i])

现在您的代码可以运行了 - 但这是让您的代码运行的一种可怕方式。 原因 你的代码没有按照你期望的方式工作,首先是文件是可消耗的迭代器。这意味着当你从他们那里阅读时,你最终会用完要阅读的东西。这是一个简单的例子:

import io

file = io.StringIO('''
This is is a file
It has some lines
okay, only three.
'''.strip())

for line in file:
    print(file.tell(), repr(line))

这个输出

18 'This is is a file\n'
36 'It has some lines\n'
53 'okay, only three.'

现在如果你尝试从文件中读取:

print(file.read())

你会看到它没有输出任何东西。那是因为您已经“使用”了该文件。我的意思是显然它仍在磁盘上,但迭代器已到达文件末尾。但如图所示,可以在文件中查找。

print(file.tell())
file.seek(0)
print(file.tell())
print(file.read())

您会看到打印的整个文件。但是那些其他职位呢?

file.seek(36)
print(file.read())  # => okay, only three.

作为旁注,您还可以指定阅读量:

file.seek(36)
print(file.read(4))  # => okay
print(file.tell())  # => 40

因此,当我们从文件中读取或对其进行迭代时,我们会使用迭代器并到达文件末尾。让我们使用您的新工具并返回到您的原始代码并探索正在发生的事情。

with open('file') as f:
    print(f.tell())
    lines = [line.rstrip('\n') for line in f]
    print(f.tell())
    print(len([line for line in f]))
    print(lines)

您会看到您位于文件中的不同位置。第二个列表推导产生一个空列表。这是因为在评估列表推导时,它会立即执行。所以当你这样做时:

for i in range(10):
    print([line.strip('\n') for line in f][i])

您第一次执行的操作是i = 0,然后列表解析读取到文件末尾。现在它采用列表的[0]th 元素,或文件中的第一行。但是您的文件迭代器位于文件末尾。

所以现在我们回到列表的开头和i = 1。现在我们迭代到文件的末尾,但我们已经到了末尾,所以没有要读取的行,并且我们有一个空列表[],我们尝试获取其中的[0]th 元素。但是那里什么都没有。所以我们得到了一个IndexError

列表推导可能很有用,但是当您开始时,编写 for 循环然后将其转换为列表推导通常要容易得多。所以你可能会这样写:

with open('file') as f:
    for i, line in enumerate(file, start=10):
        if i < 10:
            print(line.rstrip())

现在,我们不应该在列表推导式中打印,而是收集所有内容。我们首先放置我们想要的:

[line.rstrip()

现在添加 for 位:

[line.rstrip() for i, line in enumerate(f)

最后添加过滤器和我们的右大括号:

[line.rstrip() for i, line in enumerate(f) if i < 10]

有关列表推导的更多信息,这是一个很棒的资源:@​​987654321@

【讨论】:

  • 谢谢你,韦恩。在我的“for”前面使用代码仍然是我试图绕过的东西。
  • 如果这解决了您的问题,您可以(并且应该)通过左侧的复选标记将其标记为已接受 &lt;------。如果它特别有用,您也可以投票。当您说“在我的for 前面使用代码”时,您的意思是列表理解(例如[thing for thing in data]),还是您的意思是将for 嵌套在另一个块中,例如with open(...) as f: for line in f: print(line)
  • ***啊,好吧,所以当我在“for”语句前面使用“line.strip”时,它实际上将“line.strip”应用于文件中的每一行并将结果输出到行列表。当我再次迭代它时,它知道(不知何故)它已经处理了文件中的所有行,所以它没有任何东西可以使用,只是输出一个空列表 - 因此出现错误。感谢您花时间解释这一点。 :)
  • 完成并完成 :) 是的,我的意思是列表理解部分。
  • 是的 - 你的推理是正确的。我正在为我的答案添加更多内容,以更清楚地解释您现有的代码:)
猜你喜欢
  • 1970-01-01
  • 2011-10-31
  • 2015-06-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多