筆者最近遇到一個需求:將文章輸入後輸出文章中的高頻詞,這是個簡短的需求,但細分下便會出現許多細節重點。筆者細化需求後確定了這幾個步驟:1. 文章分詞(包括中英文混詞)——> 2. 分詞統計——>3. 推薦熱詞。
根據上述的簡單需求,我就想用原生JAVA通過某些資料結構實現,由於知識面有限且筆者目前是名在校的學生,實現了英文下的分詞、中文下的分詞。但是遇到中英文混排的怎麼也合併不了。經過兩天的各種思考各種分析結果以失敗告終。在查閱資料的時候發現了阿帕奇的OpenNLP 工具,然後仔細的看了看原始碼。。看的也是雲裡霧裡的,但基本思想也瞭解了。雖然阿帕奇的OpenNLP很牛逼,但是我還是選擇了一個國人自產基於n-Gram+CRF+HMM的分詞JAVA實現。具體開發檔案和原始碼可以存取GITHUB。
廢話不多說上原始碼。
package com.sim;
import org.ansj.splitWord.analysis.ToAnalysis;
import java.io.*;
import java.util.*;
public class NLPTools {
public static Map<String,String> wordFrequency(String article) {
Map<String, Integer> map = new HashMap<String, Integer>();
Map<String,String > info = new HashMap<String, String>();
String result = ToAnalysis.parse(article).toStringWithOutNature();
System.out.println(result);
String[] words = result.split(",");
for(String word: words){
String str = word.trim();
// 過濾空白字元
if (str.equals(""))
continue;
// 過濾一些高頻率的符號
else if(str.matches("[)|(|.|,|。|+|-|「|」|:|?|\\s]"))
continue;
// 此處過濾長度為1的str
else if (str.length() < 2)
continue;
if (!map.containsKey(word)){
map.put(word, 1);
} else {
int n = map.get(word);
map.put(word, ++n);
}
}
StringBuffer cp = new StringBuffer();// 詞頻
StringBuffer rc = new StringBuffer();// 熱詞
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> entry = iterator.next();
cp.append(entry.getKey() + ": " + entry.getValue()+"\t");
//System.out.print(entry.getKey() + ": " + entry.getValue()+"\t");
}
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>();
Map.Entry<String, Integer> entry;
while ((entry = getMax(map)) != null){
list.add(entry);
}
// System.out.print("\n前五熱詞為:");
rc.append("前五熱詞為:");
for (int i = 0; i < 5; i++) {
rc.append(""+list.get(i)+"\t");
// System.out.print(list.get(i)+"\t");
}
info.put("cp",cp.toString());
info.put("rc",rc.toString());
return info;
// System.out.println(Arrays.toString(list.toArray()));
}
/**
* 找出map中value最大的entry, 返回此entry, 並在map刪除此entry
* @param map
* @return
*/
public static Map.Entry<String, Integer> getMax(Map<String, Integer> map){
if (map.size() == 0){
return null;
}
Map.Entry<String, Integer> maxEntry = null;
boolean flag = false;
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> entry = iterator.next();
if (!flag){
maxEntry = entry;
flag = true;
}
if (entry.getValue() > maxEntry.getValue()){
maxEntry = entry;
}
}
map.remove(maxEntry.getKey());
return maxEntry;
}
/**
* 從檔案中讀取待分割的文章素材.
* @return
* @throws IOException
*/
public static String getString() throws IOException {
FileInputStream inputStream = new FileInputStream(new File("e://a.txt"));
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder strBuilder = new StringBuilder();
String line;
while((line = reader.readLine()) != null){
strBuilder.append(line);
}
reader.close();
inputStream.close();
return strBuilder.toString();
}
}
package com.sim;
import org.ansj.splitWord.analysis.ToAnalysis;
import java.util.ArrayList;
import java.util.List;
public class JTest {
public static void main(String[] args) throws Exception{
String str = "這幾天心裡頗不寧靜。今晚在院子裡坐著乘涼,忽然想起日日走過的荷塘,在這滿月的光裡,總該另有一番樣子吧。月亮漸漸地升高了,牆外馬路上孩子們的歡笑,已經聽不見了;妻在屋裡拍著閏兒,迷迷糊糊地哼著眠歌。我悄悄地披了大衫,帶上門出去。\n" +
" 沿著荷塘,是一條曲折的小煤屑路。這是一條幽僻的路;白天也少人走,夜晚更加寂寞。荷塘四面,長著許多樹,蓊蓊鬱鬱的。路的一旁,是些楊柳,和一些不知道名字的樹。沒有月光的晚上,這路上陰森森的,有些怕人。今晚卻很好,雖然月光也還是淡淡的。\n" +
" 路上只我一個人,揹著手踱著。這一片天地好像是我的;我也像超出了平常的自己,到了另一世界裡。我愛熱鬧,也愛冷靜;愛群居,也愛獨處。像今晚上,一個人在這蒼茫的月下,什麼都可以想,什麼都可以不想,便覺是個自由的人。白天裡一定要做的事,一定要說的話,現在都可不理。這是獨處的妙處,我且受用這無邊的荷香月色好了。\n" +
" 曲曲折折的荷塘上面,彌望的是田田的葉子。葉子出水很高,像亭亭的舞女的裙。層層的葉子中間,零星地點綴著些白花,有嫋娜地開著的,有羞澀地打著朵兒的;正如一粒粒的明珠,又如碧天裡的星星,又如剛出浴的美人。微風過處,送來縷縷清香,彷彿遠處高樓上渺茫的歌聲似的。這時候葉子與花也有一絲的顫動,像閃電般,霎時傳過荷塘的那邊去了。葉子本是肩並肩密密地挨著,這便宛然有了一道凝碧的波痕。葉子底下是脈脈的流水,遮住了,不能見一些顏色;而葉子卻更見風致了。";
System.out.println(NLPTools.wordFrequency(str));
}
}
返回的是map ,前面是排序後的熱詞後面是所有詞的統計。好了,到這裡測試還沒結束,因為中文/英文單語言實現還是非常簡單的,如果中英混雜呢?
所以正常通過。還是很nice的。