solo L 發(fā)布日期:2006年07月10日,,更新日期:2006年07月30日 Apache Lucene作為一個開放源碼的搜索軟件包應(yīng)用越來越廣泛,但是對于中文用戶來說其提供的兩個中文分詞器(CJKAnalyzer,、ChineseAnalyzer)的功能又太弱了。所以迫切需要開發(fā)自己的中文分詞器,,而開發(fā)適用的分詞器是一項很有挑戰(zhàn)的工作,。我想在文章中實現(xiàn)一個中文分詞器,讓它實現(xiàn)機(jī)械分詞中最簡單的算法--正向最大匹配法,。目前普遍認(rèn)為這一算法的錯分率為1/169,,雖然這不是一個精確的分詞算法,,但是它的實現(xiàn)卻很簡單。我想它已經(jīng)可以滿足一些項目的應(yīng)用了,。這一算法是依賴于詞庫的,,詞庫的好壞對于錯分率有重要影響,因此還想介紹一個詞庫,。 這篇文章的內(nèi)容質(zhì)量不是很高,,您可以在這里找到更新后的版本。Solo L正在努力提高這里所提供的內(nèi)容的質(zhì)量,。如果由于內(nèi)容的質(zhì)量問題給您造成了影響,,我在此真誠的表示歉意! 回頁首 什么是中文分詞眾所周知,,英文是以詞為單位的,詞和詞之間是靠空格隔開,,而中文是以字為單位,句子中所有的字連起來才能描述一個意思,。例如,英文句子I am a student,,用中文則為:“我是一個學(xué)生”,。計算機(jī)可以很簡單通過空格知道student是一個單詞,但是不能很容易明白“學(xué)”,、“生”兩個字合起來才表示一個詞,。把中文的漢字序列切分成有意義的詞,就是中文分詞,,有些人也稱為切詞,。我是一個學(xué)生,分詞的結(jié)果是:我 是 一個 學(xué)生,。 回頁首 中文分詞技術(shù)現(xiàn)有的分詞技術(shù)可分為三類:
這篇文章中使用的是基于字符串匹配的分詞技術(shù),這種技術(shù)也被稱為機(jī)械分詞,。它是按照一定的策略將待分析的漢字串與一個“充分大的”詞庫中的詞條進(jìn)行匹配,。若在詞庫中找到某個字符串則匹配成功(識別出一個詞),。按照掃描方向的不同,,串匹配分詞方法可以分為正向匹配和逆向匹配,;按照不同長度優(yōu)先匹配的情況,可以分為最大(最長)匹配和最?。ㄗ疃蹋┢ヅ?;按照是否與詞性標(biāo)注過程相結(jié)合,,又可以分為單純分詞法和分詞與標(biāo)注結(jié)合法。常用的幾種機(jī)械分詞方法如下:
回頁首 分詞器實現(xiàn)這個實現(xiàn)了機(jī)械分詞中正向最大匹配法的Lucene分詞器包括兩個類,CJKAnalyzer和CJKTokenizer,,他們的源代碼如下: package org.solol.analysis; import java.io.Reader; import java.util.Set; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.StopFilter; import org.apache.lucene.analysis.TokenStream; /** * @author solo L * */ public class CJKAnalyzer extends Analyzer {//實現(xiàn)了Analyzer接口,這是lucene的要求 public final static String[] STOP_WORDS = {}; private Set stopTable; public CJKAnalyzer() { stopTable = StopFilter.makeStopSet(STOP_WORDS); } @Override public TokenStream tokenStream(String fieldName, Reader reader) { return new StopFilter(new CJKTokenizer(reader), stopTable); } } package org.solol.analysis; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.TreeMap; import org.apache.lucene.analysis.Token; import org.apache.lucene.analysis.Tokenizer; /** * @author solo L * */ public class CJKTokenizer extends Tokenizer { //這個TreeMap用來緩存詞庫 private static TreeMap simWords = null; private static final int IO_BUFFER_SIZE = 256; private int bufferIndex = 0; private int dataLen = 0; private final char[] ioBuffer = new char[IO_BUFFER_SIZE]; private String tokenType = "word"; public CJKTokenizer(Reader input) { this.input = input; } //這里是lucene分詞器實現(xiàn)的最關(guān)鍵的地方 public Token next() throws IOException { loadWords(); StringBuffer currentWord = new StringBuffer(); while (true) { char c; Character.UnicodeBlock ub; if (bufferIndex >= dataLen) { dataLen = input.read(ioBuffer); bufferIndex = 0; } if (dataLen == -1) { if (currentWord.length() == 0) { return null; } else { break; } } else { c = ioBuffer[bufferIndex++]; ub = Character.UnicodeBlock.of(c); } //通過這個條件不難看出這里只處理了CJK_UNIFIED_IDEOGRAPHS,, //因此會丟掉其它的字符,,如它會丟掉LATIN字符和數(shù)字 //這也是該lucene分詞器的一個限制,您可以在此基礎(chǔ)之上完善它,, //也很歡迎把您完善的結(jié)果反饋給我 if (Character.isLetter(c) && ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS) { tokenType = "double"; if (currentWord.length() == 0) { currentWord.append(c); } else { //這里實現(xiàn)了正向最大匹配法 String temp = (currentWord.toString() + c).intern(); if (simWords.containsKey(temp)) { currentWord.append(c); } else { bufferIndex--; break; } } } } Token token = new Token(currentWord.toString(), bufferIndex - currentWord.length(), bufferIndex, tokenType); currentWord.setLength(0); return token; } //裝載詞庫,,您必須明白它的邏輯和之所以這樣做的目的,,這樣您才能理解正向最大匹配法是如何實現(xiàn)的 public void loadWords() { if (simWords != null)return; simWords = new TreeMap(); try { InputStream words = new FileInputStream("simchinese.txt"); BufferedReader in = new BufferedReader(new InputStreamReader(words,"UTF-8")); String word = null; while ((word = in.readLine()) != null) { //#使得我們可以在詞庫中進(jìn)行必要的注釋 if ((word.indexOf("#") == -1) && (word.length() < 5)) { simWords.put(word.intern(), "1"); if (word.length() == 3) { if (!simWords.containsKey(word.substring(0, 2).intern())) { simWords.put(word.substring(0, 2).intern(), "2"); } } if (word.length() == 4) { if (!simWords.containsKey(word.substring(0, 2).intern())) { simWords.put(word.substring(0, 2).intern(), "2"); } if (!simWords.containsKey(word.substring(0, 3).intern())) { simWords.put(word.substring(0, 3).intern(), "2"); } } } } in.close(); } catch (IOException e) { e.printStackTrace(); } } } 回頁首 分詞效果這是我在當(dāng)日的某新聞搞中隨意選的一段話: 分詞結(jié)果為: 回頁首 提示這個lucene分詞器還比較脆弱,要想將其用于某類項目中您還需要做一些工作,,不過我想這里的lucene分詞器會成為您很好的起點,。 參考資料
關(guān)于作者 solo L 一位有些理想主義的軟件工程師,創(chuàng)建了,。他常常在這里發(fā)表一些對技術(shù)的見解,。 |
|