我做 AHK 工具时,很喜欢给脚本加搜索框、命令面板、候选列表。用户输入几个字,就能从文件名、菜单项、客户名、模板名里找到最接近的一项。
普通 InStr() 很快,但它只能做包含匹配。输入有错别字、顺序不一样、中文简繁不同、只输拼音首字母时,就不够用了。
FuzzyAHK 是一个给 AHK v1 用的模糊搜索和相似度匹配库,思路参考比较成熟 RapidFuzz、difflib 和 fzf库。它不是完整移植,而是偏日常自动化的轻量实用版。
最小上手示例
#Requires AutoHotkey v1.1
#Include <FuzzyAHK>
choices := ["项目计划书", "项目实施计划", "客户回款提醒"]
best := Fuzzy_ExtractOne("项目计", choices, "Chinese")
MsgBox, % best.choice . "`nscore=" . best.score ; 输出:项目计划书 score=100.000000
这个例子适合搜索候选项。用户输入不完整,库会返回最接近的结果和分数。
我日常更推荐 RatioEX
如果你不想一开始就纠结用哪种算法,可以先用 Fuzzy_RatioEX()。它会根据中文、英文、混合文本和长度关系,自动选择更合适的比较策略。
smart := Fuzzy_RatioEX("项目计划", "項目計劃書", true, true)
MsgBox, % smart.score . " / " . smart.mode . " / " . smart.strategy ; 输出:100.00 / chinese / zh-balanced
它适合标题比较、文件名比较、中英混合名称比较,也适合调试,因为返回结果里能看到模式和策略。
中文和拼音首字母
中文搜索最麻烦的地方,是用户经常只输入几个字,或者输入拼音首字母。这个库里做了轻量中文增强,包括 n-gram、常见简繁归一化和一小批常用字拼音首字母映射。
choices := ["项目计划书", "项目实施计划", "客户回款提醒"]
cache := Fuzzy_BuildCache(choices, true, true)
best := Fuzzy_ExtractOneCached("xmjh", cache, "Chinese", 20)
MsgBox, % best.choice . "`nscore=" . best.score ; 输出:项目计划书 score=100.000000
如果候选列表固定,比如命令面板、模板列表、客户列表,建议先建缓存,再反复搜索。这样用户边输入边过滤时会更顺手。
fzf 风格过滤
如果你的场景更像文件名快速定位,可以用 Fuzzy_Filter() 和 Fuzzy_Highlight()。它能按子序列匹配,并标出命中的字符。
matches := Fuzzy_Filter("dtpl", choices, 5, 0)
for index, match in matches {
text := Fuzzy_Highlight(match.choice, match.positions, "【", "】")
MsgBox, % text
}
适合哪些脚本
命令面板、文件名搜索、菜单项搜索、配置项搜索、字段名自动匹配、OCR 后轻量纠错、网页抓取后的候选归一化,都可以考虑用它。
此库的能力与边界
它是 AHK v1 轻量实现,不是 C++ 级 RapidFuzz。几百到几千个短候选可以直接用;上万候选建议先用关键词、长度、首字母、目录范围做预过滤。
中文没有完整分词模型,拼音首字母也不是全量拼音库。它适合日常工具和自动化脚本,不适合拿来做搜索引擎。
如果你想给自己的 AHK 工具加一个“输入几个字就能找到东西”的体验,这个库会很实用。
FuzzyAHK.ahk【文章底部有完整示例】
demo代码片段展示:
#NoEnv
#SingleInstance Force
#Include <FuzzyAHK>
; 使用说明:
; 直接双击 demo.ahk,会弹出中文结果。
; 日常使用优先看第一组 Fuzzy_RatioEX,它会自动根据中文/英文/混合文本和长度选择比较策略。
; 候选列表:混合中文、英文、文件名和项目名称,方便观察不同匹配场景。
choices := []
choices.Push("项目计划书")
choices.Push("项目实施计划")
choices.Push("项目总结报告")
choices.Push("客户回款提醒")
choices.Push("客户回款通知")
choices.Push("发票提醒")
choices.Push("报价单模板")
choices.Push("采购合同审批")
choices.Push("北京海淀中关村")
choices.Push("北京市海淀区中关村软件园")
choices.Push("customer invoice template.docx")
choices.Push("DocxTplAHK demo.ahk")
summary := ""
Demo_Log("FuzzyAHK 模糊搜索 / 相似度匹配演示")
Demo_Log("")
; 第一组:智能比较函数 RatioEX。
; 它会自动判断中文、英文、混合文本,并根据短文本/长文本/包含关系选择更合适的算法。
Demo_Log("一、智能比较函数 RatioEX(推荐日常优先使用)")
Demo_RatioEX("项目计划", "項目計劃書")
Demo_RatioEX("invoice", "customer invoice template.docx")
Demo_RatioEX("xmjh", "项目计划书")
Demo_RatioEX("北京海淀中关村", "北京市海淀区中关村软件园")
Demo_RatioEX("DocxTpl demo", "DocxTplAHK demo.ahk")
Demo_Log("")
; 第二组:RapidFuzz 风格相似度。
; 这些函数更像底层工具:知道场景时可精确选择,不确定时用 RatioEX 更省心。
Demo_Log("二、RapidFuzz 风格相似度")
Demo_Log("Ratio(项目计划, 项目方案) = " . Fuzzy_Ratio("项目计划", "项目方案"))
Demo_Log("PartialRatio(回款提醒, 客户回款提醒通知) = " . Fuzzy_PartialRatio("回款提醒", "客户回款提醒通知"))
Demo_Log("SequenceRatio(北京海淀中关村, 北京市海淀区中关村软件园) = " . Fuzzy_SequenceRatio("北京海淀中关村", "北京市海淀区中关村软件园"))
Demo_Log("TokenSortRatio(模板 客户 报价单, 客户 报价单 模板) = " . Fuzzy_TokenSortRatio("模板 客户 报价单", "客户 报价单 模板"))
Demo_Log("TokenSetRatio(报价单 模板, 客户 报价单 模板) = " . Fuzzy_TokenSetRatio("报价单 模板", "客户 报价单 模板"))
Demo_Log("WRatio(项目计划, 项目计划书) = " . Fuzzy_WRatio("项目计划", "项目计划书"))
Demo_Log("")
; 第三组:候选列表 Top N。
; 适合搜索框、命令面板、配置项查找、客户/文件/模板列表快速匹配。
Demo_Log("三、候选列表 Top N 搜索")
matches := Fuzzy_Extract("回款提醒", choices, 5, "RatioEX", 20)
for index, match in matches
Demo_Log(index . ". 分数=" . match.score . " -> " . match.choice)
Demo_Log("")
; 第四组:中文增强 + 缓存搜索。
; 候选列表只预处理一次,适合用户边输入边搜索的场景。
Demo_Log("四、中文增强 + 缓存搜索")
Demo_Log("ChineseRatio(项目计划, 項目計劃書) = " . Fuzzy_ChineseRatio("项目计划", "項目計劃書"))
Demo_Log("PinyinInitials(项目计划) = " . Fuzzy_PinyinInitials("项目计划"))
Demo_Log("PinyinInitialRatio(xmjh, 项目计划书) = " . Fuzzy_PinyinInitialRatio("xmjh", "项目计划书"))
cache := Fuzzy_BuildCache(choices, true, true)
cachedMatches := Fuzzy_ExtractCached("xmjh", cache, 5, "Chinese", 20)
for index, match in cachedMatches
Demo_Log(index . ". 缓存搜索 分数=" . match.score . " -> " . match.choice)
Demo_Log("")
; 第五组:fzf 风格子序列搜索。
; 适合文件名、脚本名、菜单项快速定位,并能标出命中的字符。
Demo_Log("五、fzf 风格文件名 / 菜单快速定位")
fzfMatches := Fuzzy_Filter("dtpl", choices, 5, 0)
for index, match in fzfMatches {
highlighted := Fuzzy_Highlight(match.choice, match.positions, "【", "】")
Demo_Log(index . ". 分数=" . match.score . " -> " . highlighted)
}
Demo_Log("")
; 第六组:difflib 风格 get_close_matches。
; 适合保留 Python difflib 的调用习惯,做少量候选的最接近匹配。
Demo_Log("六、difflib 风格 get_close_matches")
close := Fuzzy_GetCloseMatches("项目计", choices, 3, 0.35)
for index, item in close
Demo_Log("匹配 " . index . ":" . item)
Demo_Log("")
MsgBox, 64, FuzzyAHK 示例完成, %summary%
ExitApp, 0
Demo_RatioEX(a, b) {
detail := Fuzzy_RatioEX(a, b, true, true)
Demo_Log(a . " vs " . b . " -> 分数=" . detail.score . ",模式=" . detail.mode . ",策略=" . detail.strategy . ",长度比=" . detail.lengthRatio)
}
Demo_Log(text) {
global summary
line := text . "`n"
summary .= line
FileAppend, %line%, *
}

评论(0)