君子博学而日参省乎己 则知明而行无过矣

博客园 首页 新随笔 联系 订阅 管理

 转载http://blog.csdn.net/wzb56/article/details/7914954#

1.构建词典内存树的TrieNode节点类:      

package cn.wzb.segmenter.mm.bean;

import java.util.HashMap;

/**
 * 构建内存词典的Trie树结点
 *       
 
*/
public class TrieNode {
    /** 结点关键字,其值为中文词中的一个字 */
    public char key = '\0';
    
    /** 如果该字在词语的末尾,则bound=true */
    public boolean bound = false;
    
    /** 指向下一个结点的指针结构,用来存放当前字在词中的下一个字的位置 */
    public HashMap<Character, TrieNode> childs = new HashMap<Character, TrieNode>();

    public TrieNode() {}

    public TrieNode(char key) {
        this.key = key;
    }
}


2. 最大正向匹配算法(Maximum Matching)算法的实现类:segmenter类:核心方法segment();

package cn.wzb.segmenter.mm;

import java.io.IOException;

import cn.wzb.segmenter.AbstractSegmenter;
import cn.wzb.segmenter.mm.bean.TrieDictionary;
import cn.wzb.segmenter.mm.bean.TrieNode;

public class MMSegmenter extends AbstractSegmenter {
    public static TrieDictionary dict = null;
    
    static { //加载词典
        String dictionaryName = "/cn/wzb/dictionary/word_dic_utf8.txt";
        dict = TrieDictionary.getInstance(dictionaryName);
    }
    
    public MMSegmenter() {
        super("一个简单的最大的正向匹配器:MMSegmenter");
    }

    /**
     * 词典:用Trie树表示,每个节点都是一个TrieNode节点
     * 每个TrieNode节点中有:
     *   1.表示一个字
     *   2.以该字为前缀的所有的下一个字的HashMap<"字", 字的TrieNode>
     *   3.bound标记,该字是不是一个词的结尾。在最大匹配中有用(Maximum Matching) 
     * 
     * 正向MM(Maximum Matching)算法的核心思想:
     *  1. 从句子中,取词 
     *  2. 将词添加到分词列表中 
     *  3. 将分词标记 "|"添加到分词表
     * 
     * 其中的句子中的成分分为以下几种: 
     * 1. 非分词:如分隔符,直接跳过 
     * 2. 分词: 分词分为以下几种:
     *      a. 非中文分词:将分隔符分隔的连续的非中文字符作为一个分词 
     *      b. 中文分词: 
     *          i. 词典中的词:作为一个分词 
     *         ii. 词典中的词的前缀:将每个字作为一个分词 
     *        iii. 非词典中的词: 将每个字作为一个分词
     * 
     * 该分词的核心:对于前缀词的划分
     
*/

    public String segment(String sentence) {
        StringBuffer segBuffer = new StringBuffer();

        TrieNode p = dict.getRoot();
        ;
        TrieNode pChild = null;

        int length = sentence.length();
        int segBoundIndex = -1; //保存上次分词结束字符在sentence中的位置    

        for (int i = 0; i < length; ++i) {            
            char c = sentence.charAt(i);
            if (CharacterType.isCharSeperator(c)) {// 分隔符
                
// do nothing;
            } else if (CharacterType.isCharOther(c)) {// 其他语言字符                
                 do {
                    segBuffer.append(c);
                    if(++i == length){
                        break;
                    }
                    c = sentence.charAt(i);                
                }while (CharacterType.isCharOther(c));
                 if( i != length) --i; //还原现场             
            } else if (CharacterType.isCharChinese(c)) {
                pChild = p.childs.get(Character.valueOf(c));
                if (pChild == null) {// 不在词典中的中文字符
                    segBuffer.append(c);
                } else {
                     do {// 在词典中的词
                        segBuffer.append(c);
                        if (p == dict.getRoot() || pChild.bound) { // 算法的关键,能够保证前缀词,被划分。
                            segBoundIndex = i;
                        }
                        if (++i >= length) {
                            break;
                        }
                        c = sentence.charAt(i);
                        p = pChild;
                        pChild = (TrieNode) p.childs.get(Character.valueOf(c));
                    }while (pChild != null);
                    //切除非词典中词的前缀词
                    if (--i >= segBoundIndex) {
                        segBuffer.delete(segBuffer.length() - (i - segBoundIndex), segBuffer.length());
                    }
                    //还原现场
                    i = segBoundIndex;
                    p = dict.getRoot();
                }
            }
            segBuffer.append('|'); //添加分词标记
        }

        return new String(segBuffer);
    }

    public String segment(String sentence, String verison) {
        StringBuffer segBuffer = new StringBuffer();

        int segBoundIdx = 0;
        int length = sentence.length();
        TrieNode p = null
        TrieNode pChild = null;
        
        for (int i = 0; i < length; i++) {
            char c = sentence.charAt(i);
            
            p = dict.getRoot();            
            pChild = p.childs.get(Character.valueOf(c));
            
            // 不在词典中的字符
            if (pChild == null) {
                if (CharacterType.isCharSeperator(c)){
                    segBuffer.append(c);// do something;
                } if (CharacterType.isCharChinese(c)) {
                    segBuffer.append(c);
                } else {
                    do { // 非中文字符
                        segBuffer.append(c);
                        if (++i == length){
                            break;
                        }                        
                        c = sentence.charAt(i);
                    } while (CharacterType.isCharOther(c));
                    if( i != length) --i; //还原现场
                }
            } else { // 中文字词
                while (pChild != null) {
                    if (p == dict.getRoot() || pChild.bound) { //词典中的词或者词典中词的前缀词;前缀词将被单字划分
                        segBoundIdx = i;
                    }
                    segBuffer.append(c);                    
                    if (++i == length) {
                        break;
                    }                                    
                    c = sentence.charAt(i);
                    p = pChild;    
                    pChild = p.childs.get(Character.valueOf(c));
                }
                //切除分词表中不在词典中的前缀字词
                if (--i > segBoundIdx) {
                    segBuffer.delete(segBuffer.length() - (i - segBoundIdx), segBuffer.length());
                }
                //还原现场
                i = segBoundIdx;                
            }            
            segBuffer.append('|');
        }
        
        return new String(segBuffer);
    }

    public static void main(String args[]) throws IOException {
        MMSegmenter mmsegger = new MMSegmenter();
        System.out.println(mmsegger.segment("中华人民共和国是一个伟大的国家hello world"));
        System.out.println(mmsegger.segment("小红是个爱学习的好学生!!!!!"));
        System.out.println(mmsegger.segment("中华民de hello world!人民共"));
        System.out.println(mmsegger.segment("中华人民共"));
        System.out.println(mmsegger.segment("中华人民共和国家"));
        System.out.println(mmsegger.segment("爱国"));
        System.out.println(mmsegger.segment("爱我Love你"));
        System.out.println(mmsegger.segment("京华时报2008年1月23日报道 昨天,受一股来自中西伯利亚的强冷空气影响,本市出现大风降温天气,白天最高气温只有零下7摄氏度,同时伴有6到7级的偏北风。"));
        
        System.out.println("another version: ");        
        System.out.println(mmsegger.segment("中华人民共和国是一个伟大的国家hello world", " "));
        System.out.println(mmsegger.segment("小红是个爱学习的好学生!!!!!", " "));
        System.out.println(mmsegger.segment("中华民de hello world!人民共", " "));
        System.out.println(mmsegger.segment("中华人民共", " "));
        System.out.println(mmsegger.segment("中华人民共和国家", " "));
        System.out.println(mmsegger.segment("爱国", " "));
        System.out.println(mmsegger.segment("爱我Love你", " "));
        System.out.println(mmsegger.segment("京华时报2008年1月23日报道 昨天,受一股来自中西伯利亚的强冷空气影响,本市出现大风降温天气,白天最高气温只有零下7摄氏度,同时伴有6到7级的偏北风。", ""));
    

        

        //System.out.println(CharacterType.isCharSeperator(' '));    
    }    
}

3.关于字符类型辅助类:CharacterType类:

package cn.wzb.segmenter.mm;

class CharacterType {    
    public static boolean isCharSeperator(char c) {
        return "\u3002\uFF01\uFF1F\uFF1A\uFF1B\u3001\uFF0C\uFF08\uFF09\u300A\u300B\u3010\u3011{}\u201C\u201D\u2018\u2019!?:;,()<>[]{}\"'\n\r\t ".indexOf(c) != -1;
    }
    
    public static boolean isCharChinese(char c) {
        return c >= '\u4E00' && c <= '\u9FBF';
    }
    
    public static boolean isCharOther(char c) {
        return !isCharSeperator(c) && !isCharChinese(c);
    }

    //private static final String C_E_SEPERATOR = "\u3002\uFF01\uFF1F\uFF1A\uFF1B\u3001\uFF0C\uFF08\uFF09\u300A\u300B\u3010\u3011{}\u201C\u201D\u2018\u2019!?:;,()<>[]{}\"'\n\r\t ";
    
//private static final String str = "。!?:;、,()《》【】{}“”‘’!?:;,()<>[]{}\"'\n\r\t ";
}
4. 该算法使用的词典文件:

  1. 希望  
  2. 中华  
  3. 人民  
  4. 共和国  
  5. 中华人民共和国  
  6. 一个  
  7. 伟大  
  8. 国家  
  9. 西安  
  10. 北京  
  11. 家庭  
  12. 家里  
  13. 爱国者  
  14. 我Love你  
  15. 学习  
  16. 好学生  
  17. 学生  
  18. 爱学  
  19. 爱学习  

5.分词测试结果:


  1. dictionary loading OK!  
  2. [oooggooo]一个简单的最大的正向匹配器:MMSegmenter segmenter on  
  3. 中华人民共和国|是|一个|伟大|的|国家|hello|world|  
  4. 小|红|是|个|爱学习|的|好学生||||||  
  5. 中华|民|de|hello|world|人民|共|  
  6. 中华|人民|共|  
  7. 中华人民共和国|家|  
  8. 爱|国|  
  9. 爱|我Love你|  
  10. another version:   
  11. 中华人民共和国|是|一个|伟大|的|国家|hello|world|  
  12. 小|红|是|个|爱学习|的|好学生|!|!|!|!|!|  
  13. 中华|民|de|hello|world|人民|共|  
  14. 中华|人民|共|  
  15. 中华人民共和国|家|  
  16. 爱|国|  
  17. 爱|我Love你|  
posted on 2012-09-30 00:39  刺猬的温驯  阅读(3927)  评论(0编辑  收藏  举报