让代码更简洁:一篇文章读懂 C++ 中的字符串包含判定

2025-12-19

这个功能让判断字符串中是否包含某个子串变得非常直观,再也不用去记那长长的 s.find(sub) != std::string::npos 啦!不过,在使用时也有一些小细节需要注意。

虽然这个方法很好用,但在实际编程中,大家经常会遇到以下几个“坑”

版本限制
这是 C++23 引入的新特性。如果你的编译器版本较低(比如还在用 C++11 或 C++17),代码是编译不过的。

大小写敏感
contains 是区分大小写的。如果你搜索 "Apple" 而原字符串是 "apple",它会返回 false

性能考量
虽然 contains 语法简洁,但它的底层实现通常还是线性搜索。在处理海量数据或超长字符串时,可能需要更高级的算法(如 KMP)。

在 C++23 中,你可以直接这样写,非常优雅

#include <iostream>
#include <string>
#include <string_view>

int main() {
    std::string text = "Hello, welcome to the world of C++23!";

    // 直接判断是否包含子串
    if (text.contains("C++23")) {
        std::cout << "找到了 C++23!" << std::endl;
    }

    // 也可以判断是否包含单个字符
    if (text.contains('!')) {
        std::cout << "句尾有感叹号。" << std::endl;
    }

    return 0;
}

如果你因为环境限制无法使用 C++23,别担心,这里有几种常见的替代方案

这是 C++23 之前最标准的做法。

#include <string>

bool manual_contains(const std::string& str, const std::string& sub) {
    // 如果 find 找不到,会返回 npos
    return str.find(sub) != std::string::npos;
}

如果你使用的是 C++17,推荐用 std::string_view,它的效率更高,且在 C++23 中也支持 contains

#include <string_view>

bool modern_check(std::string_view str, std::string_view sub) {
    // 在 C++23 之前,string_view 也没有 contains,仍需用 find
    return str.find(sub) != std::string_view::npos;
}

如果你想让搜索更智能,可以参考下面这个小技巧

#include <iostream>
#include <string>
#include <algorithm> // 用于 transform

bool contains_insensitive(std::string data, std::string search) {
    // 全部转为小写后再比较
    std::transform(data.begin(), data.end(), data.begin(), ::tolower);
    std::transform(search.begin(), search.end(), search.begin(), ::tolower);
    
    return data.find(search) != std::string::npos;
}

int main() {
    std::string msg = "Hello World";
    if (contains_insensitive(msg, "world")) {
        std::cout << "忽略大小写匹配成功!" << std::endl;
    }
    return 0;
}

std::basic_string::contains 的出现确实让 C++ 变得更现代化、更易读了。只要确保你的编译器开启了 -std=c++23 选项,就可以放心大胆地使用它!


cpp



迭代器配对神器:掌握 std::mismatch 的常见陷阱与替代方案

std::mismatch 是一个非常实用的函数,用于比较两个序列(例如数组、std::vector 或其他容器)中不匹配的第一个元素的位置。std::mismatch 函数接受两个范围(由起始和结束迭代器定义)或者一个范围和一个起始迭代器,然后从头开始逐个比较元素。它返回一个 std::pair,包含第一个不匹配元素在两个序列中的迭代器。如果两个序列在比较范围内完全匹配,它将返回各自序列的结束迭代器。


C++ hh_mm_ss 实用指南:时间格式化与负值处理

这个构造函数是用于创建和初始化一个 std::chrono::hh_mm_ss 对象,该对象能以时、分、秒的格式来表示时间。它在处理时间段(duration)并将其转换为日常可见的时/分/秒格式时非常有用。std::chrono::hh_mm_ss 类有一个主要的构造函数,它接受一个 时间段(duration) 参数,并根据这个时间段来计算出相应的时、分、秒。


C++ 编程诊断:如何标准地获取调用堆栈及符号解析缺失的应对

由于您提到了 C++ 标准头文件 <stacktrace>,这通常是用于在程序崩溃或需要诊断时获取当前的函数调用堆栈信息。这个功能对于调试和错误报告非常有用。<stacktrace> 头文件是在 C++23 中引入的(作为 ISO/IEC 148822023 的一部分)。它提供了一个标准化的、跨平台的方式来捕获和操作函数调用堆栈信息。



开发者必读:自定义指针类型时如何正确实现 pointer_to 接口

今天我们要聊的是 std::pointer_traits::pointer_to。虽然它在日常业务逻辑开发中不常见,但在编写自定义容器(Container)或分配器(Allocator)时,它可是一个非常有用的工具。简单来说,它的作用是“根据一个对象的引用,获取指向该对象的指针”。


深入理解 C++ std::array 的编译时访问:std::get 的用法与陷阱

std::get 是一个非常酷的函数模板,它允许你以编译时安全的方式访问 std::array 或 std::tuple 中的元素。对于 std::array 来说,它要求你用一个常量表达式(即编译时已知)的索引来获取元素。使用 std::get 访问 std::array 时,最常见的问题都围绕着编译时常量的要求。


高效迭代嵌套容器:理解 C++ Ranges 中 join_view 的 Sentinel 机制

很高兴能为您解释 C++ Ranges 中 std::ranges::join_view::end 的相关内容、常见问题以及替代方案。std::ranges::join_view 用于将一个内部元素本身是 Range 的 Range 扁平化(flatten),使其看起来像一个单一的 Range。而 std::ranges::join_view::end 则是获取这个扁平化 Range 的结束迭代器或 Sentinel(一个表示结束的标记)。


starts_with 编译报错?C++ 字符串前缀检查的常见问题及多版本代码示例

std::basic_string::starts_with 是 C++20 引入的一个非常方便的成员函数,它的作用是检查一个字符串是否以另一个给定的字符串、字符或字符串视图(string view)开始。头文件 <string>复杂度 线性复杂度,与被检查的前缀长度成正比。


Ranges 常见问题解答:如何避免 take_while_view::end 带来的迭代器性能开销

std::ranges::take_while_view 是一种 View,它会从底层 Range 中取出满足特定条件的元素,直到遇到第一个不满足条件的元素为止。end() 成员函数的作用是获取这个视图的结束迭代器。关于 std::ranges::take_while_view::end,使用者可能会遇到以下几个常见问题或产生困惑


现代 C++ Ranges 进阶:zip_transform_view 中 Sentinel::sentinel 的作用与替代方案

您提到的 std::ranges::zip_transform_view::sentinel<Const>::sentinel 是一个非常专业的类型,它是 std::ranges::zip_transform_view 的 哨兵(Sentinel) 类型。


C++ 关键字 mutable:在 const 世界中实现可变性

mutable 关键字只用于 类(class) 或 结构体(struct) 的 非静态(non-static) 成员变量(也叫数据成员)。它的作用是允许你在一个被声明为 const 的对象中修改该成员变量的值。通常,当一个对象被声明为 const 时,你就不能修改它的任何成员变量。但是,如果你将某个特定的成员变量用 mutable 修饰,那么即使该对象是 const 的,你仍然可以通过 const 成员函数或其他方式来修改这个 mutable 成员。