python 中文分词程序实例

以我的理解,最简单的分词程序,应该是先将中文文本切成最小的单位--汉字--再从词典里找词,将这些字按照最左最长原则(与正则精神暗合),合并为以词为单位的集合。这样的应该是最快的,只按照给定的数据划分合并即可,不必考虑语法元素的权重(词性:名动形数量代等等,语法:主谓宾定状补),以及上下文的出现次数。

关于源文本的切分,就参照《统计汉字/英文单词数》一文的思路,使用正则表达式r"(?x) (?: [w-]+  | [x80-xff]{3} )")来匹配即可。

关于词典,我使用的是CC-CEDICT的词典,原因有三:没有版权问题;速度较快;Chrome也在用它(发现了吧:在Chrome上双击中文句子,会自动选择中文词汇而不是单字或整行进行反选高亮)。

接下来是如何分词。经过思考,我发现搜索树的原理可以拿来就用。原理请见此文:Trie in Python。具体方法是,将词库逐字读入内存,建立搜索树;然后对目标文本进行逐字分析,如果该字之后还可搜索,则继续搜索;否则停止,作为一个词汇单位处理。

这样的算法理论上比较快(未进行benchmark),原因有三:使用Trie结构,本质上是哈希表,空间换时间,是O(0)级的搜索;词库只有800K,可以轻易载入,内存空间没占多少;算法最慢的部分是载入Trie的阶段,之后速度就不再受影响。

不过,谈到它的扩充性,目前只能在words.txt中手动添加新词,而不能实现机器学习。

源码
完整的程序(包括我处理过的词库列表)放在github上了。有兴趣的可以把玩一下。这里列出主程序:

 

 代码如下 复制代码

 #!/usr/bin/python
# -*- coding: utf-8 -*-
#
#author:         rex
#blog:           http://iregex.org
#filename        nlp.py
#created:        2010-09-26 19:15

import re
import sys

regex=re.compile(r"(?x) (?: [w-]+  | [x80-xff]{3} )")

def init_wordslist(fn="./words.txt"):
    f=open(fn)
    lines=sorted(f.readlines())
    f.close()
    return lines

def words_2_trie(wordslist):
    d={}
    for word in wordslist:
        ref=d
        chars=regex.findall(word)
        for char in chars:
            ref[char]=ref.has_key(char) and ref[char] or {}
            ref=ref[char]
        ref['']=1
    return d

def search_in_trie(chars, trie):
    ref=trie
    index=0
    for char in chars:
        if ref.has_key(char):
            print char,
            ref=ref[char]
            index+=1
        else:
            if index==0:
                index=1
                print char,
            print '*',
            try:
                chars=chars[index:]
                search_in_trie(chars, trie)
            except:
                pass
            break
def main():
    #init
    words=init_wordslist()
    trie=words_2_trie(words)
    #read content
    fn=sys.argv[1]
    string=open(fn).read()
    chars=regex.findall(string)
   
    #do the job
    search_in_trie(chars, trie)

if __name__=='__main__':
    main()

本机测试
测试的文本如下:

 只听得一个女子低低应了一声。绿竹翁道:“姑姑请看,这部琴谱可有些古怪。”那
女子又嗯了一声,琴音响起,调了调弦,停了一会,似是在将断了的琴弦换去,又调了调
弦,便奏了起来。初时所奏和绿竹翁相同,到后来越转越高,那琴韵竟然履险如夷,举重
若轻,毫不费力的便转了上去。令狐冲又惊又喜,依稀记得便是那天晚上所听到曲洋所奏
的琴韵。这一曲时而慷慨激昂,时而温柔雅致,令狐冲虽不明乐理,但觉这位婆婆所奏,
和曲洋所奏的曲调虽同,意趣却大有差别。这婆婆所奏的曲调平和中正,令人听着只觉音
乐之美,却无曲洋所奏热血如沸的激奋。奏了良久,琴韵渐缓,似乎乐音在不住远去,倒
像奏琴之人走出了数十丈之遥,又走到数里之外,细微几不可再闻。

理性爱国
性爱体验
我爱正则表达式
请留意末尾三行。

再看一下程序处理的结果:(*表示词汇间的分隔)

1
 只 * 听 得 * 一 个 * 女 子 * 低 低 * 应 * 了 * 一 声 * 。 * 绿 * 竹 * 翁 * 道 * : * “ * 姑 姑 * 请 看 * , * 这 * 部 * 琴 * 谱 * 可 有 * 些 * 古 怪 * 。 * ” * 那 * 女 子 * 又 * 嗯 * 了 * 一 声 * , * 琴 * 音 响 * 起 * , * 调 * 了 * 调 * 弦 * , * 停 * 了 * 一 会 * , * 似 是 * 在 * 将 * 断 * 了 * 的 * 琴 弦 * 换 * 去 * , * 又 * 调 * 了 * 调 * 弦 * , * 便 * 奏 * 了 * 起 来 * 。 * 初 * 时 * 所 * 奏 * 和 * 绿 * 竹 * 翁 * 相 同 * , * 到 * 后 来 * 越 * 转 * 越 * 高 * , * 那 * 琴 * 韵 * 竟 然 * 履 险 如 夷 * , * 举 重 * 若 * 轻 * , * 毫 不 费 力 * 的 * 便 * 转 * 了 * 上 去 * 。 * 令 狐 * 冲 * 又 * 惊 * 又 * 喜 * , * 依 稀 * 记 得 * 便 是 * 那 天 * 晚 上 * 所 * 听 到 * 曲 * 洋 * 所 * 奏 * 的 * 琴 * 韵 * 。 * 这 一 * 曲 * 时 而 * 慷 慨 * 激 昂 * , * 时 而 * 温 柔 * 雅 致 * , * 令 狐 * 冲 * 虽 * 不 明 * 乐 理 * , * 但 * 觉 * 这 位 * 婆 婆 * 所 * 奏 * , * 和 * 曲 * 洋 * 所 * 奏 * 的 * 曲 调 * 虽 * 同 * , * 意 趣 * 却 * 大 有 * 差 别 * 。 * 这 * 婆 婆 * 所 * 奏 * 的 * 曲 调 * 平 和 * 中 正 * , * 令 人 * 听 * 着 * 只 * 觉 * 音 乐 之 * 美 * , * 却 * 无 * 曲 * 洋 * 所 * 奏 * 热 血 * 如 * 沸 * 的 * 激 * 奋 * 。 * 奏 * 了 * 良 久 * , * 琴 * 韵 * 渐 * 缓 * , * 似 乎 * 乐 音 * 在 * 不 住 * 远 * 去 * , * 倒 像 * 奏 * 琴 * 之 * 人 * 走 出 * 了 * 数 十 * 丈 * 之 * 遥 * , * 又 * 走 * 到 * 数 * 里 * 之 外 * , * 细 微 * 几 * 不 可 再 * 闻 * 。 * 理 性 * 爱 国 * 性 爱 * 体 验 * 我 * 爱 * 正 则 * 表 达 式

1,实用,能满足绝大部分网络文章的分词需要。
2,快速,分词过程中不会抛出DeadlineExceededError错误。
3,低内存占用,不会因为内存占用超过限制而每个实例运行一次之后就被强制kill掉。

最初的思路是:将分词词库排序好保存在一个list对象里,然后用bisect库对词库进行快速查找。因为bisect默认是c实现的,所以匹配速度非常快,但是list对象保存的词库过于耗费内存,加载速度非常慢。完全不适合在google app engine上使用。

解决的办法是:把词库中不同长度的词分开存储在不用的str对象中,使用跟bisect库同样的二分法对词库进行匹配。

.新版论坛系列介绍之二——功能介绍篇      公告:CSDN博客频道博客搬家功能上线!     JavaEE快速开发平台G4Studio作者熊春专访
中国最大规模移动开发者高水平盛会    没有重量只有质量:iPad版《程序员杂志》应用上线    “第一次亲密接触”——有奖征文活动

python 中文分词——FMM 算法 .


分类: 各种脚本包括(python) 数据结构&算法 2009-06-23 12:04 2842人阅读 评论(2) 收藏 举报
FMM算法的最简单思想是使用贪心算法向前找n个,如果这n个组成的词在词典中出现,就ok,如果没有出现,那么找n-1个...然后继续下去。假如n个词在词典中出现,那么从n+1位置继续找下去,知道句子结束。

 代码如下 复制代码

.import re 
def PreProcess(sentence,edcode="utf-8"):  
  sentence = sentence.decode(edcode)  
  sentence=re.sub(u"[。,,!……!《》<>/"'::?/?、/|“”‘’;]"," ",sentence)  
  return sentence  

.def FMM(sentence,diction,result = [],maxwordLength = 4,edcode="utf-8"): 
.    i = 0  
    sentence = PreProcess(sentence,edcode)  
  length = len(sentence)  
   while i < length:  
        # find the ascii word   
        tempi=i  
      tok=sentence[i:i+1]  
     while re.search("[0-9A-Za-z/-/+#@_/.]{1}",tok)<>None:  
          i= i+1  
          tok=sentence[i:i+1]  
      if i-tempi>0:  
            result.append(sentence[tempi:i].lower().encode(edcode))  
       # find chinese word   
       left = len(sentence[i:])  
       if left == 1:  
           """go to 4 step over the FMM"""  
          """should we add the last one? Yes, if not blank"""  
           if sentence[i:] <> " ":  
              result.append(sentence[i:].encode(edcode))  
          return result  
        m = min(left,maxwordLength)  
         
       for j in xrange(m,0,-1):  
         leftword = sentence[i:j+i].encode(edcode)  
     #   print leftword.decode(edcode)   
           if LookUp(leftword,diction):  
              # find the left word in dictionary  
              # it's the right one   
              i = j+i  
                result.append(leftword)  
              break  
          elif j == 1:  
               """only one word, add into result, if not blank"""  
               if leftword.decode(edcode) <> " ":  
                    result.append(leftword)  
              i = i+1  
         else:  
              continue  
   return result 
def LookUp(word,dictionary):  
    if dictionary.has_key(word):  
       return True  
    return False 
def ConvertGBKtoUTF(sentence):  
   return sentence.decode('gbk').encode('utf-8') 
import re
def PreProcess(sentence,edcode="utf-8"):
    sentence = sentence.decode(edcode)
    sentence=re.sub(u"[。,,!……!《》<>/"'::?/?、/|“”‘’;]"," ",sentence)
    return sentence

def FMM(sentence,diction,result = [],maxwordLength = 4,edcode="utf-8"):
    i = 0
    sentence = PreProcess(sentence,edcode)
    length = len(sentence)
    while i < length:
        # find the ascii word
        tempi=i
        tok=sentence[i:i+1]
        while re.search("[0-9A-Za-z/-/+#@_/.]{1}",tok)<>None:
            i= i+1
            tok=sentence[i:i+1]
        if i-tempi>0:
            result.append(sentence[tempi:i].lower().encode(edcode))
        # find chinese word
        left = len(sentence[i:])
        if left == 1:
            """go to 4 step over the FMM"""
            """should we add the last one? Yes, if not blank"""
            if sentence[i:] <> " ":
                result.append(sentence[i:].encode(edcode))
            return result
        m = min(left,maxwordLength)
       
        for j in xrange(m,0,-1):
            leftword = sentence[i:j+i].encode(edcode)
         #   print leftword.decode(edcode)
            if LookUp(leftword,diction):
                # find the left word in dictionary
                # it's the right one
                i = j+i
                result.append(leftword)
                break
            elif j == 1:
                """only one word, add into result, if not blank"""
                if leftword.decode(edcode) <> " ":
                    result.append(leftword)
                i = i+1
            else:
                continue
    return result
def LookUp(word,dictionary):
    if dictionary.has_key(word):
        return True
    return False
def ConvertGBKtoUTF(sentence):
    return sentence.decode('gbk').encode('utf-8')

测试代码:

 代码如下 复制代码

[c-sharp] view plaincopyprint?
.dictions = {} 
.dictions["ab"] = 1 
.dictions["cd"] = 2 
.dictions["abc"] = 1 
.dictions["ss"] = 1 
.dictions[ConvertGBKtoUTF("好的")] = 1 
.dictions[ConvertGBKtoUTF("真的")] = 1 
.sentence = "asdfa好的是这样吗vasdiw呀真的daf dasfiw asid是吗?" 
.s = FMM(ConvertGBKtoUTF(sentence),dictions) 
.for i in s: 
.    print i.decode("utf-8") 
dictions = {}
dictions["ab"] = 1
dictions["cd"] = 2
dictions["abc"] = 1
dictions["ss"] = 1
dictions[ConvertGBKtoUTF("好的")] = 1
dictions[ConvertGBKtoUTF("真的")] = 1
sentence = "asdfa好的是这样吗vasdiw呀真的daf dasfiw asid是吗?"
s = FMM(ConvertGBKtoUTF(sentence),dictions)
for i in s:
    print i.decode("utf-8")

文本测试代码:

[c-sharp] view plaincopyprint?
.test = open("test.txt","r") 
.for line in test: 
.    s = FMM(CovertGBKtoUTF(line),dictions) 
.    for i in s: 
.        print i.decode("utf-8") 

 

时间: 2024-12-27 20:04:50

python 中文分词程序实例的相关文章

Ubuntu下Java调用IKAnalyzer中文分词程序失效

庖丁解牛等其它中文分词程序比较后发现,IKAnalyzer的中文分词效果好,程序调用简单.所以采用IKAnalyzer作为我们中文分词的程序. 调用IKAnalyzer来进行中文分词的代码十分简单:  代码如下 复制代码 /** * 传入一个中文语句,返回一个List列表,列表中的每一个元素是一个String类型的分词之后的中文词组 */ public static ArrayList<String> testJe(String testString) throws Exception {  

Python smallseg分词用法实例分析

  本文实例讲述了Python smallseg分词用法.分享给大家供大家参考.具体分析如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #encoding=utf-8 #import psyco #psyco.full() words = [x.rstrip() for x in open("main.dic",mode='r',encoding='

ASP.NET编写简单的中文分词程序

asp.net|程序|中文 几个月之前,在网上找到了一个中文词库素材(几百K),当时便想写一个分词程序了.我对汉语分词没有什么研究,也就凭自己臆想而写.若有相关方面专家,还请多给意见. 一.词库 词库大概有5万多词语(google能搜到,类似的词库都能用),我摘要如下: 地区    82重要    81新华社    80技术    80会议    80自己    79干部    78职工    78群众    77没有    77今天    76同志    76部门    75加强    75组

编写简单的中文分词程序

几个月之前,在网上找到了一个中文词库素材(几百K),当时便想写一个分词程序了.我对汉语分词没有什么研究,也就凭自己臆想而写.若有相关方面专家,还请多给意见. 一.词库 词库大概有5万多词语(Google能搜到,类似的词库都能用),我摘要如下: 地区    82重要    81新华社    80技术    80会议    80自己    79干部    78职工    78群众    77没有    77今天    76同志    76部门    75加强    75组织    75第一列是词,第

relaxlife.net发布一个自己开发的中文分词程序_实用技巧

近来因为工作原来,研究了一下中文分词,也就写了一个中文分词的程序.采用的是逆向最大匹配算算法. 使用示例: <%@ Page Language="C#"%> <%@ Import Namespace="Relaxlife.Xiaokui" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/

使用Discuz关键词服务器实现PHP中文分词_php实例

不同于使用自己的服务器进行分词,Discuz!在线中文分词服务是基于API返回分词结果的.在项目中,我们只需要一个函数即可方便地进行分词.关键词提取.以下是根据Discuz!在线分词服务API写的函数,测试可正常运行: 复制代码 代码如下: /** * DZ在线中文分词 * @param $title string 进行分词的标题 * @param $content string 进行分词的内容 * @param $encode string API返回的数据编码 * @return  arra

[推荐]jsp中文分词程序

 代码如下 复制代码 publicclass MM2 {  privatestaticfinal Log log = LogFactory.getLog(MM2.class);    privatestatic HashMap<String, Integer> dictionary =null;  privatestaticfinalint WORD_MAX_LENGTH =9;  private Reader reader;    static  {  loadDictionary();  

jieba.NET中文分词及jieba.NET与Lucene.Net的集成

jieba中文分词的.NET版本:jieba.NET jieba使用起来非常简单,同时分词的结果也令人印象深刻,有兴趣的可以到它的在线演示站点体验下(注意第三行文字). .NET平台上常见的分词组件是盘古分词,但是已经好久没有更新了.最明显的是内置词典,jieba的词典有50万个词条,而盘古的词典是17万,这样会造成明显不同的分词效果.另外,对于未登录词,jieba"采用了基于汉字成词能力的HMM模型,使用了Viterbi算法",效果看起来也不错. 基于以上两点,加上对于中文分词的兴趣

c++-关于C++中嵌入python 结巴分词

问题描述 关于C++中嵌入python 结巴分词 如题,在c++中想用到python的结巴分词库,我想的是把字符串传入py脚本,分词后再返回,但是会出现各种问题,而且jieba的对象类型不好处理,有没有大神可以提供下思路,有可行代码提供的,直接给分.跪求-- 解决方案 python结巴分词python中文分词:结巴分词 解决方案二: 如果觉得C++直接调用py脚本来处理,对象类型等不是很好处理,可以用一个中间介质的方式 把字符串写入文件,然后调用py脚本来处理,同样py脚本处理完写入另一个文件,