ICTCLAS分词器与Lucene4.9的结合

来源:转载


一直以来喜欢搜索方向,虽然未能如愿以偿,但是依旧保持着那份狂热。还记得那个暑假、那间实验室、那一群人,一切的一切早已随风而去。踏上新的征程,我早已不是曾经的自己。面对三分技术七分业务的环境,我选择了沉淀。社会就是个大机器,我们只是个小螺丝,容不下半点扭扭捏捏。人终究归属于一个时代的产物,也终将被时代所抛弃。言归正题,在lucene加入自定义的分词器,需要继承Analyzer类,实现createComponents方法,同时定义Tokenzier类用于记录所需建立索引的词以及其在文章的位置,这里继承SegmentingTokenizerBase类,需要实现setNextSentence与incrementWord两个方法。其中,setNextSentence设置下一个句子,在多域(Filed)分词索引时,setNextSentence就是设置下一个域的内容,可以通过new String(buffer, sentenceStart, sentenceEnd - sentenceStart)获取。而incrementWord方法则是记录每个单词以及它的位置,需要注意一点就是要在前面加clearAttributes(),否则可能出现first position increment must be > 0...错误。以ICTCLAS分词器为例,下面贴上个人代码,希望能给大家带来帮助,不足之处,多多拍砖。


import java.io.IOException;import java.io.Reader;import java.io.StringReader;import org.apache.lucene.analysis.Analyzer;import org.apache.lucene.analysis.TokenStream;import org.apache.lucene.analysis.Tokenizer;import org.apache.lucene.analysis.core.LowerCaseFilter;import org.apache.lucene.analysis.en.PorterStemFilter;import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;import org.apache.lucene.util.Version;/** * 中科院分词器 继承Analyzer类,实现其 tokenStream方法 * * @author ckm * */public class ICTCLASAnalyzer extends Analyzer { /** * 该方法主要是将文档转变成lucene建立索 引所需的TokenStream对象 * * @param fieldName * 文件名 * @param reader * 文件的输入流 */ @Override protected TokenStreamComponents createComponents(String fieldName, Reader reader) { try { System.out.println(fieldName); final Tokenizer tokenizer = new ICTCLASTokenzier(reader); TokenStream stream = new PorterStemFilter(tokenizer); stream = new LowerCaseFilter(Version.LUCENE_4_9, stream); stream = new PorterStemFilter(stream); return new TokenStreamComponents(tokenizer,stream); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public static void main(String[] args) throws Exception { Analyzer analyzer = new ICTCLASAnalyzer(); String str = "黑客技术"; TokenStream ts = analyzer.tokenStream("field", new StringReader(str)); CharTermAttribute c = ts.addAttribute(CharTermAttribute.class); ts.reset(); while (ts.incrementToken()) { System.out.println(c.toString()); } ts.end(); ts.close(); } }


import java.io.IOException;import java.io.Reader;import java.text.BreakIterator;import java.util.Arrays;import java.util.Iterator;import java.util.List;import java.util.Locale;import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;import org.apache.lucene.analysis.util.SegmentingTokenizerBase;import org.apache.lucene.util.AttributeFactory;/** * * 继承lucene的SegmentingTokenizerBase,重载其setNextSentence与 * incrementWord方 法,记录所需建立索引的词以及其在文章的位置 * * @author ckm * */public class ICTCLASTokenzier extends SegmentingTokenizerBase { private static final BreakIterator sentenceProto = BreakIterator.getSentenceInstance(Locale.ROOT); private final CharTermAttribute termAttr= addAttribute(CharTermAttribute.class);// 记录所需建立索引的词 private final OffsetAttribute offAttr = addAttribute(OffsetAttribute.class);// 记录所需建立索引的词在文章中的位置 private ICTCLASDelegate ictclas;// 分词系统的委托对象 private Iterator<String> words;// 文章分词后形成的单词 private int offSet= 0;// 记录最后一个词元的结束位置 /** * 构造函数 * * @param segmented 分词后的结果 * @throws IOException */ protected ICTCLASTokenzier(Reader reader) throws IOException { this(DEFAULT_TOKEN_ATTRIBUTE_FACTORY, reader); } protected ICTCLASTokenzier(AttributeFactory factory, Reader reader) throws IOException { super(factory, reader,sentenceProto); ictclas = ICTCLASDelegate.getDelegate(); } @Override protected void setNextSentence(int sentenceStart, int sentenceEnd) { // TODO Auto-generated method stub String sentence = new String(buffer, sentenceStart, sentenceEnd - sentenceStart); String result=ictclas.process(sentence); String[] array = result.split("//s"); if(array!=null){ List<String> list = Arrays.asList(array); words=list.iterator(); } offSet= 0; } @Override protected boolean incrementWord() { // TODO Auto-generated method stub if (words == null || !words.hasNext()) { return false; } else { String t = words.next(); while(t.equals("")||StopWordFilter.filter(t)){ //这里主要是为了过滤空白字符以及停用词 //StopWordFilter为自定义停用词过滤类 if (t.length() == 0) offSet++; else offSet+= t.length(); t =words.next(); } if (!t.equals("") && !StopWordFilter.filter(t)) { clearAttributes(); termAttr.copyBuffer(t.toCharArray(), 0, t.length()); offAttr.setOffset(correctOffset(offSet), correctOffset(offSet=offSet+ t.length())); return true; } return false; } } /** * 重置 */ public void reset() throws IOException { super.reset(); offSet= 0; } public static void main(String[] args) throws IOException { String content = "宝剑锋从磨砺出,梅花香自苦寒来!"; String seg = ICTCLASDelegate.getDelegate().process(content); //ICTCLASTokenzier test = new ICTCLASTokenzier(seg); //while (test.incrementToken()); } }



import java.io.File;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.charset.Charset;import ICTCLAS.I3S.AC.ICTCLAS50;/** * 中科院分词系统代理类 * * @author ckm * */public class ICTCLASDelegate { private static final String userDict = "userDict.txt";// 用户词典 private final static Charset charset = Charset.forName("gb2312");// 默认的编码格式 private static String ictclasPath =System.getProperty("user.dir"); private static String dirConfigurate = "ICTCLASConf";// 配置文件所在目录名 private static String configurate = ictclasPath + File.separator+ dirConfigurate;// 配置文件所在目录的绝对路径 private static int wordLabel = 2;// 词性标注类型(北大二级标注集) private static ICTCLAS50 ictclas;// 中科院分词系统的jni接口对象 private static ICTCLASDelegate instance = null; private ICTCLASDelegate(){ } /** * 初始化ICTCLAS50对象 * * @return ICTCLAS50对象初始化化是否成功 */ public boolean init() { ictclas = new ICTCLAS50(); boolean bool = ictclas.ICTCLAS_Init(configurate .getBytes(charset)); if (bool == false) { System.out.println("Init Fail!"); return false; } // 设置词性标注集(0 计算所二级标注集,1 计算所一级标注集,2 北大二级标注集,3 北大一级标注集) ictclas.ICTCLAS_SetPOSmap(wordLabel); importUserDictFile(configurate + File.separator + userDict);// 导入用户词典 ictclas.ICTCLAS_SaveTheUsrDic();// 保存用户字典 return true; } /** * 将编码格式转换为分词系统识别的类型 * * @param charset * 编码格式 * @return 编码格式对应的数字 **/ public static int getECode(Charset charset) { String name = charset.name(); if (name.equalsIgnoreCase("ascii")) return 1; if (name.equalsIgnoreCase("gb2312")) return 2; if (name.equalsIgnoreCase("gbk")) return 2; if (name.equalsIgnoreCase("utf8")) return 3; if (name.equalsIgnoreCase("utf-8")) return 3; if (name.equalsIgnoreCase("big5")) return 4; return 0; } /** * 该方法的作用是导入用户字典 * * @param path * 用户词典的绝对路径 * @return 返回导入的词典的单词个数 */ public int importUserDictFile(String path) { System.out.println("导入用户词典"); return ictclas.ICTCLAS_ImportUserDictFile( path.getBytes(charset), getECode(charset)); } /** * 该方法的作用是对字符串进行分词 * * @param source * 所要分词的源数据 * @return 分词后的结果 */ public String process(String source) { return process(source.getBytes(charset)); } public String process(char[] chars){ CharBuffer cb = CharBuffer.allocate (chars.length); cb.put (chars); cb.flip (); ByteBuffer bb = charset.encode (cb); return process(bb.array()); } public String process(byte[] bytes){ if(bytes==null||bytes.length<1) return null; byte nativeBytes[] = ictclas.ICTCLAS_ParagraphProcess(bytes, 2, 0); String nativeStr = new String(nativeBytes, 0, nativeBytes.length-1, charset); return nativeStr; } /** * 获取分词系统代理对象 * * @return 分词系统代理对象 */ public static ICTCLASDelegate getDelegate() { if (instance == null) { synchronized (ICTCLASDelegate.class) { instance = new ICTCLASDelegate(); instance.init(); } } return instance; } /** * 退出分词系统 * * @return 返回操作是否成功 */ public boolean exit() { return ictclas.ICTCLAS_Exit(); } public static void main(String[] args) { String str="结婚的和尚未结婚的"; ICTCLASDelegate id = ICTCLASDelegate.getDelegate(); String result = id.process(str.toCharArray()); System.out.println(result.replaceAll(" ", "-")); }}


import java.util.Iterator;import java.util.Set;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * 停用词过滤器 * * @author ckm * */public class StopWordFilter { private static Set<String> chineseStopWords = null;// 中文停用词集 private static Set<String> englishStopWords = null;// 英文停用词集 static { init(); } /** * 初始化中英文停用词集 */ public static void init() { LoadStopWords lsw = new LoadStopWords(); chineseStopWords = lsw.getChineseStopWords(); englishStopWords = lsw.getEnglishStopWords(); } /** * 判断关键字的类型以及判断其是否为停用词 注意:暂时只考虑中文,英文,中英混合, 中数混合,英数混合这五种类型,其中中英 混合, * 中数混合,英数混合还没特定的停用 词库或语法规则对其进行判别 * * @param word * 关键字 * @return true表示是停用词 */ public static boolean filter(String word) { Pattern chinese = Pattern.compile("^[/u4e00-/u9fa5]+$");// 中文匹配 Matcher m1 = chinese.matcher(word); if (m1.find()) return chineseFilter(word); Pattern english = Pattern.compile("^[A-Za-z]+$");// 英文匹配 Matcher m2 = english.matcher(word); if (m2.find()) return englishFilter(word); Pattern chineseDigit = Pattern.compile("^[/u4e00-/u9fa50-9]+$");// 中数匹配 Matcher m3 = chineseDigit.matcher(word); if (m3.find()) return chineseDigitFilter(word); Pattern englishDigit = Pattern.compile("^[A-Za-z0-9]+$");// 英数匹配 Matcher m4 = englishDigit.matcher(word); if (m4.find()) return englishDigitFilter(word); Pattern englishChinese = Pattern.compile("^[A-Za-z/u4e00-/u9fa5]+$");// 中英匹配,这个必须在中文匹配与英文匹配之后 Matcher m5 = englishChinese.matcher(word); if (m5.find()) return englishChineseFilter(word); return true; } /** * 判断关键字是否为中文停用词 * * @param word * 关键字 * @return true表示是停用词 */ public static boolean chineseFilter(String word) { // System.out.println("中文停用词判断"); if (chineseStopWords == null || chineseStopWords.size() == 0) return false; Iterator<String> iterator = chineseStopWords.iterator(); while (iterator.hasNext()) { if (iterator.next().equals(word)) return true; } return false; } /** * 判断关键字是否为英文停用词 * * @param word * 关键字 * @return true表示是停用词 */ public static boolean englishFilter(String word) { // System.out.println("英文停用词判断"); if (word.length() <= 2) return true; if (englishStopWords == null || englishStopWords.size() == 0) return false; Iterator<String> iterator = englishStopWords.iterator(); while (iterator.hasNext()) { if (iterator.next().equals(word)) return true; } return false; } /** * 判断关键字是否为英数停用词 * * @param word * 关键字 * @return true表示是停用词 */ public static boolean englishDigitFilter(String word) { return false; } /** * 判断关键字是否为中数停用词 * * @param word * 关键字 * @return true表示是停用词 */ public static boolean chineseDigitFilter(String word) { return false; } /** * 判断关键字是否为英中停用词 * * @param word * 关键字 * @return true表示是停用词 */ public static boolean englishChineseFilter(String word) { return false; } public static void main(String[] args) { /* * Iterator<String> iterator= * StopWordFilter.chineseStopWords.iterator(); int n=0; * while(iterator.hasNext()){ System.out.println(iterator.next()); n++; * } System.out.println("总单词量:"+n); */ boolean bool = StopWordFilter.filter("宝剑"); System.out.println(bool); }}

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.HashSet;import java.util.Iterator;import java.util.Set;/** * 加载停用词文件 * * @author ckm * */public class LoadStopWords { private Set<String> chineseStopWords = null;// 中文停用词集 private Set<String> englishStopWords = null;// 英文停用词集 /** * 获取中文停用词集 * * @return 中文停用词集Set<String>类型 */ public Set<String> getChineseStopWords() { return chineseStopWords; } /** * 设置中文停用词集 * * @param chineseStopWords * 中文停用词集Set<String>类型 */ public void setChineseStopWords(Set<String> chineseStopWords) { this.chineseStopWords = chineseStopWords; } /** * 获取英文停用词集 * * @return 英文停用词集Set<String>类型 */ public Set<String> getEnglishStopWords() { return englishStopWords; } /** * 设置英文停用词集 * * @param englishStopWords * 英文停用词集Set<String>类型 */ public void setEnglishStopWords(Set<String> englishStopWords) { this.englishStopWords = englishStopWords; } /** * 加载停用词库 */ public LoadStopWords() { chineseStopWords = loadStopWords(this.getClass().getResourceAsStream( "ChineseStopWords.txt")); englishStopWords = loadStopWords(this.getClass().getResourceAsStream( "EnglishStopWords.txt")); } /** * 从停用词文件中加载停用词, 停用词文件是普通GBK编码的文本文件, 每一行 是一个停用词,注释利用“//”, 停用词中包括中文标点符号, * 中文空格, 以及使用率太高而对索引意义不大的词。 * * @param input * 停用词文件流 * @return 停用词组成的HashSet */ public static Set<String> loadStopWords(InputStream input) { String line; Set<String> stopWords = new HashSet<String>(); try { BufferedReader br = new BufferedReader(new InputStreamReader(input, "GBK")); while ((line = br.readLine()) != null) { if (line.indexOf("//") != -1) { line = line.substring(0, line.indexOf("//")); } line = line.trim(); if (line.length() != 0) stopWords.add(line.toLowerCase()); } br.close(); } catch (IOException e) { System.err.println("不能打开停用词库!!"); } return stopWords; } public static void main(String[] args) { LoadStopWords lsw = new LoadStopWords(); Iterator<String> iterator = lsw.getEnglishStopWords().iterator(); int n = 0; while (iterator.hasNext()) { System.out.println(iterator.next()); n++; } System.out.println("总单词量:" + n); }}

这里需要ChineseStopWords.txt 与EnglishStopWords.txt分别存放中文与英文停用词,这里不知道怎么上传,还有就是ICTCLAS的基本文件。

完整项目下载地址:http://download.csdn.net/detail/km1218/7754907






分享给朋友:
您可能感兴趣的文章:
随机阅读: