前言:
笔者最近遇到一个需求:将文章输入后输出文章中的高频词,这是个简短的需求,但细分下便会出现许多细节重点。笔者细化需求后确定了这几个步骤: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的。
来源:oschina
链接:https://my.oschina.net/u/4267707/blog/4663104