我把做好的聊天机器人给群里的朋友测试,他测试的语句是这样的:“你好”,“你会说中文吗?”,“你吃饭了吗?”
因为我之前没告诉他只支持简单的英文,他就理所当然地用中文测试。测试后,他问我,可以加上中文支持吗?我说:“没问题,加上足够的关键字判断和语句库而已。”,后来思考一下,发觉没这么简单。
中文和英文有个最大的不同是,英文词组之间是分开的,有空格或标点符号,而中文因为字的表意功能太强,差不多每个字都有自己的意思,
所以词组之间没有空格隔开,而我用的机器人是基于词组工作的,如果要支持中文,就以为着必须把中文句子进行分词。
比如:我去学校上课
要转换为:我|去|学校|上课
那么具体如何分词呢?
我认为,之所以“我去学校上课”可以分割为"我|去|学校|上课"是因为"我","去","学校","上课"这几个元素都是词组,而且这么切分非常恰当,能表达需要表达的意思,而切分为
我去|学|校上|课
就会有一些词莫名其妙,“我去“不是个,词“校上”也什么都不是。
综上所诉,分词要以词为单位分,也就是分出的每个元素都能完整表意,都是个“词”。
还有个问题,那就是如果遇到这种情况
我学习编译原理
该如何切割呢?
我|学习|编译|原理
我|学习|编译原理
都满足我们上面提出的条件,切出的都是词。但是我们要选择后者,因为“编译原理“作为一个整体相对于“我学习编译“后再来个原理好理解。
当然也有反例,比如
我吃饭团
可以切割为
我|吃|饭团
我|吃饭|团
明显前者才是正确的切割法,但是又不符合我们刚才提出的切更长的词更好。我估计这个问题可以通过切出的全体词组的合理性分析,词组使用频率分析,语义分析等等方法解决,但是那样就复杂了,而我现在只是简单地做一个聊天机器人而已。
现在我们就按从左向右切词,切最长词的方法来切词。
首先,我们需要一个单词列表,作为切词的依据。单词列表我是从CEDIT汉英辞典提取的,总共包含31992个词,比较少,但是做为测试用也足够了。使用提取工具把辞典索引转换为单词列表。提取工具在:StarDict格式辞典单词列表提取工具
$ ./a.out cedict-gb.idx > wordlist.txt
因为我们是根据最长词语进行匹配的,所以需要把提取的词语按长度进行排序,程序如下:
#!/usr/bin/python import sys import codecs if len(sys.argv) != 3: print "usage: <prg> <src_file> <dst_file>" exit(1) f = codecs.open(sys.argv[1], encoding="utf8", mode = "r") wordlist =[] line = f.readline() while line: wordlist.append(line) line = f.readline() f.close() wordlist.sort(key=lambda x:len(x), reverse=True) f = codecs.open(sys.argv[2], encoding="utf8", mode ="w") for item in wordlist: f.write(item) f.close()
使用如下命令转换
$ ./sort_items.py wordlist sorted_wordlist
就可以得到排序好的wordlist。
现在是匹配单词和分词程序的主体了:
#!/usr/bin/python # -*- coding: utf-8 -*- import sys, codecs # Regard english char strings are words def isEnglishCharOrNumber(ch): if ('A' <= s[s_pos] <= 'Z') or ('a' <= s[s_pos] <= 'z') or ('0' <= s[s_pos] <= '9'): return True else: return False # Arguments number checking if len(sys.argv) != 3: print "usage :"+sys.argv[0] + " <wordlist> <sentense>" exit(1) # Read words from wordlist f = codecs.open(sys.argv[1], encoding="utf8", mode = "r") wordlist = [] line = f.readline() while line: wordlist.append(line.strip()) line = f.readline() f.close() # Split sentence into words s_pos = 0 s = unicode(sys.argv[2], 'utf-8') while True: if s_pos >= len(s): break else: if s_pos > 0:sys.stdout.write("|") found = False if isEnglishCharOrNumber(s[s_pos]): s_pos_old = s_pos while s_pos < len(s) and isEnglishCharOrNumber(s[s_pos]): s_pos += 1 print s[s_pos_old: s_pos], continue for word in wordlist: word_length = len(word) pick_word = s[s_pos: s_pos + word_length] pick_word_length = len(pick_word) if pick_word_length < word_length: continue if pick_word == word: found = True s_pos += word_length print word, break if not found: print s[s_pos], s_pos += 1
我们可以测试一下分词效果
$ ./word.py sorted_wordlist 在UNIX操作系统下 我一般使用VIM编辑文件 在|UNIX|操作系统|下|我|一般|使用|VIM|编辑|文件 $ ./word.py sorted_wordlist 如果今天下雨那么 我们就取消活动 如果|今天|下雨|那么|我们|就|取消|活动 $