【问题标题】:Why am I getting an IndexError in Python 3 when indexing a string and not slicing?为什么在索引字符串而不是切片时在 Python 3 中出现 IndexError?
【发布时间】:2015-09-19 12:07:16
【问题描述】:

我是编程新手,并且正在尝试使用 Python 3。我发现了一些处理 IndexError 的主题,但似乎对这种特定情况没有帮助。

我编写了一个函数,它打开一个文本文件,一次读取一行,然后将该行分割成单独的字符串,每个字符串都附加到一个特定的列表(记录行中的每个“列”一个列表) )。大多数切片是多个字符 [x:y],但有些是单个字符 [x]。

我收到了IndexError: string index out of range 消息,但据我所知,它不是。这是函数:

def read_recipe_file():
    recipe_id = []
    recipe_book = []
    recipe_name = []
    recipe_page = []
    ingred_1 = []
    ingred_1_qty = []
    ingred_2 = []
    ingred_2_qty = []
    ingred_3 = []
    ingred_3_qty = []

    f = open('recipe-file.txt', 'r')  # open the file 
    for line in f:
        # slice out each component of the record line and store it in the appropriate list
        recipe_id.append(line[0:3])
        recipe_name.append(line[3:23])
        recipe_book.append(line[23:43])
        recipe_page.append(line[43:46])
        ingred_1.append(line[46]) 
        ingred_1_qty.append(line[47:50])
        ingred_2.append(line[50]) 
        ingred_2_qty.append(line[51:54])
        ingred_3.append(line[54]) 
        ingred_3_qty.append(line[55:])
    f.close()
return recipe_id, recipe_name, recipe_book, recipe_page, ingred_1, ingred_1_qty, ingred_2, ingred_2_qty, ingred_3, \
       ingred_3_qty

这是回溯:

Traceback (most recent call last):
  File "recipe-test.py", line 84, in <module>
    recipe_id, recipe_book, recipe_name, recipe_page, ingred_1, ingred_1_qty, ingred_2, ingred_2_qty, ingred_3, ingred_3_qty = read_recipe_file()
  File "recipe-test.py", line 27, in read_recipe_file
    ingred_1.append(line[46])

调用相关函数的代码是:

print('To show list of recipes: 1')
print('To add a recipe: 2')
user_choice = input()
recipe_id, recipe_book, recipe_name, recipe_page, ingred_1, ingred_1_qty, ingred_2, ingred_2_qty, \
ingred_3, ingred_3_qty = read_recipe_file()

if int(user_choice) == 1:
    print_recipe_table(recipe_id, recipe_book, recipe_name, recipe_page, ingred_1, ingred_1_qty,
                    ingred_2, ingred_2_qty, ingred_3, ingred_3_qty)

elif int(user_choice) == 2:
    #code to add recipe

失败的路线是这样的:

ingred_1.append(line[46])

我尝试读取的文本文件的每一行都有超过 46 个字符,所以我不明白为什么会出现越界错误(示例行如下)。如果我将代码更改为:

ingred_1.append(line[46:])

要读取切片而不是特定字符,该行会正确执行,而程序会在该行上失败:

ingred_2.append(line[50])

这让我认为它在某种程度上与从字符串中附加单个字符有关,而不是多个字符的切片。

这是我正在阅读的文本文件中的示例行:

001Cheese on Toast     Meals For Two       012120038005002

我可能应该补充一点,我很清楚这不是很好的代码 - 通常有很多方法可以改进程序,但据我所知,代码应该可以实际工作。

【问题讨论】:

  • 有空行吗?这将导致此错误。
  • 输入文件中有标签吗?尝试打印行长。
  • 我认为 unutbu 做到了——源文本文件的末尾有一个额外的换行符。删除它会发现附加代码中的另一个错误(忘记了某些列表名称末尾的 [i]) - 但是当我修复它时,一切都按预期工作。 :)
  • line[100000:] 始终是合法的,无论行长如何。您可能需要添加一个 try except 块并在异常时打印 len(line)
  • @RichCairns:很高兴你解决了这个问题。随意接受已经发布的答案之一。

标签: python string list


【解决方案1】:

如果文件中的某些行是空的或至少很短,就会发生这种情况。文件末尾的杂散换行符是一个常见原因,因为它作为额外的空白行出现。调试此类案例的最佳方法是捕获异常,并调查失败的特定line(几乎可以肯定不会是您复制的示例行):

try:
    ingred_1.append(line[46])
except IndexError:
    print(line)
    print(len(line))

捕获此异常通常也是处理错误的正确方法:您已检测到病态案例,现在您可以考虑如何处理。例如,您可能会:

  • continue,它将默默地跳过处理该行,
  • 记录一些东西然后然后continue
  • 通过提出一个新的、更具话题性的例外来解决问题:例如raise ValueError("Line too short")

打印相关的东西,不管是否继续,如果这表示输入文件存在需要修复的问题,几乎总是一个好主意。如果它是相对微不足道的事情,那么静默继续是一个不错的选择,您知道在其余处理中不会导致流错误。您可能希望通过尽早检测“完全空”的情况(例如在循环顶部执行此操作)来区分“太短”和“完全空”的情况:

if not line:
    # Skip blank lines
    continue

并适当处理另一种情况的错误。


将其更改为切片有效的原因是字符串切片从不失败。如果切片中的两个索引都在字符串之外(方向相同),您将得到一个空字符串 - 例如:

>>> 'abc'[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range
>>> 'abc'[4:]
''
>>> 'abc'[4:7]
''

【讨论】:

    【解决方案2】:

    您的代码在 line[46] 上失败,因为 line 包含少于 47 个字符。切片操作line[46:] 仍然有效,因为超出范围的字符串切片返回一个空字符串。

    你可以通过替换来验证这条线是否太短

    ingred_1.append(line[46])
    

    try:
        ingred_1.append(line[46])
    except IndexError:
        print('line = "%s", length = %d' % (line, len(line)))
    

    【讨论】:

    • 谢谢@michael - 但line 肯定包含超过 47 个字符 - 我在 OP 的源文件中发布了一个示例行。我忽略添加(我的错)是源文件中的所有行的长度完全相同。我认为 OP 中的评论者已经确定了 - 源文件末尾有一个额外的换行符。
    • 不,如果line 包含至少 47 个字符,line[46] 不会导致 IndexError。请注意,空行包含少于 47 个字符。如果您恢复到输入文件的先前版本并实现try ... catch 构造,您将看到问题。
    猜你喜欢
    • 1970-01-01
    • 2011-05-12
    • 2015-12-26
    • 2016-06-01
    • 2020-12-26
    • 1970-01-01
    • 2014-03-10
    • 2014-11-27
    • 1970-01-01
    相关资源
    最近更新 更多