业务场景
- 自己定义分词标签,不使用中文分词工具,自己整理收集添加词语(是为了满足任意词语,如人名等)
- 分词标签可能会互相包含,例如 ABC, AB ,BC三个标签词,对于输入“ABCD”三个标签都要命中,词频加一
- 需要统计标签词语的出现频率,按照词频倒序
- 使用尽可能少的查找次数统计出来
- 避免内存溢出
- 考虑匹配的速度和效率
代码实现
- 使用正则表达式 find group,统计词频
- 为了处理标签词的包含关系,对标签词语遍历处理,得到多个正则表达式
- 使用java8一些特性,对list、map排序
import com.alibaba.fastjson.JSON;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class WordCountUtil {
public static void main(String[] args) {
String content = "3月2日上午,市长凌云深入企业、职工宿舍、机场,调研疫情防控和经济社会发展工作。市政府秘书长罗平参加调研。\n" +
"\n" +
"在合肥长鑫存储技术有限公司,凌云听工作汇报、问应急预案,详细了解企业疫情防控和复工复产情况。得知公司目前员工返岗率达96%,凌云指出,要进一步健全企业内部防控机制,不断加强生产运营调度,加快推动复工达产,努力完成阶段性和全年目标任务。作为安徽单体投资最大的工业项目,该公司外籍员工众多,对外籍员工返肥,公司采取“提前申报+点对点接返+集中隔离”等措施,确保防控和复工“两不误”。对此,凌云表示肯定。她强调,企业要进一步夯实主体责任,严而又严、细而又细、实而又实地做好各项防控工作,确保员工生命健康安全。要主动做好对接工作,为员工提供温馨服务,同时不断完善医院、学校等配套设施建设,让他们留得下住得好,实现安居乐业。对公司提出需要亟需解决的实际问题,凌云要求相关部门,要下大决心,尽快制定切实可行的方案,全力帮助企业稳产、增产。\n" +
"\n" +
"在新桥国际机场,凌云实地查看国内厅、国际厅安全公司进出口体温测量设备使用情况,详细了解旅客体温检测、扫码登记等工作开展情况。她指出,目前,国际进出口公司境外输入是疫情防控工作面临的新挑战。要高度警惕境外人员带来的疫情输入风险,克服麻痹思想、摒弃厌战情绪和侥幸心理,进一步夯实责任,落实管控措施,坚决防止疫情输入扩散。要进一步优化工作流程,全力做好来肥人员体温检测和后续跟进工作,切实守好第一道关口。公安边防、海关、机场等部门要建立联防联控工作机制,不断细化工作任务,以更加扎实细致的作风和务实有力的举措,保障旅客安全有序出行,为全面打赢疫情防控阻击战贡献力量。(赵俊松)";
String words = "疫情|防控|应急|工作|经济社会|安全|企业|国际进出口|复工|公司|疫情防控|安全公司|复工复产|国际|国际进出口公司";
wordCount(words,content);
}
public static Map<String,Integer> wordCount(String words,String content){
List<String> wordList = Arrays.asList(words.split("\\|"));
wordList.sort(Comparator.comparingInt(String::length));
Map<Integer,String> regexMap = new LinkedHashMap<>();
for(String word:wordList){
if(regexMap.size() == 0){
regexMap.put(0,word);
continue;
}
for(int i=0; i < regexMap.size(); i++){
String regex = regexMap.get(i);
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(word);
if(!m.find()){
regex += "|" + word;
regexMap.put(i,regex);
break;
}else if(i+1 == regexMap.size()){
regexMap.put(i+1,word);
break;
}
}
}
return wordCount(regexMap,content);
}
public static Map<String,Integer> wordCount(Map<Integer,String> regexMap,String content){
System.out.println("正则表达式列表为:" + JSON.toJSONString(regexMap));
Map<String,Integer> result = new HashMap<>();
for(int i=0; i < regexMap.size(); i++){
String regex = regexMap.get(i);
result.putAll(hitStatistic(regex,content));
}
final Map<String, Integer> sortedByCount = result.entrySet()
.stream()
.sorted((Map.Entry.<String, Integer>comparingByValue().reversed()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
System.out.println("匹配结果为:" + JSON.toJSONString(sortedByCount));
return sortedByCount;
}
private static Map<String,Integer> hitStatistic(String regex,String content){
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
Map<String,Integer> result = new LinkedHashMap<>();
while (m.find()){
String target = m.group();
if(result.containsKey(target)){
result.put(target,result.get(target) + 1);
}else {
result.put(target, 1);
}
}
return result;
}
}
改进思考
- 匹配的速度、效率,内存使用情况,还没有进行测试,需进一步压测
- 可以使用结巴分词等第三方jar包,直接对文章内容分词,词频统计
- 可以利用已有的开源中文词库,作为自己的原始标签库,再手动添加个性化需要的
- 本文应对的需求是只统计词频,不考虑词性等要素,才可以直接使用正则表达式处理