
用 std::string::find 判断子串是否存在最直接
这是 C++ 标准库中最常用、最轻量的方式。它返回子串首次出现的索引,没找到时返回 std::string::npos。
注意:不要用 == -1 判断失败——std::string::npos 是无符号类型(通常是 size_t),值为 static_cast,直接和 -1 比较会触发隐式转换警告或逻辑错误。
std::string s = "hello world";
std::string sub = "world";
if (s.find(sub) != std::string::npos) {
// 找到了
}
- 时间复杂度平均 O(n+m),最坏 O(n×m),适用于大多数日常场景
- 区分大小写,如需忽略大小写,得先转小写再查,或手写遍历比较
- 不支持通配符或正则,纯字面量匹配
需要更高效匹配时考虑 std::string_view::find
如果原始字符串是只读的、生命周期可控(比如来自函数参数或常量字符串字面量),用 std::string_view 替代 std::string 能避免拷贝开销,find 行为一致但更快。
std::string_view sv = "The quick brown fox";
if (sv.find("fox") != std::string_view::npos) {
// 成功
}
-
std::string_view不拥有数据,构造零成本 - C++17 起可用;C++14 及更早需自行实现类似视图或坚持用
std::string - 不能用于需要修改内容或存储长期引用的场景(因底层数据可能已析构)
要找所有匹配位置或支持复杂模式就上 std::regex
当需求超出“是否包含”——比如要找全部出现位置、提取捕获组、支持 .* 或字符类——std::regex 是标准方案,但代价明显更高。
立即学习“C++免费学习笔记(深入)”;
std::string text = "a1b2c3";
std::regex pattern(R"(\d)");
auto begin = std::sregex_iterator(text.begin(), text.end(), pattern);
auto end = std::sregex_iterator();
for (auto it = begin; it != end; ++it) {
std::cout << it->str() << "\n"; // 输出 1, 2, 3
}
- 编译正则表达式有开销,频繁使用建议缓存
std::regex对象 - 不同编译器对
std::regex的实现质量差异大(尤其 GCC libstdc++ 历史上有 bug 和性能问题) - 简单子串查找用正则属于杀鸡用牛刀,运行时慢一个数量级以上
自定义函数做大小写不敏感查找要注意 locale 陷阱
标准库没有内置 case-insensitive find,常见做法是转小写后查,但直接用 std::tolower 处理多字节字符(如 UTF-8 中文、德语 ß)会出错——它只对单字节 char 安全。
- 若确定输入是 ASCII,可安全用
std::tolower遍历转换 - 否则应使用 ICU、Boost.Locale 或 C++20 的
std::ranges::search+ 自定义谓词 - 简单起见,多数项目选择外部依赖
boost::algorithm::icontains,它内部处理了 locale 边界
真正容易被忽略的是:哪怕只是判断“是否包含”,在跨平台或国际化场景下,std::string::find 的语义仍是字节级精确匹配——它不理解 Unicode 码点、组合字符或正规化形式。这时候“包含”本身就需要重新定义。











