使用Java实现拼音模糊搜索功能(支持拼音、首字母、多音字、谐音字、汉字、阿拉伯数字)

news2024/9/20 15:45:56

🍎 介绍

Java实现的简单的工具类支持(拼音, 多音字, 谐音字, 汉字, 阿拉伯数字) 对标阿里钉钉的上方搜索栏实现的


🍉 对应依赖

<!-- https://mvnrepository.com/artifact/com.github.open-android/pinyin4j -->
<dependency>
    <groupId>com.github.open-android</groupId>
    <artifactId>pinyin4j</artifactId>
    <version>2.5.0</version>
</dependency>
// https://mvnrepository.com/artifact/com.github.open-android/pinyin4j
implementation group: 'com.github.open-android', name: 'pinyin4j', version: '2.5.0'

🔥 代码调试

  1. 代码中含有一个main方法, 在方法传入参数即可使用
  2. fuzzyQuery()就是模糊查询的方法, name即为用户输入的拼音, userName即为需要匹配的名称集合

 


👀 业务思路

  1. 当库中已进行租户隔离并且业务数据较少时,您可以使用工具类对库中的姓名进行模糊匹配(前提需要将库中所有的数据查询出来进行比对),并将结果全部查出, 再将结果去库中查询
  2. 当库中的数据量庞大且增长速度快时,我们建议将生成的拼音字符串存储到MySQL中,然后再进行模糊匹配操作。这样可以提高查询效率。

💰 源码

仓库地址(后期会更新) : https://gitee.com/programmer-k/pinyin-fuzzy-search

package com.itjcloud.dingdong.server.common.utils;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pinyin4j.PinyinHelper;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 拼音模糊搜索工具类
 */
@Slf4j
public class PinyinFuzzySearchUtil {

    //汉字
    private static final int CHINESE_CHARACTER = 1;
    //拼音
    private static final int PIN_YIN = 2;
    //即包含汉字,也包含字母
    private static final int WHOLE = 3;


    //测试方法
    public static void main(String[] args) {
        System.out.println("------------------------------最终搜索结果>>>" + fuzzyQuery("zhang", Arrays.asList("刘长青1号?测试", "刘长青2号[]+-", "忘长长", "张长")));
    }


    // 将n个数组元素进行组合
    public static List<String> combineArrays(List<List<String>> arrays) {
        List<String> result = new ArrayList<>();
        // 从索引0开始组合
        combineHelper(arrays, 0, "", result);
        return result;
    }

    // 递归函数,用于将数组元素进行组合
    private static void combineHelper(List<List<String>> arrays, int index, String current, List<String> result) {
        // 当索引等于数组个数时,表示已经遍历完所有数组元素,将当前组合添加到结果列表中
        if (index == arrays.size()) {
            result.add(current);
            return;
        }

        // 获取当前数组
        List<String> currentArray = arrays.get(index);
        // 遍历当前数组的元素
        for (String word : currentArray) {
            // 递归调用,将当前元素与下一个数组进行组合
            combineHelper(arrays, index + 1, current + word, result);
        }
    }

    /**
     * 拼音转换(不含首字母)
     *
     * @param chinese 中文姓名
     * @return 拼音
     */
    public static List<String> pinYinConvertNoInitial(String chinese) {
        // 存储拼音字符串
        List<List<String>> pinyinArrays = new ArrayList<>();

        for (int i = 0; i < chinese.length(); i++) {
            char c = chinese.charAt(i);

            // 忽略空格
            if (Character.isWhitespace(c)) {
                continue;
            }

            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);
            log.info("pinyinArray:{}", (Object) pinyinArray);

            if (pinyinArray != null && pinyinArray.length > 0) {
                // 使用正则表达式去除声调
                List<String> pinyin = Arrays.stream(pinyinArray).map(str -> str.replaceAll("[1-5]", "")).collect(Collectors.toList());
                pinyinArrays.add(pinyin);
            }
        }

        //最终的拼音组合
        System.out.println("---------" + pinyinArrays);
        List<String> pinyinCombinations = combineArrays(pinyinArrays);
        log.info("输出拼音:{}", JSON.toJSONString(pinyinCombinations));

        return pinyinCombinations;
    }

    /**
     * 拼音转换
     *
     * @param chinese 中文姓名
     * @return 拼音和首字母
     */
    public static String pinYinConvert(String chinese) {


        // 存储拼音字符串
        List<List<String>> pinyinArrays = new ArrayList<>();
        // 存储首字母
        List<List<String>> initialArrays = new ArrayList<>();

        for (int i = 0; i < chinese.length(); i++) {
            char c = chinese.charAt(i);

            // 忽略空格
            if (Character.isWhitespace(c)) {
                continue;
            }


            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);
            log.info("pinyinArray:{}", (Object) pinyinArray);

            if (pinyinArray != null && pinyinArray.length > 0) {

                // 使用正则表达式去除声调
                List<String> pinyin = Arrays.stream(pinyinArray).map(str -> str.replaceAll("[1-5]", "")).collect(Collectors.toList());
                pinyinArrays.add(pinyin);

                //首字母
                List<String> initial = Arrays.stream(pinyinArray).map(str -> str.replaceAll("[1-5]", "")).map(str -> String.valueOf(str.charAt(0))).collect(Collectors.toList());
                initialArrays.add(initial);

            }
        }

        //最终的拼音组合
        List<String> pinyinCombinations = combineArrays(pinyinArrays);
        String pinyinCombinationsJoin = String.join(",", pinyinCombinations);

        //最终的首字母组合
        List<String> initialCombinations = combineArrays(initialArrays);
        String initialCombinationsJoin = String.join(",", initialCombinations);

        log.info("输出首字母,拼音:{}", initialCombinationsJoin + "," + pinyinCombinationsJoin);
        return initialCombinationsJoin + "," + pinyinCombinationsJoin;
    }



    /**
     * 汉字模糊搜索
     *
     * @param chineseCharacters 用户输入的汉字
     * @param userName          需要匹配的名称集合
     * @return 匹配成功用户名称
     */
    public static List<String> chineseCharactersFuzzySearch(String chineseCharacters, List<String> userName) {
        if (CollectionUtil.isEmpty(userName)) {
            log.info("userName为空");
            return null;
        }

        //如果用户输入的是单个字符,则按照中文模糊搜索
        if (chineseCharacters.length() == 1) {
            return userName.stream().map(name -> {
                if (name.contains(chineseCharacters)) {
                    return name;
                }
                return null;
            }).filter(Objects::nonNull).collect(Collectors.toList());
        }

        //如果用户输入的事多个字符, 则按照谐音去搜索, 既输入长三 可以搜索出张三
        if (chineseCharacters.length() > 1) {
            //将汉字转换为拼音
            List<String> pinyin = pinYinConvertNoInitial(chineseCharacters);

            //循环拼音模糊搜索, 将返回值扁平流组装为一个数组
            return pinyin.stream().flatMap(user -> Objects.requireNonNull(pinyinFuzzySearch(user, userName)).stream()).distinct().collect(Collectors.toList());
        }

        return null;
    }

    /**
     * 拼音模糊搜索
     *
     * @param pinyin   用户输入的拼音
     * @param userName 需要匹配的名称集合
     * @return 匹配成功用户名称
     */
    public static List<String> pinyinFuzzySearch(String pinyin, List<String> userName) {

        String lowercasePinYin = pinyin.toLowerCase();

        if (CollectionUtil.isEmpty(userName)) {
            log.info("userName为空");
            return null;
        }

        //存储<拼音大小写, 姓名>
        Map<String, String> pinYinMap = new HashMap<>();
        for (String name : userName) {
            //将姓名转换为 拼音首字母 + 拼音
            String pinYin = pinYinConvert(name);
            pinYinMap.put(pinYin, name);
        }

        List<String> userNameList = new ArrayList<>();

        //循环对拼音进行模糊匹配
        pinYinMap.forEach((py, name) -> {
            if (py.contains(lowercasePinYin)) {
                userNameList.add(name);
            }
        });

        return userNameList;
    }

    /**
     * 判断当前字符串的类型
     *
     * @param userName 用户名称
     * @return 1是汉字 2是字母 3既是汉字也是字母
     */
    public static int determineStringType(String userName) {
        if (userName.matches("[\\u4E00-\\u9FA5]+")) {
            log.info("字符串由汉字组成:{}", userName);
            return CHINESE_CHARACTER;
        } else if (userName.matches("[a-zA-Z]+")) {
            log.info("字符串由字母组成:{}", userName);
            return PIN_YIN;
        } else {
            log.info("字符串既包含汉字又包含字母:{}", userName);
            return WHOLE;
        }
    }

    public static String  convertToChinese(String str) {
        String pattern = ".*\\d+.*"; // 包含数字的正则表达式

        if (str.matches(pattern)) {
            str = str.replaceAll("0", "零")
                    .replaceAll("1", "一")
                    .replaceAll("2", "二")
                    .replaceAll("3", "三")
                    .replaceAll("4", "四")
                    .replaceAll("5", "五")
                    .replaceAll("6", "六")
                    .replaceAll("7", "七")
                    .replaceAll("8", "八")
                    .replaceAll("9", "九");
        }
        return str; // 返回修改后的值
    }


    public static List<String> numberToChineseCharacters(List<String> userName) {
        List<String> list = new ArrayList<>();
        for (String str : userName) {
            list.add(convertToChinese(str));
        }
        return list;
    }

    /**
     * 模糊查询
     *
     * @param name     用户输入的拼音
     * @param userName 需要匹配的名称集合
     */
    public static List<String> fuzzyQuery(String name, List<String> userName) {

        String convertName = convertToChinese(name);

        List<String> convertUserName = numberToChineseCharacters(userName);

        //判断当前字符串的类型
        int state = determineStringType(convertName);

        //判断用户输入的是否是 汉字
        if (state == CHINESE_CHARACTER) {
            return chineseCharactersFuzzySearch(convertName, convertUserName);
        }

        //判断用户输入的是否是 拼音
        if (state == PIN_YIN) {
            return pinyinFuzzySearch(convertName, convertUserName);
        }

        //判断用户输入的 即输入拼音也输入汉字
        if (state == WHOLE) {
            return null;
        }

        return null;
    }


    /**
     * 将中文汉字替换为阿拉伯数字
     * @param input
     * @return
     */
    public static String replaceChineseNumber(String input) {
        String[] chineseNumbers = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "零"};
        String[] arabicNumbers = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "0"};

        for (int i = 0; i < chineseNumbers.length; i++) {
            input = input.replace(chineseNumbers[i], arabicNumbers[i]);
        }

        return input;
    }




}

🐛 注意

搜索阿拉伯数字的业务逻辑: 假设存储的用户姓名为"张三2", 用户输入的内容为3, 在后台的逻辑是需要将"张三2"用正则表达式替换为"张三二"然后在进行后续的拼音处理, 到最后的时候需要开发者手动的调用replaceChineseNumber(String input)方法将中文汉字替换为阿拉伯数字, 此时匹配上的数据是两条"张三2","张三二" 其中有一条数据是冗余的, 这块开发者需要对应自己的业务进行自行处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/888560.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视觉大模型综述

万字长文带你全面解读视觉大模型细数近期涌现的优秀视觉大模型工作https://mp.weixin.qq.com/s/jLQaguLejx9zXjZjaJWx-Q深入了解视觉语言模型 - 知乎人类学习本质上是多模态 (multi-modal) 的&#xff0c;因为联合利用多种感官有助于我们更好地理解和分析新信息。理所当然地&am…

uniapp 顶部头部样式

<u-navbartitle"商城":safeAreaInsetTop"true"><view slot"left"><image src"/static/logo.png" mode"" class"u-w-50 u-h-50"></image></view></u-navbar>

TIA博途_通过EXCEL快速给PLC程序段添加注释信息的方法示例

通过EXCEL快速给PLC程序段添加注释信息的方法示例 如下图所示,以OB1为例,正常情况下,我们可以在博途中直接输入各个程序段的注释信息, 但是如果程序段较多的话,逐个输入的话效率不高,此时可以参考下面这种通过EXCEL进行快速添加的方法。 如下图所示,选中某个OB或FC、FB块…

蓝牙资讯|2023年Q2蓝牙耳机市场报告发布,苹果依然占据第一

市场调查机构 Canalys 发布最新报告&#xff0c;2023 年第二季度全球个人智能音频设备&#xff08;包括 TWS, 无线头戴&#xff0c;无线颈挂&#xff09;下滑了 2%&#xff0c;出货量达到 9568 万部&#xff0c;基本追平去年同期。其中&#xff0c;真无线耳机&#xff08;TWS&a…

出现ffmpeg.dll丢失的修复方法分享,教你快速修复ffmpeg.dll文件

当你使用或尝试运行与FFmpeg相关的应用程序时&#xff0c;可能会遇到一个常见的问题&#xff0c;ffmpeg.dll文件丢失。这个动态链接库文件对于正常运行FFmpeg应用程序至关重要。在本文中&#xff0c;我们将详细探讨为什么会出现ffmpeg.dll丢失的情况&#xff0c;并提供一些修复…

互联网账号被封禁解决办法,以qq为例

百度搜索&#xff1a;互联网信息服务投诉平台 电脑端浏览器&#xff1a;打开 ts.isc.org.cn 推荐使用360极速浏览器 谷歌浏览器 提交完成后&#xff0c;将投诉码保存&#xff0c;可以在“查询评价”处用投诉码查询进度

什么是可视化编程?为什么它如此重要?

可视化编程&#xff0c;又叫可视化程序设计&#xff0c;一直以来就是备受讨论的“热门技术”。一方面&#xff0c;程序员抵触它&#xff0c;觉得它不如用代码开发。另一方面&#xff0c;对于产品经理等稍微懂点开发的业余人员&#xff0c;它确实能提供价值。所以&#xff0c;它…

第十课:Qt 字符编码和中文乱码相关问题

功能描述&#xff1a;最全的 Qt 字符编码相关知识以及中文乱码的原因与解决办法 一、字符编码种类 ASCII 码 美国人对信息交流的编码&#xff0c;包括 26 个字母&#xff08;大小写&#xff09;、数字和标点符号等&#xff0c;用一个字节&#xff08;8 位&#xff09;表示这些…

【Matter】基于Ubuntu 22.04搭建matter开发环境:chip-tool 配网之 matter-over-wifi

前言 主要是记录一下学习过程&#xff0c;梳理下思路&#xff0c;抛转~ 官方的开发环境&#xff0c;基于Linux版本&#xff0c;官方的环境是基于树莓派环境的&#xff0c;原理其实也比较明了&#xff0c;目的也比较明确&#xff0c;就是达到Linux 主机和wifi 路由在同一局域网…

四天两次盛会,西凤以品质和信仰深度诠释凤香价值

上周&#xff0c;西凤无疑是白酒圈的热点。 8月8日&#xff0c;在中国酿酒原料及品质安全研究院科技创新论坛暨西凤酒项目成果发布会上&#xff0c;《凤香型酒用高粱品种培育及抗逆高产技术集成示范》与《西凤酒原产地地质地理环境特征研究》研究成果被重磅发布。 8月11日&…

爬虫逆向实战(十二)--某交易所登录

一、数据接口分析 主页地址&#xff1a;某交易所 1、抓包 通过抓包可以发现登录是通过表单提交的 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块&#xff0c;可以发现有两个加密参数password和execution 请求头是否加密&#xff1f; 无响应是…

leader-line相关配置问题

安装完两个插件都是教去改config // configureWebpack: config > {// let path require(path)// config.module.rules.push({// test: path.resolve(__dirname, node_modules/leader-line/),// use: [// {// loader: skeleton-loader,// …

校企合作谋发展 合作共赢谱新篇|云畅科技与湖南民族职业学院签订校企合作协议

产业是经济发展的重要引擎&#xff0c;人才是产业发展的重要资源。为积极探索软件人才培育新路径&#xff0c;共商政产学研协同新机制&#xff0c;8月8日&#xff0c;云畅科技与湖南省民族职业学院教育技术学院软件技术专业签订校企合作协议。 会上&#xff0c;学院副校长王志平…

uniapp 企业微信侧边栏开发网页授权 注入企业权限 注入应用权限 获取userid(2)

1、网页授权&#xff0c;获取code 代码&#xff1a; oauthUrl() {const that thisuni.removeStorageSync(code)let REDIRECT_URI encodeURIComponent(window.location.href)let CORPID webConfig.appIdlet url https://open.weixin.qq.com/connect/oauth2/authorize?appi…

keil编译链接文件警告Pattern *.o(RAMCODE) only matches removed unused sections

问题&#xff1a; 从提示看是链接文件中&#xff0c;RAM中原先分配给代码存储的空间没有用上 解决办法&#xff1a;删除掉上图中红框里面代码&#xff1b;

途乐证券-宁德时代发力超充赛道,高压快充概念强势拉升,泰永长征涨停

高压快充概念17日盘中强势拉升&#xff0c;到发稿&#xff0c;泰永长征涨停&#xff0c;万祥科技涨超9%&#xff0c;英可瑞涨逾8%&#xff0c;迦南智能涨超4%。 消息面上&#xff0c;8月16日&#xff0c;宁德时代举行线下新品发布会&#xff0c;正式发布全球首款磷酸铁锂4C超充…

股票交易c接口包含哪些调用函数?

股票交易的C接口中可能包含多个调用函数&#xff0c;具体的调用函数取决于所使用的接口规范和交易所的要求。接下来看看下面是一些可能常见的股票交易C接口调用函数的示例&#xff1a; 1. 连接函数&#xff08;Connect&#xff09;&#xff1a;用于与交易所建立网络连接。 2.…

python3.73安装教程,python3.10安装教程

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python3.73安装教程&#xff0c;python3.10安装教程&#xff0c;现在让我们一起来看看吧&#xff01; Python目前已支持所有主流操作系统&#xff0c;在Linux,Unix,Mac系统上自带Python环境&#xff0c;一般默认装的是P…

等保案例 5

用户简介 四川省人民代表大会常务委员会&#xff0c;作为省人民代表大会地常设机关&#xff0c;随着政府部门信息化程度地提高&#xff0c;对信息系统地依赖程度越来越高&#xff0c;同时由于网络安全形势日益严峻、新型攻击层出不穷&#xff0c;单位信息化所面临地各种风险也…