C++如何实现字符串的模糊搜索?(基于通配符或正则)

2次阅读

std::Regex模糊匹配需确认编译器版本(msvc 19.28+/gcc 10+/clang 10+),避免老版本静默降级;注意字符串字面量反斜杠双写、ecmascript模式限制、全匹配/子串匹配区别、性能优化(缓存regex)、通配符场景优先手写双指针算法,路径匹配用generic_String(),模式固定选ctre,动态则考虑pcre2。

C++如何实现字符串的模糊搜索?(基于通配符或正则)

std::regex 做正则模糊匹配,但得先确认编译器支持

MSVC 19.28+、GCC 10+、Clang 10+ 才完整支持 std::regex 的 ECMAScript 模式;老版本(如 GCC 9)会静默降级或抛 std::regex_error。别等运行时报错才怀疑——编译期加个测试:

static_assert(std::regex_constants::ECMAScript != 0, "regex not supported");

常见错误现象:std::regex_match("abc", std::regex("a.*c")) 返回 false,其实是忘了字符串字面量里反斜杠要双写:"a.*c" 没问题,但 "a.*c" 才匹配字面点号。正则里 . 默认匹配任意字符,不是通配符意义上的“任意长度任意字符”。

  • 想匹配文件名模式(如 "*.log"),别直接塞进 std::regex —— 先转义:std::string pattern = std::regex_replace(user_input, std::regex("*"), ".*");
  • std::regex_search 查子串,std::regex_match 要全匹配,别混用
  • 性能敏感场景慎用:每次构造 std::regex 对象有开销,重复使用请缓存 std::regex 实例

手写通配符匹配(*?)比正则更轻量也更可控

如果只需要 *(匹配任意长度)和 ?(匹配单字符),自己写个双指针算法,50 行内搞定,零依赖、无异常、可 constexpr(c++20)。它比 std::regex 快一个数量级,尤其短字符串高频匹配时。

关键逻辑:遇到 * 就记下当前通配符位置和字符串位置,往后贪心匹配;不匹配就回退,让 * 多吃一个字符。容易踩的坑:

立即学习C++免费学习笔记(深入)”;

  • "a*b" 匹配 "ab" ✅,但 "a**b""a*b*c" 要归一化,否则可能溢出或死循环
  • 空字符串 "" 只能被 "*""" 匹配,"?" 不行
  • 区分大小写:默认区分,如需忽略,预处理时统一转小写,别在匹配循环里反复调 std::tolower

跨平台路径匹配别碰 std::Filesystem::path::filename() 直接比较

windows 路径分隔符是 ''linux'/',而 std::filesystem::path 内部会 normalize,但 filename() 返回的 string_type 在不同平台类型不同(Windows 是 wstring)。直接拿 filename().string() 去喂 std::regex,在 MinGW 或某些 MSVC 配置下会触发编码转换失败。

正确做法:先转为通用格式再匹配:

auto name = p.filename().generic_string(); // 返回 std::string,含 '/' 分隔符 if (wildcard_match(name, "*.cpp")) { ... }
  • 不要用 p.stem() + p.extension() 拼接判断,stem() 会砍掉最后一个点后的部分,"config.test.json"stem()"config.test",不是你想要的通配意图
  • 路径中含 Unicode(如中文文件名)时,generic_string() 在 UTF-8 环境下安全;Windows 上若用窄字符编译,确保控制台/终端也设为 UTF-8(SetConsoleOutputCP(65001)

第三方库选 ctre 还是 PCRE2?看构建约束

ctre(Compile-Time Regular Expressions)把正则编译到模板实例里,匹配极快,但要求模式必须是字面量("^d{3}-d{2}-d{4}$"),不能来自用户输入;PCRE2 动态编译、功能全、兼容 perl,但得链接额外库、Windows 下需手动处理 DLL。

选型依据很实际:

  • 你的模式是否固定?是 → 用 ctre::search(s),编译时检查语法,运行零开销
  • 需要捕获组且模式动态?→ 接入 PCRE2,但注意 pcre2_compile() 可能返回 NULL,必须判空
  • 项目禁用 CMake 外部依赖?那 ctre 单头文件友好,PCRE2 需预编译或子模块管理

通配符看似简单,但路径、编码、编译器差异、动态输入这四点叠在一起,很容易在某个客户环境突然失效。最稳的路是:确定只要 */? 就手写;要正则且模式固定就上 ctre;其余情况,先测好目标平台的 std::regex 行为再说。

text=ZqhQzanResources