当前位置 博文首页 > 闫玉林的博客:java使用正则表达式,针对自定义分词标签,对中文

    闫玉林的博客:java使用正则表达式,针对自定义分词标签,对中文

    作者:[db:作者] 时间:2021-08-17 18:55

    业务场景

    • 自己定义分词标签,不使用中文分词工具,自己整理收集添加词语(是为了满足任意词语,如人名等)
    • 分词标签可能会互相包含,例如 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;
    
    /**
     * 词频统计工具类
     * @author yanyulin
     * @date 2020-3-4 00:39:47
     */
    public class WordCountUtil {
        //main方法测试
        public static void main(String[] args) {
            String content = "3月2日上午,市长凌云深入企业、职工宿舍、机场,调研疫情防控和经济社会发展工作。市政府秘书长罗平参加调研。\n" +
                    "\n" +
                    "在合肥长鑫存储技术有限公司,凌云听工作汇报、问应急预案,详细了解企业疫情防控和复工复产情况。得知公司目前员工返岗率达96%,凌云指出,要进一步健全企业内部防控机制,不断加强生产运营调度,加快推动复工达产,努力完成阶段性和全年目标任务。作为安徽单体投资最大的工业项目,该公司外籍员工众多,对外籍员工返肥,公司采取“提前申报+点对点接返+集中隔离”等措施,确保防控和复工“两不误”。对此,凌云表示肯定。她强调,企业要进一步夯实主体责任,严而又严、细而又细、实而又实地做好各项防控工作,确保员工生命健康安全。要主动做好对接工作,为员工提供温馨服务,同时不断完善医院、学校等配套设施建设,让他们留得下住得好,实现安居乐业。对公司提出需要亟需解决的实际问题,凌云要求相关部门,要下大决心,尽快制定切实可行的方案,全力帮助企业稳产、增产。\n" +
                    "\n" +
                    "在新桥国际机场,凌云实地查看国内厅、国际厅安全公司进出口体温测量设备使用情况,详细了解旅客体温检测、扫码登记等工作开展情况。她指出,目前,国际进出口公司境外输入是疫情防控工作面临的新挑战。要高度警惕境外人员带来的疫情输入风险,克服麻痹思想、摒弃厌战情绪和侥幸心理,进一步夯实责任,落实管控措施,坚决防止疫情输入扩散。要进一步优化工作流程,全力做好来肥人员体温检测和后续跟进工作,切实守好第一道关口。公安边防、海关、机场等部门要建立联防联控工作机制,不断细化工作任务,以更加扎实细致的作风和务实有力的举措,保障旅客安全有序出行,为全面打赢疫情防控阻击战贡献力量。(赵俊松)";
            String words = "疫情|防控|应急|工作|经济社会|安全|企业|国际进出口|复工|公司|疫情防控|安全公司|复工复产|国际|国际进出口公司";
            wordCount(words,content);
        }
    
        /**
         * 标签词处理,词频检测
         * @param words 词库词语
         * @param content 需要检测的内容
         * @return Map<String,Integer>
         */
        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);
        }
    
        /**
         * 词频检测
         * @param regexMap 标签词语正则
         * @param content 需要检测的内容
         * @return Map<String,Integer>
         */
        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;
        }
    
        //命中统计
    
        /**
         * 正则命中词频统计
         * @param regex 正则表达式
         * @param content 需要统计的内容
         * @return Map<String,Integer>
         */
        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包,直接对文章内容分词,词频统计
    • 可以利用已有的开源中文词库,作为自己的原始标签库,再手动添加个性化需要的
    • 本文应对的需求是只统计词频,不考虑词性等要素,才可以直接使用正则表达式处理
    cs