You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Stanza处理文本时出现List index out of range报错解决

问题描述

我尝试从大型.txt文件中移除所有空行,但无论使用哪种方法,始终返回如下报错:

Traceback (most recent call last):
  File "C:\Users\svp12\PycharmProjects\practiques\main.py", line 53, in <module>
    doc = nlp(texts[line])
IndexError: list index out of range

如果不移除这些空行,后续2个for循环会触发IndexError,目前我用如下try/except逻辑规避报错:

try:
    for word in doc.sentences[0].words:
        noun.append(word.text)
        lemma.append(word.lemma)
        pos.append(word.pos)
        xpos.append(word.xpos)
        deprel.append(word.deprel)
except IndexError:
    errors += 1
    pass

我希望能够直接移除所有空行,无需通过捕获异常的方式规避IndexError,以下是完整项目代码:

import io
import stanza
import os


def linecount(filename):
    ffile = open(filename, 'rb')
    lines = 0
    buf_size = 1024 * 1024
    read_f = ffile.read

    buf = read_f(buf_size)
    while buf:
        lines += buf.count(b'\n')
        buf = read_f(buf_size)

    return lines


errors = 0

with io.open('@_Calvia_2018-01-01_2022-04-01.txt', 'r+', encoding='utf-8') as f:
    text = f.read()

# replacing eos with \n, numbers and symbols
texts = text.replace('eos', '.\n')
texts = texts.replace('0', ' ').replace('1', ' ').replace('2', ' ').replace('3', ' ').replace('4', ' ')\
    .replace('5', ' ').replace('6', ' ').replace('7', ' ').replace('8', ' ').replace('9', ' ').replace(',', ' ')\
    .replace('&quot;', ' ').replace('·', ' ').replace('?', ' ').replace('¿', ' ').replace(':', ' ').replace(';', ' ')\
    .replace('-', ' ').replace('!', ' ').replace('¡', ' ').replace('.', ' ').splitlines()

os.system("sed -i '/^$/d' @_Calvia_2018-01-01_2022-04-01.txt")            # removing empty lines to avoid IndexError

nlp = stanza.Pipeline(lang='ca')

nouns = []
lemmas = []
poses = []
xposes = []
heads = []
deprels = []

total_lines = linecount('@_Calvia_2018-01-01_2022-04-01.txt') - 1

for line in range(50):                                                  # range should be total_lines which is 6682
    noun = []
    lemma = []
    pos = []
    xpos = []
    head = []
    deprel = []
    # print('analyzing: '+str(line+1)+' / '+str(len(texts)), end='\r')
    doc = nlp(texts[line])
    try:
        for word in doc.sentences[0].words:
            noun.append(word.text)
            lemma.append(word.lemma)
            pos.append(word.pos)
            xpos.append(word.xpos)
            deprel.append(word.deprel)
    except IndexError:
        errors += 1
        pass
    try:
        for word in doc.sentences[0].words:
            head.extend([lemma[word.head-1] if word.head > 0 else "root"])
    except IndexError:
        errors += 1
        pass
    nouns.append(noun)
    lemmas.append(lemma)
    poses.append(pos)
    xposes.append(xpos)
    heads.append(head)
    deprels.append(deprel)

print(nouns)
print(lemmas)
print(poses)
print(xposes)
print(heads)
print(deprels)

print("errors: " + str(errors))                                                         # wierd, seems to be range/2-1
附带疑问

是否值得仅为执行移除空行的sed命令,就单独导入os模块?相关代码如下:

os.system("sed -i '/^$/d' @_Calvia_2018-01-01_2022-04-01.txt")
解决方案

问题根因

你的代码逻辑顺序存在明显问题:

  1. 先把磁盘文件的全部内容读入内存,做字符替换、splitlines()得到texts列表,这时候空行已经存在于texts里了
  2. 之后才调用sed命令修改磁盘上的原文件,这个操作完全不会改动已经加载到内存的texts列表
  3. 你用linecount读取被sed修改后的磁盘文件行数,作为循环的范围依据,和内存里texts的实际长度不匹配,自然会触发索引越界
    另外你当前运行环境是Windows,系统默认没有sed命令,那行os调用本身就是无效代码,根本没起到删除空行的作用。

修复步骤

  • 删掉无效的os.system(sed)调用和自定义的linecount函数,完全不需要为了删空行导入os模块,直接在Python内存处理阶段过滤空行即可,跨平台且可控。
    修改文本处理部分的代码,在splitlines()之后直接过滤空行,同时剔除只有空白字符的无效行:
    with io.open('@_Calvia_2018-01-01_2022-04-01.txt', 'r', encoding='utf-8') as f:
        text = f.read()
    
    # replacing eos with \n, numbers and symbols
    texts = text.replace('eos', '.\n')
    texts = texts.replace('0', ' ').replace('1', ' ').replace('2', ' ').replace('3', ' ').replace('4', ' ')\
        .replace('5', ' ').replace('6', ' ').replace('7', ' ').replace('8', ' ').replace('9', ' ').replace(',', ' ')\
        .replace('&quot;', ' ').replace('·', ' ').replace('?', ' ').replace('¿', ' ').replace(':', ' ').replace(';', ' ')\
        .replace('-', ' ').replace('!', ' ').replace('¡', ' ').replace('.', ' ').splitlines()
    # 过滤空行和纯空白行
    texts = [line.strip() for line in texts if line.strip()]
    
  • 改掉用索引遍历的写法,直接遍历过滤后的texts列表,从根源避免索引和列表长度不匹配的问题。同时把异常捕获换成前置判断,逻辑更清晰、执行效率更高:
    nlp = stanza.Pipeline(lang='ca')
    
    nouns = []
    lemmas = []
    poses = []
    xposes = []
    heads = []
    deprels = []
    errors = 0
    
    # 直接遍历过滤后的所有行,不需要手动计算行数
    for line_content in texts:
        noun = []
        lemma = []
        pos = []
        xpos = []
        head = []
        deprel = []
        doc = nlp(line_content)
        # 提前判断是否识别到有效句子,替代异常捕获
        if not doc.sentences:
            errors += 1
            continue
        for word in doc.sentences[0].words:
            noun.append(word.text)
            lemma.append(word.lemma)
            pos.append(word.pos)
            xpos.append(word.xpos)
            deprel.append(word.deprel)
        # 处理依存关系头节点,增加边界判断避免索引越界
        for word in doc.sentences[0].words:
            if word.head > 0 and word.head -1 < len(lemma):
                head.append(lemma[word.head-1])
            else:
                head.append("root")
        nouns.append(noun)
        lemmas.append(lemma)
        poses.append(pos)
        xposes.append(xpos)
        heads.append(head)
        deprels.append(deprel)
    

关于os模块导入的结论

完全不值得。
一方面sed是类Unix系统的自带命令,Windows环境默认不支持,你写的那行调用在你当前的开发环境下根本无法正常执行,属于无效代码;另一方面过滤空行是非常简单的原生字符串操作,几行Python代码就能实现,逻辑可控还没有跨平台兼容问题,完全没必要调用外部命令,自然不需要为了这个功能导入os模块。


内容的提问来源于stack exchange,提问作者Questioneer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 17:18:24