首页 > 解决方案 > str.split() 返回一个 AttributeError: 'NoneType' 对象没有属性 'split'

问题描述

我很难在 \n 上拆分字符串。我正在将一个约 138M 长的日语字符串传递到一个标记器/单词标记器中,并且我得到了“AttributeError:'NoneType'对象没有属性'split'”错误。

分词器的名称是 MeCab,它的作用是获取一个字符串,在其中找到单词,然后返回一个具有单词特征(名词、粒子、阅读等)的字符串。分词器在字符串中标记的词是用“\n”分割,所以我想用新行把它分割成一个列表。

字符串的前 25 个字符:

str_text[:25]

输出:

'このページは以下にある削除依頼の議論を保存したもの'

当我使用下面的代码拆分前 100 万个字符时,我没有错误,但是当我将其扩展到 1000 万个时,我得到了上面提到的错误。

前 25 个字符的代码和输出:

import MeCab

#opening the file containing the long string with Japanese text
file = open('output_text.txt')
str_text = file.read()

#passing string into the MeCab tokenizer/tagger and splitting the long string into a list based on
tagger = MeCab.Tagger()
words = tagger.parse(str_text[:25]).split('\n')[:-2] #last two entries are just some tagger info

for word in words:
    temp_str = word.split('\t')
    print(temp_str)

输出(第一个元素是单词,第二个元素包含有关单词的信息):

['この', '連体詞,*,*,*,*,*,この,コノ,コノ']
['ページ', '名詞,一般,*,*,*,*,ページ,ページ,ページ']
['は', '助詞,係助詞,*,*,*,*,は,ハ,ワ']
['以下', '名詞,非自立,副詞可能,*,*,*,以下,イカ,イカ']
['に', '助詞,格助詞,一般,*,*,*,に,ニ,ニ']
['ある', '動詞,自立,*,*,五段・ラ行,基本形,ある,アル,アル']
['削除', '名詞,サ変接続,*,*,*,*,削除,サクジョ,サクジョ']
['依頼', '名詞,サ変接続,*,*,*,*,依頼,イライ,イライ']
['の', '助詞,連体化,*,*,*,*,の,ノ,ノ']
['議論', '名詞,サ変接続,*,*,*,*,議論,ギロン,ギロン']
['を', '助詞,格助詞,一般,*,*,*,を,ヲ,ヲ']
['保存', '名詞,サ変接続,*,*,*,*,保存,ホゾン,ホゾン']
['し', '動詞,自立,*,*,サ変・スル,連用形,する,シ,シ']
['た', '助動詞,*,*,*,特殊・タ,基本形,た,タ,タ']
['もの', '名詞,非自立,一般,*,*,*,もの,モノ,モノ']

我替换了 str_text 文件中出现的所有“\n”,所以这不是问题。字符串不能真正通过一个字符传递到标记器/标记器,因为它根据长字符串确定 str 是一个单词。它适用于前 1M 字符但在 10M 时失败的事实告诉我,这个我是千万分之一。我已经寻找了几个小时的解决方案,但找不到任何可以帮助解决此问题的方法。我可能会以 1M 块的形式传递字符串,但是当某处可能有解决方案时,丢失这么多数据感觉不对。

任何帮助将不胜感激。

@mrivanlima 感谢您修正我帖子的语法

@Karl Knechtel 在评论中建议解决了我的问题。谢谢!

对于那些感兴趣的人,下面是最终工作的完整代码:

%%time

#load the txt file with Japanese characters:
file = open('output_text.txt')
str_text = file.read()

#boundries for the text blocks used in the below for loop
lower = 0
upper = 100000

#dictionary for words and kanji characters
counts_words = dict()
counts_kanji = dict()

word_counter = 0

#tokenizer/tagger
tagger = MeCab.Tagger()

#splits strings into a list, used for words that have more than one character to get individual characters
def splitter(word): 
    return list(word)

#break condition for the loop
condition = 'no'

while True:
    if condition == 'yes':
        break

    #this is for the last block of 100k increments
    elif lower > 133400001:
        #initiate break condition
        condition = 'yes'
        words = tagger.parse(str_text[lower:]).split('\n')[:-2]
        print('Last block, chief!',lower,':',upper)
        lower+=100000
        upper+=100000
        for word in words:
            temp_str = word.split('\t')
            word_counter+=1
            counts_words[temp_str[0]+' '+temp_str[1]] = counts_words.get(temp_str[0]+' '+temp_str[1], 0) + 1
            if len(temp_str[0])>1:
                for i in splitter(temp_str[0]):
                    counts_kanji[i] = counts_kanji.get(i, 0) + 1
                    break
            else:
                counts_kanji[temp_str[0]] = counts_kanji.get(temp_str[0], 0) + 1
                break

    else:
        #pass string 100k long string block into a tokenizer/tagger
        words = tagger.parse(str_text[lower:upper]).split('\n')[:-2]
        
        #increment the lower and upper boundries of the str blocks
        lower+=100000
        upper+=100000
        
        #iterate through each word parsed by the tokenizer
        for word in words:
            temp_str = word.split('\t') #split each word data by tab, [word, info]
            word_counter+=1 #count number of words
            
            #check if the entry exists in the words dict, either add or increment the counts
            counts_words[temp_str[0]+' '+temp_str[1]] = counts_words.get(temp_str[0]+' '+temp_str[1], 0) + 1
            
            #check if the word has more than one character, if yes split it and add each character to the kanji dict
            if len(temp_str[0])>1:
                for i in splitter(temp_str[0]):
                    #check if the entry exists in the words dict, either add or increment the counts
                    counts_kanji[i] = counts_kanji.get(i, 0) + 1
            else:
                counts_kanji[temp_str[0]] = counts_kanji.get(temp_str[0], 0) + 1

输出:

Last block, chief! 133500000 : 133600000
CPU times: user 3min 7s, sys: 2.83 s, total: 3min 10s
Wall time: 3min 10s

标签: pythonsplittokenizeattributeerror

解决方案


我是 mecab-python3 的开发者。

我想你可能已经给我发了邮件,但是请不要传递 MeCab 1M 字符串。它是在假设输入是单个句子的情况下开发的。它很健壮,而且它可以处理更长的字符串——例如,你不会对段落有任何问题——但你基本上会进入未经测试的领域而没有任何好处。

在将输入文本传递给 MeCab 之前,将其拆分为段落或句子。

另外,关于这个:

我可能会以 1M 块的形式传递字符串,但是当某处可能有解决方案时,丢失这么多数据感觉不对。

传递较短的字符串不会丢失任何数据。我不确定你在这里指的是什么。


推荐阅读