首先要说明了是,本文采用的ICTCLAS分词器为:2011版本的windows 32位JNI,Lucene版本为3.6.1.
众所周知的是做中文信息处理时,Lucene自带的分词器往往是不被我们所采用的,这时候,我们就需要实现自己的分词器。而现在中文处理采用最多的分词器就是ICTCLAS了。本着没必要重复造轮子的原则,当然分词器这个轮子也没那么容易造,我用ICTCLAS实现了Lucene的analyzer。这个分词器的大部分代码是看视频的Demo 直接copy过来的,并不是我自己写的。(这里对Lucene大牛觉先表示感谢)我实现的功能就是:
一、在分词的同时过滤停用词。
二、可以得到每个词的词性。
用Lucene实现自己的分词器只需要extends Analyzer类就可以了。代码如下:
- package com.xh.analyzer;
- import java.io.IOException;
- import java.io.Reader;
- import java.io.UnsupportedEncodingException;
- import java.util.ArrayList;
- import java.util.List;
- import org.apache.lucene.analysis.Analyzer;
- import org.apache.lucene.analysis.TokenStream;
- import org.apache.lucene.analysis.Tokenizer;
- import ICTCLAS.I3S.AC.ICTCLAS50;
- public class ICTCLASAnalyzer extends Analyzer {
- private ICTCLAS50 icta;
- private volatile boolean initialized = false;
- public ICTCLASAnalyzer() throws UnsupportedEncodingException {
- icta = new ICTCLAS50();
- String initPath=".";
- // 初始化
- if (icta.ICTCLAS_Init(initPath.getBytes("GB2312")) == false) {
- System.out.println("Init Fail!");
- return;
- }
- // 设置词性标注集(0 计算所二级标注集,1 计算所一级标注集,2 北大二级标注集,3 北大一级标注集)
- icta.ICTCLAS_SetPOSmap(2);
- // 导入用户字典
- int nCount = 0;
- String usrdir = "userdict.txt"; // 用户字典路径
- byte[] usrdirb = usrdir.getBytes();// 将string转化为byte类型
- // 导入用户字典,返回导入用户词语个数第一个参数为用户字典路径,第二个参数为用户字典的编码类型
- nCount = icta.ICTCLAS_ImportUserDictFile(usrdirb, 0);
- //System.out.println("导入用户词个数" + nCount);
- initialized = true;
- }
- public List<String> tokenizeReader(Reader reader) {
- List<String> result = new ArrayList<String>(1000);
- try {
- StringBuffer contentbuffer = new StringBuffer();
- char[] temp = new char[1024];
- int size = 0;
- while ((size = reader.read(temp, 0, 1024)) != -1) {
- String tempstr = new String(temp, 0, size);
- contentbuffer.append(tempstr);
- }
- byte nativeBytes[] = icta.ICTCLAS_ParagraphProcess(contentbuffer.toString().getBytes("GB2312"), 2, 1);
- String nativeStr = new String(nativeBytes, 0, nativeBytes.length, "GB2312");
- System.out.println("分词结果: " + nativeStr);
- //进行词用词过滤
- String[] terms=nativeStr.split("\\s+");
- int pos;
- String term,type;
- for (String string : terms) {
- pos=string.lastIndexOf('/');
- if(pos==-1)continue;
- term=string.substring(0,pos);
- type=string.substring(pos+1, string.length());
- if(accept(term,type)){
- result.add(string);
- }
- }
- } catch (Throwable e) {
- e.printStackTrace();
- }
- return result;
- }
- private boolean accept(String term,String type){
- boolean accept=false;
- //对词的要求
- // if(term.length()>1){
- // accept=true;
- // }
- // if(!accept)return accept;
- //对词性的要求
- if(type.startsWith("n") //名词
- ||type.startsWith("t") //时间词
- ||type.startsWith("s") //处所词
- ||type.startsWith("f") //方位词
- ||type.startsWith("a") //形容词
- ||type.startsWith("v") //动词
- ||type.startsWith("b") //区别词
- ||type.startsWith("z") //状态词
- // ||type.startsWith("r") //代词
- // ||type.startsWith("m") //数词
- ||type.startsWith("q") //量词
- // ||type.startsWith("d") //副词
- ||type.startsWith("p") //介词
- ||type.startsWith("c") //连词
- // ||type.startsWith("u") //助词
- // ||type.startsWith("e") //叹词
- // ||type.startsWith("y") //语气词
- ||type.startsWith("o") //拟声词
- ||type.startsWith("h") //前缀
- ||type.startsWith("k") //后缀
- ||type.startsWith("x") //网址URL
- // ||type.startsWith("w") //标点符号
- ){
- return true;
- }
- return accept;
- }
- @Override
- public TokenStream tokenStream(String fieldName, Reader reader) {
- if(!initialized)
- return null;
- List<String> tokens = tokenizeReader(reader);
- return new ICTCLASTokenizer(tokens);
- }
- @Override
- public TokenStream reusableTokenStream(String fieldName, Reader reader) throws IOException {
- Tokenizer tokenizer = (Tokenizer) getPreviousTokenStream();
- if (tokenizer == null) {
- List<String> tokens = tokenizeReader(reader);
- tokenizer = new ICTCLASTokenizer(tokens);
- setPreviousTokenStream(tokenizer);
- } else{
- tokenizer.reset(reader);
- ICTCLASTokenizer t = (ICTCLASTokenizer)tokenizer;
- List<String> tokens = tokenizeReader(reader);
- t.reset(tokens);
- }
- return tokenizer;
- }
- @Override
- public void close() {
- icta.ICTCLAS_SaveTheUsrDic();
- icta.ICTCLAS_Exit();
- initialized = false;
- }
- }
同时我们还需要自己写Tokenizer,方法也很简单,extends Lucene的Tokenizer就OK了。
- package com.xh.analyzer;
- import java.io.IOException;
- import java.io.Reader;
- import java.util.Iterator;
- import java.util.List;
- import org.apache.lucene.analysis.Tokenizer;
- import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
- import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
- public class ICTCLASTokenizer extends Tokenizer {
- private List<String> tokens;
- private Iterator<String> tokenIter;
- private CharTermAttribute termAtt;
- private TypeAttribute typeAtt;
- public ICTCLASTokenizer(List<String> tokens) {
- this.tokens = tokens;
- this.tokenIter = tokens.iterator();
- termAtt = addAttribute( CharTermAttribute.class);
- typeAtt=addAttribute(TypeAttribute.class);
- }
- @Override
- public boolean incrementToken() throws IOException {
- clearAttributes();
- if(tokenIter.hasNext()){
- String tokenstring = tokenIter.next();
- int pos=tokenstring.lastIndexOf('/');
- typeAtt.setType(tokenstring.substring(pos,tokenstring.length()));
- termAtt.append(tokenstring.substring(0, pos));
- termAtt.setLength(pos);
- return true;
- }
- return false;
- }
- @Override
- public void reset() throws IOException {
- tokenIter = tokens.iterator();
- }
- @Override
- public void reset(Reader input) throws IOException {
- }
- public void reset(List<String> tokens) {
- this.tokens = tokens;
- this.tokenIter = tokens.iterator();
- }
- }
这样的话,一个能在Lucene中使用的分词器就构造好了。测试一下吧:
测试的语句为(程序根目录下的text.txt文件里):
- 随后温总理就离开了舟曲县城,预计温总理今天下午就回到北京。以上就是今天上午的最新动态。
- 美丽的姑娘,你在哪里?http//:www.baidu.com
- 两位青年工人协助民警抓住了一伙歹徒。
- 公元二零一零年十二月,抓住了一只兔子。
测试的代码如下:
- import java.io.File;
- import java.io.FileReader;
- import org.apache.lucene.analysis.Analyzer;
- import org.apache.lucene.analysis.TokenStream;
- import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
- import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
- import com.xh.analyzer.ICTCLASAnalyzer;
- class TestMain
- { //主函数
- public static void main(String[] args) throws Exception
- {
- Analyzer analyzer = new ICTCLASAnalyzer();
- TokenStream ts=analyzer.tokenStream("contents", new FileReader(new File("text.txt")));
- while(ts.incrementToken()){
- System.out.print(ts.getAttribute(CharTermAttribute.class)+""+ts.getAttribute(TypeAttribute.class).type()+" ");
- }
- }
- }
测试结果如下:
温/nr 总理/n 离开/v 预计/v 温/nr 总理/n 今天/t 下午/t 回到/v 北京/ns 以上/f 是/v 今天/t 上午/t 最新/a 动态/n 美丽/a 姑娘/n 在/p http/x :/x www/x baidu/x com/x 位/q 青年/n 工人/n 协助/v 民警/n 抓住/v 伙/q 歹徒/n 公元/n 十二月/t 抓住/v 只/q 兔子/n
如果不输出词性:则TestMain代码改为:
- import java.io.File;
- import java.io.FileReader;
- import org.apache.lucene.analysis.Analyzer;
- import org.apache.lucene.analysis.TokenStream;
- import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
- import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
- import com.xh.analyzer.ICTCLASAnalyzer;
- class TestMain
- { //主函数
- public static void main(String[] args) throws Exception
- {
- Analyzer analyzer = new ICTCLASAnalyzer();
- TokenStream ts=analyzer.tokenStream("contents", new FileReader(new File("text.txt")));
- while(ts.incrementToken()){
- System.out.print(ts.getAttribute(CharTermAttribute.class)+" ");
- }
- }
- }
温 总理 离开 预计 温 总理 今天 下午 回到 北京 以上 是 今天 上午 最新 动态 美丽 姑娘 在 http : www baidu com 位 青年 工人 协助 民警 抓住 伙 歹徒 公元 十二月 抓住 只 兔子
项目本来想直接上传,但是不支持2M以上的,上传到我的下载去吧。链接为:http://down.51cto.com/data/724179