中文分词

下面是一个中文分词算法,只有片段,完整例子请在后面下载完整代码:

package com.acgist.nlp.query.analyzer;
 
import java.util.ArrayList;
import java.util.List;
 
import org.apache.commons.lang.StringUtils;
 
/**
 * 分词器
 * 注意词典必须有序排放
 */
public class Analyzer {
 
    private char[] chars; // 字符串分割
    private int index = 0; // 分词序号
    private int matchIndex = 0; // 已经匹配的长度
    private String content; // 原始句子
    private boolean maximize; // 是否最大分词
    private List<Token> tokens; // 分解出来的词语
 
    public static final String REGEX_BLANK = "\\s";
    public static final String REGEX_NUMBER = "[0-9]";
    public static final String REGEX_LETTER = "[a-zA-Z]";
    public static final String REGEX_CHINESE = "[\\u4e00-\\u9fa5]";
     
    /**
     * 默认使用最小分词
     */
    private Analyzer() {
        this(false);
    }
     
    /**
     * 最大分词
     * @param maximize 是否最大分词
     */
    private Analyzer(boolean maximize) {
        this.maximize = maximize;
    }
     
    /**
     * 初始化
     * @param content 内容
     * @return 分词器
     */
    public static Analyzer instance(String content) {
        Analyzer analyzer = new Analyzer();
        analyzer.setContent(content);
        analyzer.setTokens(new ArrayList<Token>());
        if(StringUtils.isNotEmpty(content))
            analyzer.chars = content.toCharArray();
        return analyzer;
    }
     
    /**
     * 初始化
     * @param content 内容
     * @return 分词器
     */
    public static Analyzer instanceMaximize(String content) {
        Analyzer analyzer = new Analyzer(true);
        analyzer.setContent(content);
        analyzer.setTokens(new ArrayList<Token>());
        if(StringUtils.isNotEmpty(content))
            analyzer.chars = content.toCharArray();
        return analyzer;
    }
     
    /**
     * 开始分析
     */
    public void analyze() {
        if(StringUtils.isEmpty(content))
            return;
        if(maximize) {
            for(;this.index < chars.length; this.index++) {
                this.analyzeTokenMaximize();
            }
        } else {
            for(;this.index < chars.length; this.index++) {
                this.analyzeToken();
            }
        }
        this. recombination();
    }
     
    /**
     * 最小分词
     */
    private void analyzeToken() {
        String word = "";
        boolean exact = false; // 判断是否是一个完整匹配的词语
        String exactWord = null; // 完整匹配到的词语
        String dictionary = null;
        List<Dictionary> dictionaries = DictionaryUtils.getInstance().getDictionaries();
        for(int index = this.index; index < this.chars.length; index++) {
            boolean match = false; // 是否有匹配
            word += String.valueOf(this.chars[index]);
            for(int i = 0; i < dictionaries.size(); i++) {
                dictionary = dictionaries.get(i).getWord();
                if(dictionary.equals(word)) {
                    exact = true;
                    match = true;
                    exactWord = word;
                    break;
                } else if (dictionary.startsWith(word)) {
                    match = true;
//                  未排序会导致分词失败
//                  break;
                }
            }
            if(!match)
                break;
        }
        if(exact) {
            Token token = new Token();
            token.setWord(exactWord);
            token.setExact(true);
            this.tokens.add(token);
            this.index = this.index + exactWord.length() - 1;
        } else {
            Token token = new Token();
            token.setWord(String.valueOf(this.chars[this.index]));
            token.setExact(false);
            this.tokens.add(token);
        }
    }
     
    /**
     * 最小分词
     */
    private void analyzeTokenMaximize() {
        String word = "";
        boolean exact = false; // 判断是否是一个完整匹配的词语
        String dictionary = null;
        List<Dictionary> dictionaries = DictionaryUtils.getInstance().getDictionaries();
        for(int index = this.index; index < this.chars.length; index++) {
            boolean match = false; // 是否有匹配
            word += String.valueOf(this.chars[index]);
            for(int i = 0; i < dictionaries.size(); i++) {
                dictionary = dictionaries.get(i).getWord();
                if(dictionary.equals(word)) {
                    exact = true;
                    match = true;
                    Token token = new Token();
                    token.setWord(word);
                    token.setExact(true);
                    this.tokens.add(token);
                    if(this.index + word.length() > matchIndex)
                        matchIndex = this.index + word.length();
                    break;
                } else if(dictionary.startsWith(word)) {
                    match = true;
//                  未排序会导致分词失败
//                  break;
                }
            }
            if(!match)
                break;
        }
        if(!exact && matchIndex <= this.index) {
            Token token = new Token();
            token.setWord(String.valueOf(this.chars[this.index]));
            token.setExact(false);
            this.tokens.add(token);
        }
    }
     
    /**
     * 组装数字、字母
     */
    private void recombination() {
        StringBuffer tmpWord = new StringBuffer();
        List<Token> tokens = new ArrayList<>();
        for (Token token : this.getTokens()) {
            if(token.getExact() != null && token.getExact()) {
                if(tmpWord.length() != 0) {
                    Token recombineToken = new Token();
                    recombineToken.setWord(tmpWord.toString());
                    recombineToken.setExact(false);
                    tokens.add(recombineToken);
                    tmpWord.setLength(0);
                }
                tokens.add(token);
            } else {
                String word = token.getWord();
                if(word.matches(REGEX_LETTER) || word.matches(REGEX_NUMBER)) {
                    tmpWord.append(token.getWord());
                } else {
                    if(tmpWord.length() != 0) {
                        if(!tmpWord.toString().matches(REGEX_BLANK)) { // 去掉空白符
                            Token recombineToken = new Token();
                            recombineToken.setWord(tmpWord.toString());
                            recombineToken.setExact(false);
                            tokens.add(recombineToken);
                        }
                        tmpWord.setLength(0);
                    }
                    if(!word.matches(REGEX_BLANK)) // 去掉空白符
                        tokens.add(token);
                }
            }
        }
        if(tmpWord.length() != 0) {
            Token recombineToken = new Token();
            recombineToken.setWord(tmpWord.toString());
            recombineToken.setExact(false);
            tokens.add(recombineToken);
        }
        this.setTokens(tokens);
    }
     
    public String getContent() {
        return content;
    }
 
    public void setContent(String content) {
        this.content = content;
    }
 
    public List<Token> getTokens() {
        return tokens;
    }
 
    public void setTokens(List<Token> tokens) {
        this.tokens = tokens;
    }
 
}

注:使用的词库为IK-Analyzer-2012FF的词库,如果是生产环境推荐使用IK分词,本例子仅供学习交流。

下载地址:http://pan.baidu.com/s/1nts0mP7

待优化:

  1. 数量词
  2. 词库按照头一个字符分区