在處理百萬量級條目(如文本名)的搜索時,每一次匹配的效率對提高總搜索時間至關重要。如果在每次檢查文件名與關鍵字時執(zhí)行復雜的操作,會對總時間產生累計影響,進而影響用戶體驗。本文將詳細分享之前 TDS 的文本搜索邏輯,希望能為大家提供一些參考。
一、拼音首字母轉換
考慮字符串“123四五六78abc”,我們的預期是匹配關鍵字[“sw”,“六”]時能分別命中子串[“四五”,“六”]。要獲取漢字的拼音首字母,需要先獲取字符的 Unicode 編碼,在不考慮多音字的情況下,可直接通過查表簡單實現。如果字符處于中文區(qū)間,可直接返回對應的首字母。
從零開始:C# 文件名搜索拼音首字母支持、搜索加速策略與關鍵詞高亮顯示
在處理百萬量級條目(如文本名)的搜索時,每一次匹配的效率對提高總搜索時間至關重要。如果在每次檢查文件名與關鍵字時執(zhí)行復雜的操作,會對總時間產生累計影響,進而影響用戶體驗。本文將詳細分享之前 TDS 的文本搜索邏輯,希望能為大家提供一些參考。
一、拼音首字母轉換
考慮字符串“123四五六78abc”
,我們的預期是匹配關鍵字[“sw”,“六”]
時能分別命中子串[“四五”,“六”]
。要獲取漢字的拼音首字母,需要先獲取字符的 Unicode 編碼,在不考慮多音字的情況下,可直接通過查表實現。如果字符處于中文區(qū)間,可直接返回對應的首字母。具體實現如下:
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”。因此,對源文本的搜索需要對原字符串和拼音串進行兩次匹配。目前嘗試了多個方法,發(fā)現String.Contains
的效率最高。StringComparison.OrdinalIgnoreCase
參數是一種快速的字符串比較方式,它忽略大小寫差異,直接比較字符的 Unicode 值,避免了額外的字符轉換操作。使用內置方法可以充分利用底層優(yōu)化,減少不必要的計算,從而提高整體性能。
二、存儲及索引輔助優(yōu)化
拼音首字母預先轉換比實時拼音轉換匹配速度要高效得多,除非存儲空間實在緊張。但多了一個字符串,也意味著多了一次字符串搜索,而字符串搜索是一個較占時間的操作。有沒有辦法繼續(xù)優(yōu)化呢?
(一)模式串
首先建立一個特定長度的char
數組作為模式串,以長度為 6 的[“a”,”b”,”c”,”1”,”2”,”3”]
為例,這個模式串包含了我們關心的元素。由于我們所有的中文全部映射到了字母的空間,因此這個模式串其實是有限的. 0-9
共10個符號加上26個字母
以及其他標點一共也不到64
個,所以我們可以很方便的用一個64位長度'long'類型存儲編碼.
(二)字符串編碼
以目標文件名“apple 1”為例,我們初始化一個對應模式串長度的二進制位,0_0_0_0_0_0
,逐字符掃描時,發(fā)現模式串中a
和1
字符命中,那么我們通過二進制位記錄將得到1_0_0_1_0_0
。
以目標文件名“xyz7890”為例,我們初始化一個對應模式串長度的二進制位,0_0_0_0_0_0
,逐字符掃描時,發(fā)現模式串均未命中,那么我們通過二進制位記錄將得到0_0_0_0_0_0
。
以目標文件名“231cbaa”為例,我們初始化一個對應模式串長度的二進制位,0_0_0_0_0_0
,逐字符掃描時,發(fā)現模式串所有字符命中,那么我們通過二進制位記錄將得到1_1_1_1_1_1
。
有限的二進制位可以方便地用int32
或int64
存儲,已經相當夠用了。這種二進制編碼的方式不僅節(jié)省了存儲空間,還大大提高了搜索效率。布爾運算(如或運算)的速度遠超字符串操作,因為它們直接在內存的位級別上進行操作,而字符串操作則需要逐字符比較和處理。
(三)關鍵詞初篩
以目標文件名“apple 1”為例,此時的二進制位為1_0_0_1_0_0 = 36
(int
值表示)
- 我們搜索關鍵詞“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;
int index_keyTxt_2;
int index_keyTxt_3;
int index_keyTxt_final = index_keyTxt_2 | index_keyTxt_3;
if (index_originTxt | index_keyTxt_final != index_keyTxt)
{
return "索引初篩失敗";
}
else
{
}
int
或long
的或運算非常輕量,速度遠超string.Contains
,且關鍵詞越多,文件名越長,過濾效果越好。這種初篩機制可以快速排除大量不匹配的文件名,從而顯著減少后續(xù)精確匹配的計算量,提高整體搜索效率。
底層技術細節(jié):
- 初篩機制: 使用位運算進行初篩,可以快速排除大量不匹配的文件名,減少后續(xù)精確匹配的計算量。
- 性能優(yōu)勢: 位運算的速度極快,適合大規(guī)模數據的快速篩選。
三、關鍵詞高亮(v1.1.7新增)
在 Avalonia 中,文件名的顯示通過 TextBlock 控件實現,其 Inlines 屬性綁定至一個自定義的高亮轉換器.
<TextBlock Inlines="{Binding FileName, Converter={StaticResource HighlightConverter}}" />
該轉換器類需實現 IValueConverter 接口,由 Avalonia 依賴注入容器自動實例化。其核心方法是 Convert,負責將原始文本按匹配的關鍵詞切分,并構造高亮顯示的 InlineCollection。具體實現如下:
- 初始化一個 InlineCollection 對象用于存放文本段;
- 遍歷預處理后的關鍵詞匹配結果(通常需先按起始位置排序,并合并相鄰或重疊區(qū)間);
- 對非匹配區(qū)域,添加普通 Run 對象顯示文本;
- 對匹配區(qū)域,創(chuàng)建 Run 對象并設置高亮畫刷(如 Brushes.Yellow)以改變前景色;
- 需注意多個關鍵詞可能引起的區(qū)間重疊與重復問題,需通過算法(如區(qū)間合并)確保每個字符只處理一次。
var inlines = new InlineCollection();
foreach (var result in results)
{
var textSegment = nameOrigin.Substring(result.Start, result.Length);
var run = new Run(textSegment);
if (result.IsMatch)
{
run.Foreground = highlightBrush;
}
inlines.Add(run);
}
return inlines;
需要注意的是,多個關鍵詞高亮后,需要對substring
的區(qū)域進行重排以及去重/結合。這種設計不僅保證了高亮顯示的準確性,還避免了重復處理同一段文本,從而提高了渲染效率。通過這種方式,用戶可以清晰地看到搜索關鍵詞在文本中的位置,提升了用戶體驗。
所有的Converter都是在虛擬模式下按需執(zhí)行的,可以較好滿足我們的性能需求.
四、最后
對于TDS搜索軟件的其他信息信息可見此公眾號的文章 https://mp.weixin.qq.com/s/inD-brKhii57UJnCYLgxKQ
目前關于TDS搜索的版本已經更新到了1.1.7, 優(yōu)化了很多細節(jié),增加了高亮,磁盤緩存索引,更多選項等一些功能. 歡迎大家拿走,分享,使用.

如果你對這款工具有任何建議或想法,歡迎隨時交流!項目已在 GitHub 完全開源.
如果你覺得有用,歡迎點個 Star ??支持一下! https://github.com/LdotJdot/TDS
?轉自https://www.cnblogs.com/luojin765/p/19100717
該文章在 2025/10/10 10:24:14 編輯過