从零开始:C# 拼音首字母搜索、字符串编码、关键词高亮的原理即实现考虑

news/2025/9/19 13:26:49/文章来源:https://www.cnblogs.com/luojin765/p/19100717

从零开始:C# 拼音首字母搜索、字符串编码、关键词高亮的原理即实现考虑

在处理百万量级条目(如文本名)的搜索时,每一次匹配的效率对提高总搜索时间至关重要。如果在每次检查文件名与关键字时执行复杂的操作,会对总时间产生累计影响,进而影响用户体验。本文将详细分享之前 TDS 的文本搜索逻辑,希望能为大家提供一些参考。

一、拼音首字母转换

考虑字符串“123四五六78abc”,我们的预期是匹配关键字[“sw”,“六”]时能分别命中子串[“四五”,“六”]。要获取汉字的拼音首字母,需要先获取字符的 Unicode 编码,在不考虑多音字的情况下,可直接通过查表简单实现。如果字符处于中文区间,可直接返回对应的首字母。

从零开始:C# 文件名搜索拼音首字母支持、搜索加速策略与关键词高亮显示

在处理百万量级条目(如文本名)的搜索时,每一次匹配的效率对提高总搜索时间至关重要。如果在每次检查文件名与关键字时执行复杂的操作,会对总时间产生累计影响,进而影响用户体验。本文将详细分享之前 TDS 的文本搜索逻辑,希望能为大家提供一些参考。

一、拼音首字母转换

考虑字符串“123四五六78abc”,我们的预期是匹配关键字[“sw”,“六”]时能分别命中子串[“四五”,“六”]。要获取汉字的拼音首字母,需要先获取字符的 Unicode 编码,在不考虑多音字的情况下,可直接通过查表实现。如果字符处于中文区间,可直接返回对应的首字母。具体实现如下:

return iCnChar switch
{>= 45217 and <= 45252 => 'A',>= 45253 and <= 45760 => 'B',>= 45761 and <= 46317 => 'C',>= 46318 and <= 46825 => 'D',>= 46826 and <= 47009 => 'E',>= 47010 and <= 47296 => 'F',>= 47297 and <= 47613 => 'G',>= 47614 and <= 48118 => 'H',>= 48119 and <= 49061 => 'J',>= 49062 and <= 49323 => 'K',>= 49324 and <= 49895 => 'L',>= 49896 and <= 50370 => 'M',>= 50371 and <= 50613 => 'N',>= 50614 and <= 50621 => 'O',>= 50622 and <= 50905 => 'P',>= 50906 and <= 51386 => 'Q',>= 51387 and <= 51445 => 'R',>= 51446 and <= 52217 => 'S',>= 52218 and <= 52697 => 'T',>= 52698 and <= 52979 => 'W',>= 52980 and <= 53688 => 'X',>= 53689 and <= 54480 => 'Y',>= 54481 and <= 65289 => 'Z',_ => throw new ArgumentOutOfRangeException(nameof(iCnChar), iCnChar, null)
};

经过上述转换,字符串“123四五六78abc”将得到一个新的字符串“123swl78abc”。因此,对源文本的搜索需要对原字符串和拼音串进行两次匹配。目前尝试了多个方法,发现String.Contains的效率最高。StringComparison.OrdinalIgnoreCase 参数是一种快速的字符串比较方式,它忽略大小写差异,直接比较字符的 Unicode 值,避免了额外的字符转换操作。使用内置方法可以充分利用底层优化,减少不必要的计算,从而提高整体性能。

二、存储及索引辅助优化

拼音首字母预先转换比实时拼音转换匹配速度要高效得多,除非存储空间实在紧张。但多了一个字符串,也意味着多了一次字符串搜索,而字符串搜索是一个较占时间的操作。有没有办法继续优化呢?

(一)模式串

首先建立一个特定长度的char数组作为模式串,以长度为 6 的[“a”,”b”,”c”,”1”,”2”,”3”]为例,这个模式串包含了我们关心的元素。由于我们所有的中文全部映射到了字母的空间,因此这个模式串其实是有限的. 0-9共10个符号加上26个字母以及其他标点一共也不到64个,所以我们可以很方便的用一个64位长度'long'类型存储编码.

(二)字符串编码

以目标文件名“apple 1”为例,我们初始化一个对应模式串长度的二进制位,0_0_0_0_0_0,逐字符扫描时,发现模式串中a1字符命中,那么我们通过二进制位记录将得到1_0_0_1_0_0

以目标文件名“xyz7890”为例,我们初始化一个对应模式串长度的二进制位,0_0_0_0_0_0,逐字符扫描时,发现模式串均未命中,那么我们通过二进制位记录将得到0_0_0_0_0_0

以目标文件名“231cbaa”为例,我们初始化一个对应模式串长度的二进制位,0_0_0_0_0_0,逐字符扫描时,发现模式串所有字符命中,那么我们通过二进制位记录将得到1_1_1_1_1_1

有限的二进制位可以方便地用int32int64存储,已经相当够用了。这种二进制编码的方式不仅节省了存储空间,还大大提高了搜索效率。布尔运算(如或运算)的速度远超字符串操作,因为它们直接在内存的位级别上进行操作,而字符串操作则需要逐字符比较和处理。

(三)关键词初筛

以目标文件名“apple 1”为例,此时的二进制位为1_0_0_1_0_0 = 36int值表示)

  • 我们搜索关键词“apel”,与模式串匹配得到关键词的二进制位1_0_0_1_0_0 = 36(十进制)
  • 我们搜索关键词“bpel”,与模式串匹配得到关键词的二进制位0_1_0_1_0_0 = 20(十进制)

对关键词与目标文件名做或运算:

int index_originTxt; // 假设已初始化
int index_keyTxt;    // 假设已初始化
if (index_originTxt | index_keyTxt != index_keyTxt) return "索引初筛失败";

如果或运算后的值不等于原值,则表示关键词与目标文件名在模式串中存在不包含的字符。

对于多个关键词:

int index_originTxt;  //文件名
int index_keyTxt_1;  // 搜索关键字1,假设已初始化
int index_keyTxt_2;  // 搜索关键字2,假设已初始化
int index_keyTxt_3;  // 搜索关键字3,假设已初始化
int index_keyTxt_final = index_keyTxt_2 | index_keyTxt_3;if (index_originTxt | index_keyTxt_final != index_keyTxt)
{return "索引初筛失败";
}
else
{// ...正常文本搜索
}

intlong的或运算非常轻量,速度远超string.Contains,且关键词越多,文件名越长,过滤效果越好。这种初筛机制可以快速排除大量不匹配的文件名,从而显著减少后续精确匹配的计算量,提高整体搜索效率。

底层技术细节:

  • 初筛机制: 使用位运算进行初筛,可以快速排除大量不匹配的文件名,减少后续精确匹配的计算量。
  • 性能优势: 位运算的速度极快,适合大规模数据的快速筛选。

三、关键词高亮(v1.1.7新增)

在 Avalonia 中,文件名的显示通过 TextBlock 控件实现,其 Inlines 属性绑定至一个自定义的高亮转换器.

<TextBlock Inlines="{Binding FileName, Converter={StaticResource HighlightConverter}}" />  

该转换器类需实现 IValueConverter 接口,由 Avalonia 依赖注入容器自动实例化。其核心方法是 Convert,负责将原始文本按匹配的关键词切分,并构造高亮显示的 InlineCollection。具体实现如下:

  • 初始化一个 InlineCollection 对象用于存放文本段;
  • 遍历预处理后的关键词匹配结果(通常需先按起始位置排序,并合并相邻或重叠区间);
  • 对非匹配区域,添加普通 Run 对象显示文本;
  • 对匹配区域,创建 Run 对象并设置高亮画刷(如 Brushes.Yellow)以改变前景色;
  • 需注意多个关键词可能引起的区间重叠与重复问题,需通过算法(如区间合并)确保每个字符只处理一次。
// 创建行内元素集合,用于存储文本元素(Run)
var inlines = new InlineCollection();// 遍历所有排序整合后的搜索结果
foreach (var result in results)
{// 从原始文本中提取当前片段的子字符串// result.Start: 片段起始位置// result.Length: 片段长度var textSegment = nameOrigin.Substring(result.Start, result.Length);// 创建文本元素(Run),用于显示文本片段var run = new Run(textSegment);// 如果当前片段是匹配项,则应用高亮样式// result.IsMatch: 标识该片段是否为搜索匹配项if (result.IsMatch) {// 设置前景色为高亮画刷,突出显示匹配文本run.Foreground = highlightBrush;// 可选:添加其他高亮样式,如加粗、背景色等// run.FontWeight = FontWeight.Bold;// run.Background = Brushes.Yellow;}// 将文本运行添加到行内集合中inlines.Add(run);
}// 返回构建完成的行内元素集合
// 该集合可在Avalonia TextBlock等控件中直接使用,显示带有高亮效果的文本
return inlines;

需要注意的是,多个关键词高亮后,需要对substring的区域进行重排以及去重/结合。这种设计不仅保证了高亮显示的准确性,还避免了重复处理同一段文本,从而提高了渲染效率。通过这种方式,用户可以清晰地看到搜索关键词在文本中的位置,提升了用户体验。

所有的Converter都是在虚拟模式下按需执行的,可以较好满足我们的性能需求.

四、最后

对于TDS搜索软件的其他信息信息可见此公众号的文章 https://mp.weixin.qq.com/s/inD-brKhii57UJnCYLgxKQ

目前关于TDS搜索的版本已经更新到了1.1.7, 优化了很多细节,增加了高亮,磁盘缓存索引,更多选项等一些功能. 欢迎大家拿走,分享,使用.

ui

如果你对这款工具有任何建议或想法,欢迎随时交流!项目已在 GitHub 完全开源.
如果你觉得有用,欢迎点个 Star ⭐️支持一下! https://github.com/LdotJdot/TDS

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

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

相关文章

深入解析:多模态大模型3:TAViS

深入解析:多模态大模型3:TAViSpre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&qu…

基于STM32F103C8T6与DS18B20的温度测量系统

基于STM32F103C8T6(Blue Pill开发板)和DS18B20温度传感器的温度测量系统实现 系统概述 这个系统使用STM32F103C8T6单片机读取DS18B20数字温度传感器的数据,并通过串口将温度值发送到电脑或其他设备显示。DS18B20是一…

afx100.dll afrvidwindowmanager.dll afresu.dll afrcomputeserver.dll afckernel.dll aexplore_view. - 详解

afx100.dll afrvidwindowmanager.dll afresu.dll afrcomputeserver.dll afckernel.dll aexplore_view. - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; displ…

UE5 增量 Cook

UE5 增量 Cook UE 版本 5.5.4关键-iterativecooking添加了之后的 log 如下这部分 log 比较重要 cs 中即 -iterativecooking 等同于 -iterate 数据结构 1. FAssetPackageData简单来说根据 GetPackageSavedHash() 判断包…

Oxygen Forensic Detective 18.0 发布,新增功能简介

Oxygen Forensic Detective 18.0 发布,新增功能简介Oxygen Forensic Detective 18 Windows Multilingual - 领先的一体化数字取证软件 digital forensic software 请访问原文链接:https://sysin.org/blog/oxygen-for…

Windows如何美化cmd窗口

一、下载Windows Terminal 方法一:Microsoft store搜索 方法二:GitHub 1、GitHub搜索Terminal,选择下图所示 2、下载双击安装 3、安装完毕后,启动效果如下:二、替换默认的cmd窗口 打开原来的cmd窗口,右键属性,选…

MX Round 7 解题报告

T1 其实条件就是 \(a_i-i \le a_j-j,i-b_i \le j-b_j\),因此我们记 \(x_i=a_i-i,y_i=i-b_i\)。 显然,同一个 \(x_i\) 的点都在一个连通块内,因为它们都可以被 \(y_i\) 最大的点连起来;依照这个思路,我们记 \(mx_i…

【数据库强大的系统Trip 第1站】总概

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【完整源码+数据集+部署教程】机器人足球场景分割环境: yolov8-seg-C2f-DCNV2

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

RenderPass与 SubPass 理论

一、 1.1 基础概念说明 RenderPass: 每次提交Draw call命令之后,到输出一张或多张图片结果,这个过程称为RenderPass,在Vulkan中可以创建其对象,主要描述了Pass当中的输入输出相关信息。 SubPass:存在很多渲染方式,…

信号处理相关

一、信噪比(SNR)计算​为什么要选”选波段都选择其附近的一个平坦区来计算其SNR“?不能直接计算每一个吸收波段的SNR吗? 这是一个非常棒的问题,直指光谱处理中的一个关键细节。您提出的“直接计算每一个吸收波段的…

k8s系列--组件说明

服务分类: 有状态服务:数据库DBMS,就是抽调到其他服务,再回来会有数据缺失的服务,数据需要持久化 无状态服务:LVS APACHE,docker更适用于无状态服务 pod控制器: 服务发现: 存储: 调度器: 准入控制(安全策略…

实用指南:售价3499美元,英伟达Jetson Thor实现机器人与物理世界的实时智能交互

实用指南:售价3499美元,英伟达Jetson Thor实现机器人与物理世界的实时智能交互2025-09-19 13:03 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overfl…

详细介绍:AWS WAF 防护敏感配置文件泄露完整指南

详细介绍:AWS WAF 防护敏感配置文件泄露完整指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

详细介绍:html让该元素忽略所有鼠标事件,例如点击、悬停、拖拽都无效。

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

梗棋

\(\mathtt{Master\_Xie}\) 发明了一个叫做「梗棋」的棋类游戏。

逻辑回归 vs 支持向量机 vs 随机森林:哪个更适合小数据集? - 指南

逻辑回归 vs 支持向量机 vs 随机森林:哪个更适合小数据集? - 指南2025-09-19 12:52 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !…

javax.imageio.IIOException: Cant create output stream! 解决方法 验证码出不来

javax.imageio.IIOException: Cant create output stream! 解决方法 验证码出不来百度其它帖子说设置temp权限,但我这边还是不行。 通过下面关闭缓存可以解决,如果不关就会向tomcat/temp/ 目录下写东西但好像写不了就…

【转载】在Vue3中引用Vue2组件

同事以为要重写,我用 8 行代码让 Vue3 跑起已打包的 Vue2 组件 - 前后端小能手的文章 - 知乎 https://zhuanlan.zhihu.com/p/1951345700597138301

JUC 学习笔记

并发与并行 并发:多个线程轮流使用CPU执行任务,将CPU的时间分割成合适大小的时间片,每一个线程拿到时间片之后就会利用CPU资源执行任务,当时间到了之后就会把CPU资源释放,并且保存线程的上下文,比如程序计数器,…