參考文獻:http://blog.csdn.net/fatpanda/article/details/37911079
jar包:
IK-Analyzer-extra-5.3.1.jar
IKAnalyzer5.3.1.jar
lucene-core-5.3.1.jar
lucene-analyzers-common-5.3.1.jar
一、創建類自己的分詞器配置類並實現IK-Analyzer分詞器的配置接口:
值得注意的一點是:我們在指定配置文件時候,要指定我們自己的配置文件。如果不指定路徑默認會視作IKAnalyzer5.3.1.jar里面的IKAnalyzer.cfg.xml
import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.InvalidPropertiesFormatException; import java.util.List; import java.util.Properties; import org.apache.commons.lang.StringUtils; import org.wltea.analyzer.cfg.Configuration; public class MyConfiguration implements Configuration { // 懶漢單例 private static final Configuration CFG = new MyConfiguration(); /* * 分詞器默認字典路徑 */ private String PATH_DIC_MAIN = "org/wltea/analyzer/dic/main2012.dic";// 需要把static final去掉 private static final String PATH_DIC_QUANTIFIER = "org/wltea/analyzer/dic/quantifier.dic"; /* * 分詞器配置文件路徑 */ private static final String FILE_NAME = "com/unruly/test/IKAnalyzer.cfg.xml";// 指定我們自己的分詞器配置文件 // 配置屬性——擴展字典 private static final String EXT_DICT = "ext_dict"; // 配置屬性——擴展停止詞典 private static final String EXT_STOP = "ext_stopwords"; private Properties props; /* * 是否使用smart方式分詞 */ private boolean useSmart; /** * 返回單例 * * @return Configuration單例 */ public static Configuration getInstance() { return CFG; } /* * 初始化配置文件 */ MyConfiguration() { props = new Properties(); InputStream input = this.getClass().getClassLoader().getResourceAsStream(FILE_NAME); if (input != null) { try { props.loadFromXML(input); } catch (InvalidPropertiesFormatException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } /** * 返回useSmart標志位 useSmart =true ,分詞器使用智能切分策略, =false則使用細粒度切分 * * @return useSmart */ public boolean useSmart() { return useSmart; } /** * 設置useSmart標志位 useSmart =true ,分詞器使用智能切分策略, =false則使用細粒度切分 * * @param useSmart */ public void setUseSmart(boolean useSmart) { this.useSmart = useSmart; } /** * 新加函數:設置主詞典路徑 * * @return String 主詞典路徑 */ public void setMainDictionary(String path) { if(!StringUtils.isBlank(path)){ this.PATH_DIC_MAIN = path; } } /** * 獲取主詞典路徑 * * @return String 主詞典路徑 */ public String getMainDictionary() { return PATH_DIC_MAIN; } /** * 獲取量詞詞典路徑 * * @return String 量詞詞典路徑 */ public String getQuantifierDicionary() { return PATH_DIC_QUANTIFIER; } /** * 獲取擴展字典配置路徑 * * @return List<String> 相對類加載器的路徑 */ public List<String> getExtDictionarys() { List<String> extDictFiles = new ArrayList<String>(2); String extDictCfg = props.getProperty(EXT_DICT); if (extDictCfg != null) { // 使用;分割多個擴展字典配置 String[] filePaths = extDictCfg.split(";"); if (filePaths != null) { for (String filePath : filePaths) { if (filePath != null && !"".equals(filePath.trim())) { extDictFiles.add(filePath.trim()); } } } } return extDictFiles; } /** * 獲取擴展停止詞典配置路徑 * * @return List<String> 相對類加載器的路徑 */ public List<String> getExtStopWordDictionarys() { List<String> extStopWordDictFiles = new ArrayList<String>(2); String extStopWordDictCfg = props.getProperty(EXT_STOP); if (extStopWordDictCfg != null) { // 使用;分割多個擴展字典配置 String[] filePaths = extStopWordDictCfg.split(";"); if (filePaths != null) { for (String filePath : filePaths) { if (filePath != null && !"".equals(filePath.trim())) { extStopWordDictFiles.add(filePath.trim()); } } } } return extStopWordDictFiles; } }
二、IKAnalyzer.cfg.xml默認不指定自定義詞典
原因有二:
其一、我們是動態配置自定義詞典,這里配不配配置又何妨
其二、如果這里配置了自定義詞典、后面使用我們指定的詞典時 還需要清空這個里配置的詞典、
為什么要清空詞典?這里我要說明一下為什么在使用我們自定義的某個詞典前要清空詞典:因為你不清空默認就是累加。在使用分詞的時候,就是系統配置的詞典(或前一次使用的詞典)+你當前配置的某個詞典的合集。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--用戶可以在這里配置自己的擴展字典 -->
<entry key="ext_dict"></entry>
<!--用戶可以在這里配置自己的擴展停止詞字典-->
<entry key="ext_stopwords">com/unruly/test/stopword.dic;</entry>
</properties>
三、准備定義的詞典:(我這准備了三個)
四、獲取分詞
1、配置我們自己的MyConfiguration ,動態指定詞典路徑
MyConfiguration mycfg = new MyConfiguration(); mycfg.setUseSmart(true); //true 用智能分詞 ,false細粒度 mycfg.setMainDictionary("com/unruly/test/dict1.dic");//動態設置自定義的詞典
詞典也動態指定了,是不是現在就可以使用指定的詞典分詞了呢?天真。
2、手動去配置(刷新)詞典的詞組
我們僅僅指定了詞典的文件是根本沒有用的,雖然運行起來不會報錯,但是仔細去看文本拆分出來的詞組你會發現你這個配置根本沒有起到作用。這時候我們就需要手動的去刷新Dictionary對象的words屬性。
首先我們要拿到Dictionary對象(單例的)
private static Dictionary singleton; if (singleton == null) { singleton = Dictionary.initial(mycfg); }
其次我們動過這個singleton 對象來設置words()
//dictList:通過我們指定的詞典文件獲取到的詞組list dictList = getWordList(dict); singleton.addWords(dictList);
其實做到這一步就已經可以用於我們指定的詞典分詞了,但是我們似乎忘記了一件事情:清空不需要的詞組,如:我們第一次使用了A詞典分詞。第二次要用B詞典進行分詞。這時候如果你不想用A里面的分詞時我們就需要把A詞典從singleton 對象中移除去。
//在使用新詞典時,清除其他詞典(刷新) if(dictList.size()>0){ singleton.disableWords(dictList); }
這樣才是正在的OK了
3、開始分詞
String content = "曾做過周朝“守藏室之官”(管理藏書的官員),是中國古代偉大的思想家、哲學家、文學家和史學家," + "被道教尊為教祖,世界文化名人。老子思想主張“無為”,《老子》以“道”解釋宇宙萬物的演變," + "“道”為客觀自然規律,同時又具有“獨立不改,周行而不殆”的永恆意義。《老子》書中包括大量" + "朴素辯證法觀點,如以為一切事物均具有正反兩面,並能由對立而轉化,是為“反者道之動”,“正復為奇," + "善復為妖”,“禍兮福之所倚,福兮禍之所伏”。又以為世間事物均為“有”與“無”之統一,“有、無相生”," + "而“無”為基礎,“天下萬物生於有,有生於無”。他關於民眾的格言有:“天之道,損有余而補不足,人之道" + "則不然,損不足以奉有余”;“民之飢,以其上食稅之多”;“民之輕死,以其上求生之厚”;“民不畏死,奈何以" + "死懼之”。他的哲學思想和由他創立的道家學派,不但對中國古代思想文化的發展作出了重要貢獻,而且對" + "中國2000多年來思想文化的發展產生了深遠的影響。關於他的身份,還有人認為他是老萊子,也是楚國人," + "跟孔子同時,曾著書十五篇宣傳道家之用。"; StringReader input = new StringReader(content.trim()); IKSegmenter ikSeg = new IKSegmenter(input,mycfg); Lexeme lexeme=null; while((lexeme=ikSeg.next())!=null){ String keys = lexeme.getLexemeText(); if(keys.length()>1){ System.out.println(keys); } } input.close();
五、分詞結果:
1、使用jar包自帶的詞典:org/wltea/analyzer/dic/main2012.dic
2、使用dict1詞典:
3、使用dict2詞典:
4、使用dict3詞典:
六、總結解釋:從結果可以看出,同樣的一段文本,使用不同的詞典,獲取到的詞組是不一樣的並且獲得的詞組不會混雜
至於“2000” 、“十五篇” 這種帶數字和字母的詞組分詞器會默認拆分出來的。至於為啥?你問我?我也不知道。因為我們搞的是中文分詞器,程序給你把數字,單詞也拆出來也是正常的
七、demo完整代碼:
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Properties; import javax.imageio.stream.FileImageInputStream; import org.apache.commons.lang.StringUtils; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.util.ResourceLoader; import org.wltea.analyzer.cfg.Configuration; import org.wltea.analyzer.core.IKSegmenter; import org.wltea.analyzer.core.Lexeme; import org.wltea.analyzer.dic.Dictionary; import org.wltea.analyzer.lucene.IKAnalyzer; import org.wltea.analyzer.util.IKTokenizerFactory; public class TestIK { private static Dictionary singleton; private static ResourceLoader loader; static List<String> dictList = new ArrayList<String>(); public static void main(String[] args) throws IOException { for (int i = 0; i < 4; i++) { test(i); } } public static void test(int num) throws IOException{ String content = "曾做過周朝“守藏室之官”(管理藏書的官員),是中國古代偉大的思想家、哲學家、文學家和史學家," + "被道教尊為教祖,世界文化名人。老子思想主張“無為”,《老子》以“道”解釋宇宙萬物的演變," + "“道”為客觀自然規律,同時又具有“獨立不改,周行而不殆”的永恆意義。《老子》書中包括大量" + "朴素辯證法觀點,如以為一切事物均具有正反兩面,並能由對立而轉化,是為“反者道之動”,“正復為奇," + "善復為妖”,“禍兮福之所倚,福兮禍之所伏”。又以為世間事物均為“有”與“無”之統一,“有、無相生”," + "而“無”為基礎,“天下萬物生於有,有生於無”。他關於民眾的格言有:“天之道,損有余而補不足,人之道" + "則不然,損不足以奉有余”;“民之飢,以其上食稅之多”;“民之輕死,以其上求生之厚”;“民不畏死,奈何以" + "死懼之”。他的哲學思想和由他創立的道家學派,不但對中國古代思想文化的發展作出了重要貢獻,而且對" + "中國2000多年來思想文化的發展產生了深遠的影響。關於他的身份,還有人認為他是老萊子,也是楚國人," + "跟孔子同時,曾著書十五篇宣傳道家之用。"; StringReader input = new StringReader(content.trim()); MyConfiguration mycfg = new MyConfiguration(); mycfg.setUseSmart(true); //true 用智能分詞 ,false細粒度 if (singleton == null) { singleton = Dictionary.initial(mycfg); } String dict = null; if(num==0){ dict = "org/wltea/analyzer/dic/main2012.dic";//分詞jar包自帶的詞典 System.out.println("加載擴展詞典:"+dict); mycfg.setMainDictionary(dict);//動態設置自定義的詞庫 //在使用新詞典時,清除其他詞典(刷新) if(dictList.size()>0){ singleton.disableWords(dictList); } //dictList:通過我們指定的詞典文件獲取到的詞組list dictList = getWordList(dict); singleton.addWords(dictList); }else if(num==1){ dict = "com/unruly/test/dict1.dic"; System.out.println("加載擴展詞典:"+dict); mycfg.setMainDictionary(dict);//動態設置自定義的詞庫 //在使用新詞典時,清除其他詞典(刷新) if(dictList.size()>0){ singleton.disableWords(dictList); } dictList = getWordList(dict); singleton.addWords(dictList); }else if(num==2){ dict = "com/unruly/test/dict2.dic"; System.out.println("加載擴展詞典:"+dict); mycfg.setMainDictionary(dict);//動態設置自定義的詞庫 //在使用新詞典時,清除其他詞典(刷新) if(dictList.size()>0){ singleton.disableWords(dictList); } dictList = getWordList(dict); singleton.addWords(dictList); }else if(num==3){ dict = "com/unruly/test/dict3.dic"; System.out.println("加載擴展詞典:"+dict); mycfg.setMainDictionary(dict);//動態設置自定義的詞庫 //在使用新詞典時,清除其他詞典(刷新) if(dictList.size()>0){ singleton.disableWords(dictList); } dictList = getWordList(dict); singleton.addWords(dictList); } IKSegmenter ikSeg = new IKSegmenter(input,mycfg); Lexeme lexeme=null; while((lexeme=ikSeg.next())!=null){ String keys = lexeme.getLexemeText(); if(keys.length()>1){ System.out.println(keys); } } input.close(); } /** * 讀取字典 * @param dict * @return */ public static List<String> getWordList(String dict) { List<String> list = new ArrayList<>(); InputStream is = null; try { is = TestIK.class.getClass().getResourceAsStream("/"+dict); BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"), 1024); String theWord = null; do { theWord = br.readLine(); if (theWord != null && !"".equals(theWord.trim())) { list.add(theWord); } } while (theWord != null); } catch (IOException ioe) { System.err.println("字典文件讀取失敗:"+dict); ioe.printStackTrace(); } finally { try { if (is != null) { is.close(); is = null; } } catch (IOException e) { e.printStackTrace(); } } return list; } }