[WPF] 脱机环境实现支持拼音模糊搜索的AutoCompleteBox

AutoCompleteBox是一个常见的提高输入效率的组件,很多WPF的第三方控件库都提供了这个组件,但基本都是字符串的子串匹配,不支持拼音模糊匹配,例如无法通过输入ldhliudehua匹配到刘德华。要实现拼音模糊搜索功能,通常会采用分词、数据库等技术对待匹配数据集进行预处理。某些场景受制于条件限制,无法对数据进行预处理,本文将介绍在这种情况下如何实现支持拼音模糊搜索的AutoCompleteBox,先来看下实现效果。

主要思路

WPF中并没有AutoCompleteBox控件,我们可以使用TextBox输入搜索内容,用Popup+ListBox显示匹配到的提示内容。拼音模糊匹配汉字则采用字符串匹配的方式来解决,也就是搜索字符串和待匹配数据集的内容全部转换为拼音字符串,然后进行子串匹配。这里有三个问题需要解决。

  1. 汉字转换为拼音。
  2. 拼音如何匹配。 例如ldhlidhldhualiudehuadhuahua等都能匹配到刘德华
  3. 匹配后的内容高亮显示。 当输入dhua匹配到刘德华时需要把德华两个字高亮。

汉字转换拼音

微软为了开发者实现国际化语言的互转,提供了Microsoft Visual Studio International Pack,这个扩展包里面有中文、日文、韩文、英语等各国语言包,并提供方法实现互转、获取拼音、获取字数、甚至获取笔画数等等。下载Microsoft Visual Studio International Pack 1.0 SR1安装后,在安装目录中找到ChnCharInfo.dll,然后在项目中添加引用。
ChnCharInfo.dll获取汉字的拼音时只能传入单个字符,因此只能把汉字字符串拆分成一个个字符处理,由于汉字存在多音字情况以及缺少语义信息,获取的拼音组合可能是多个,例如输入长江,返回的是changjiangzhangjiang。汉字转拼音的方法如下:

c# 复制代码
/// <summary>
/// 获取汉字拼音
/// </summary>
/// <param name="str">待处理包含汉字的字符串</param>
/// <param name="split">拼音分隔符</param>
/// <returns></returns>
public static List<string> GetChinesePhoneticize(string str, string split = "")
{
    List<string> result = new List<string>();
    char[] chs = str.ToCharArray();
    Dictionary<int, List<string>> totalPhoneticizes = new Dictionary<int, List<string>>();
    for (int i = 0; i < chs.Length; i++)
    {
        var phoneticizes = new List<string>();
        if (ChineseChar.IsValidChar(chs[i]))
        {
            ChineseChar cc = new ChineseChar(chs[i]);
            phoneticizes.AddRange(cc.Pinyins.Where(r => !string.IsNullOrWhiteSpace(r)).ToList<string>().ConvertAll(p => Regex.Replace(p, @"\d", "").ToLower()).Distinct());
        }
        else
        {
            phoneticizes.Add(chs[i].ToString());
        }
        if (phoneticizes.Any())
            totalPhoneticizes[i] = phoneticizes;
    }

    foreach (var phoneticizes in totalPhoneticizes)
    {
        var items = phoneticizes.Value;
        if (result.Count <= 0)
        {
            result = items;
        }
        else
        {
            var newtotalPhoneticizes = new List<string>();
            foreach (var totalPingYin in result)
            {
                newtotalPhoneticizes.AddRange(items.Select(item => totalPingYin + split + item));
            }
            newtotalPhoneticizes = newtotalPhoneticizes.Distinct().ToList();
            result = newtotalPhoneticizes;
        }
    }
    return result;
}

拼音匹配算法

汉字转换后的拼音字符串有多组,只要搜索字符串转换的拼音组合有一组与待匹配字符串转换的拼音组合中匹配,则认为匹配成功,为了后续高亮显示,需要记录下匹配的起始位置以及匹配的子串长度。代码如下:

c# 复制代码
public static bool fuzzyMatchChar(string character, string input, out int matchStart, out int matchCount)
{
    List<string> regexs = GetChinesePhoneticize(input);
    List<string> targetStr = GetChinesePhoneticize(character, " ");
    matchStart = -1;
    matchCount = 0;
    foreach (string regex in regexs)
    {
        foreach (string target in targetStr)
        {
            if (PhoneticizeMatch(regex, target.Split(' '), out matchStart, out matchCount))
                return true;
        }
    }
    return false;
}

这里的PhoneticizeMatch方法是拼音匹配算法的核心,是在【算法】拼音匹配算法这篇博文中算法的基础上稍作修改,详细的思路及图解可阅读这篇博文。

高亮匹配的子串

WPF中可以通过TextEffectPositionStartPositionCount以及Foreground属性设置字符串中需要高亮内容的起始位置、长度以及高亮颜色。前面拼音匹配算法中获取了匹配成功子串的起始位置和长度,也正是为此做准备。之前在WPF使用TextBlock实现查找结果高亮显示一文中有详细介绍思路和代码,此处不再赘述。

小结

本文介绍了在不依赖数据库及分词的情况下如何实现拼音模糊搜索并在目标字符串中高亮显示,方法中也存在诸多不足需要完善的地方。

  1. 匹配策略存在误匹配。例如输入,可以匹配出拼音为shi的所有汉字。
  2. 匹配算法效率不够高。测试过程中,待匹配数据集中模拟了500条数据,匹配耗时大概在400~500ms左右。

代码示例

ChinesePhoneticizeFuzzyMatch

相关推荐
晚安苏州10 小时前
WPF DataTemplate 数据模板
wpf
甜甜不吃芥末1 天前
WPF依赖属性详解
wpf
Hat_man_1 天前
WPF制作图片闪烁的自定义控件
wpf
晚安苏州3 天前
WPF Binding 绑定
wpf·wpf binding·wpf 绑定
wangnaisheng3 天前
【WPF】RenderTargetBitmap的使用
wpf
dotent·3 天前
WPF 完美解决改变指示灯的颜色
wpf
orangapple5 天前
WPF 用Vlc.DotNet.Wpf实现视频播放、停止、暂停功能
wpf·音视频
ysdysyn5 天前
wpf mvvm 数据绑定数据(按钮文字表头都可以),根据长度进行换行,并把换行的文字居中
c#·wpf·mvvm
orangapple5 天前
WPF 使用LibVLCSharp.WPF实现视频播放、停止、暂停功能
wpf
晚安苏州5 天前
WPF ControlTemplate 控件模板
wpf