在C++中检查一个字符串是否包含另一个字符串

737

我有一个std::string类型的变量。我想检查它是否包含某个特定的std::string。应该怎样做?

是否有一个函数可以在找到字符串时返回true,未找到则返回false?


6
你是指 char* 类型的字符串还是来自 STL 的字符串? - anthares
2
是的,因为 std::string 类型有一个字符串文字构造函数。 - Matthieu N.
48
请提出一个建议,向 stdlib 添加 std::basic_string::contains - emlai
10
自从提出了这个建议(使用包含字符串函数),现在已经写成了提案,并且该成员函数现在已被纳入 C++23 草案中。 - Stephen
7
@Stephen 这应该从一开始就存在。令人难以置信的是,直到现在还没有人实施它。 - SimonC
显示剩余2条评论
16个回答

1109

使用std::string::find的方法如下:

if (s1.find(s2) != std::string::npos) {
    std::cout << "found!" << '\n';
}

注意:如果s2s1的子字符串,且s1s2均为std::string类型,则会打印出"found!"。

2
当尝试查找子字符串时,std::string::find返回什么? - maDeveloper
2
它返回子字符串在字符串中从给定起始位置开始的第一个出现的索引。起始位置的默认值为0。 - Morris Bahrami
2
根据C++文档,std::string::npos的值为-1。如果没有匹配项,则find函数返回-1。如果有匹配项,则返回出现的索引,该索引始终为正数。这就是为什么我们在此处执行“!= -1”检查的原因。 - Sam B
1
@SamB 差不多正确了!;) 更精确的说法是“0或正数”。 - Claudiu Cruceanu

167
你可以尝试使用find函数:
std::string str ("There are two needles in this haystack.");
std::string str2 ("needle");

if (str.find(str2) != std::string::npos) {
//.. found.
} 

由于无法编辑上面的答案(编辑队列待处理),我将在以下链接中提供有关该答案逻辑的更多信息: https://www.cplusplus.com/reference/string/string/npos/考虑到网站上的信息,您可以将 if 语句解读为: “如果 str2 的值不等于:“not found”(在搜索时);则找到了字符串!” - user12761381

91

从C++23开始,您可以使用std::string::contains

#include <string>

const auto haystack = std::string("haystack with needles");
const auto needle = std::string("needle");

if (haystack.contains(needle))
{
    // found!
}

156
仍然让我惊讶的是,我们在C++23之前才实现了这一点。 - Kobski
1
contains函数最近(2021年1月)在Clang和GCC中才被实现。在最新版本中,它可以编译:https://godbolt.org/z/PGWj4W - Synck
1
GCC 11和Clang 12支持在C++23/2b模式下调用std::string::contains() - PFee
8
它的用处正如它看起来的那样 - 它告诉你一个字符串是否包含某个子字符串/字符,通常这就是你所需要的全部。如果你关心它的位置,那么你可以使用“find”来告诉你它的位置。 - ABaumstumpf
2
要了解为什么它存在,请查看此处:(https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1679r0.html#:~:text=A%20contains%20member%20function%20would,the%20intention%20of%20the%20programmer.) - Angaj Sharma
显示剩余4条评论

41

实际上,你可以尝试使用boost库,我认为std::string并没有提供足够的方法来执行所有常见的字符串操作。在boost中,你只需要使用 boost::algorithm::contains

#include <string>
#include <boost/algorithm/string.hpp>

int main() {
    std::string s("gengjiawen");
    std::string t("geng");
    bool b = boost::algorithm::contains(s, t);
    std::cout << b << std::endl;
    return 0;
}

48
我认为std::string并没有提供足够的方法来执行所有常见的字符串操作。但是,对于这个问题,有一个名为find的方法可以使用。不需要引入库依赖。 - stefan
10
@stefan,你是正确的,确实有一个find方法,但是split、replace和许多其他操作呢。你可以将std::string与Java中的字符串API进行比较。另外,我认为contains比find更优雅,用于检查一个字符串是否包含另一个字符串。 - Geng Jiawen
1
此外,这段代码很短,易于记忆。Cpp 17已经增加了对文件系统的支持。我希望Cpp 2x也能为字符串做些什么。在现代cpp中缺乏基本字符串方法支持非常令人痛苦。 - Geng Jiawen
2
你真的需要这些 "usings" 吗?当我阅读这段代码时,我不知道 containsstd::contains 还是 boost::contains,这似乎是一个重大缺陷。我猜测 std::contains 目前不存在,但我不确定读者已经记住了 std 中的所有内容是否合理。而且 std::contains 很可能在未来的某个版本中存在,这将破坏这个程序。 - Don Hatch

30

你可以尝试这个

string s1 = "Hello";
string s2 = "el";
if(strstr(s1.c_str(),s2.c_str()))
{
   cout << " S1 Contains S2";
}

不适用于包含NUL字符的字符串。 - Johannes Overmann
2
很酷。这很快,即1微秒。而在我的机器上,data.find(needle) != string::npos需要4微秒。 - Gray Programmerz

16
如果功能对您的系统至关重要,使用旧的strstr方法实际上是有益的。在algorithm中的std::search方法是可能最慢的。我的猜测是创建那些迭代器需要很长时间。
我用于计时的代码是:
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <random>
#include <chrono>

std::string randomString( size_t len );

int main(int argc, char* argv[])
{
        using namespace std::chrono;

        const size_t haystacksCount = 200000;
        std::string haystacks[haystacksCount];
        std::string needle = "hello";

        bool sink = true;

        high_resolution_clock::time_point start, end;
        duration<double> timespan;

        int sizes[10] = { 10, 20, 40, 80, 160, 320, 640, 1280, 5120, 10240 };

        for(int s=0; s<10; ++s)
        {
                std::cout << std::endl << "Generating " << haystacksCount << " random haystacks of size " << sizes[s] << std::endl;
                for(size_t i=0; i<haystacksCount; ++i)
                {
                        haystacks[i] = randomString(sizes[s]);
                }

                std::cout << "Starting std::string.find approach" << std::endl;
                start = high_resolution_clock::now();
                for(size_t i=0; i<haystacksCount; ++i)
                {
                        if(haystacks[i].find(needle) != std::string::npos)
                        {
                                sink = !sink; // useless action
                        }
                }
                end = high_resolution_clock::now();
                timespan = duration_cast<duration<double>>(end-start);
                std::cout << "Processing of " << haystacksCount << " elements took " << timespan.count() << " seconds." << std::endl;

                std::cout << "Starting strstr approach" << std::endl;
                start = high_resolution_clock::now();
                for(size_t i=0; i<haystacksCount; ++i)
                {
                        if(strstr(haystacks[i].c_str(), needle.c_str()))
                        {
                                sink = !sink; // useless action
                        }
                }
                end = high_resolution_clock::now();
                timespan = duration_cast<duration<double>>(end-start);
                std::cout << "Processing of " << haystacksCount << " elements took " << timespan.count() << " seconds." << std::endl;

                std::cout << "Starting std::search approach" << std::endl;
                start = high_resolution_clock::now();
                for(size_t i=0; i<haystacksCount; ++i)
                {
                        if(std::search(haystacks[i].begin(), haystacks[i].end(), needle.begin(), needle.end()) != haystacks[i].end())
                        {
                                sink = !sink; // useless action
                        }
                }
                end = high_resolution_clock::now();
                timespan = duration_cast<duration<double>>(end-start);
                std::cout << "Processing of " << haystacksCount << " elements took " << timespan.count() << " seconds." << std::endl;
        }

        return 0;
}

std::string randomString( size_t len)
{
        static const char charset[] = "abcdefghijklmnopqrstuvwxyz";
        static const int charsetLen = sizeof(charset) - 1;
        static std::default_random_engine rng(std::random_device{}());
        static std::uniform_int_distribution<> dist(0, charsetLen);
        auto randChar = [charset, &dist, &rng]() -> char
        {
                return charset[ dist(rng) ];
        };

        std::string result(len, 0);
        std::generate_n(result.begin(), len, randChar);
        return result;
}

在这里我生成随机的haystacks并在其中搜索needle。干草堆计数已经设置,但是每个干草堆中字符串的长度从一开始的10增加到最终的10240。大多数时候程序实际上花费在生成随机字符串上,但这是可以预料的。

输出如下:

Generating 200000 random haystacks of size 10
Starting std::string.find approach
Processing of 200000 elements took 0.00358503 seconds.
Starting strstr approach
Processing of 200000 elements took 0.0022727 seconds.
Starting std::search approach
Processing of 200000 elements took 0.0346258 seconds.

Generating 200000 random haystacks of size 20
Starting std::string.find approach
Processing of 200000 elements took 0.00480959 seconds.
Starting strstr approach
Processing of 200000 elements took 0.00236199 seconds.
Starting std::search approach
Processing of 200000 elements took 0.0586416 seconds.

Generating 200000 random haystacks of size 40
Starting std::string.find approach
Processing of 200000 elements took 0.0082571 seconds.
Starting strstr approach
Processing of 200000 elements took 0.00341435 seconds.
Starting std::search approach
Processing of 200000 elements took 0.0952996 seconds.

Generating 200000 random haystacks of size 80
Starting std::string.find approach
Processing of 200000 elements took 0.0148288 seconds.
Starting strstr approach
Processing of 200000 elements took 0.00399263 seconds.
Starting std::search approach
Processing of 200000 elements took 0.175945 seconds.

Generating 200000 random haystacks of size 160
Starting std::string.find approach
Processing of 200000 elements took 0.0293496 seconds.
Starting strstr approach
Processing of 200000 elements took 0.00504251 seconds.
Starting std::search approach
Processing of 200000 elements took 0.343452 seconds.

Generating 200000 random haystacks of size 320
Starting std::string.find approach
Processing of 200000 elements took 0.0522893 seconds.
Starting strstr approach
Processing of 200000 elements took 0.00850485 seconds.
Starting std::search approach
Processing of 200000 elements took 0.64133 seconds.

Generating 200000 random haystacks of size 640
Starting std::string.find approach
Processing of 200000 elements took 0.102082 seconds.
Starting strstr approach
Processing of 200000 elements took 0.00925799 seconds.
Starting std::search approach
Processing of 200000 elements took 1.26321 seconds.

Generating 200000 random haystacks of size 1280
Starting std::string.find approach
Processing of 200000 elements took 0.208057 seconds.
Starting strstr approach
Processing of 200000 elements took 0.0105039 seconds.
Starting std::search approach
Processing of 200000 elements took 2.57404 seconds.

Generating 200000 random haystacks of size 5120
Starting std::string.find approach
Processing of 200000 elements took 0.798496 seconds.
Starting strstr approach
Processing of 200000 elements took 0.0137969 seconds.
Starting std::search approach
Processing of 200000 elements took 10.3573 seconds.

Generating 200000 random haystacks of size 10240
Starting std::string.find approach
Processing of 200000 elements took 1.58171 seconds.
Starting strstr approach
Processing of 200000 elements took 0.0143111 seconds.
Starting std::search approach
Processing of 200000 elements took 20.4163 seconds.

3
答案的简短版本是:使用 C 而不是 C++ :) - r0n9

7

如果字符串的大小相对较大(数百个字节或更多),且C++17可用,则可以使用Boyer-Moore-Horspool搜索器(来自cppreference.com的示例):

#include <iostream>
#include <string>
#include <algorithm>
#include <functional>

int main()
{
    std::string in = "Lorem ipsum dolor sit amet, consectetur adipiscing elit,"
                     " sed do eiusmod tempor incididunt ut labore et dolore magna aliqua";
    std::string needle = "pisci";
    auto it = std::search(in.begin(), in.end(),
                   std::boyer_moore_searcher(
                       needle.begin(), needle.end()));
    if(it != in.end())
        std::cout << "The string " << needle << " found at offset "
                  << it - in.begin() << '\n';
    else
        std::cout << "The string " << needle << " not found\n";
}

25
时代的迹象。以前会有人提供一个函数:bool contains(const std::string& haystack, const std::string& needle)。现在,他们提供一组以某些鲜为人知的论文作者命名的拼图块,使它看起来更像计算机科学……(注意不要添加解释或其他内容) - BitTickler

5

关于什么

string response = "hello world";
string findMe = "world";

if(response.find(findMe) != string::npos)
{
     //found
}

3
如果您不想使用标准库函数,以下是一种解决方案。
#include <iostream>
#include <string>

bool CheckSubstring(std::string firstString, std::string secondString){
    if(secondString.size() > firstString.size())
        return false;

    for (int i = 0; i < firstString.size(); i++){
        int j = 0;
        // If the first characters match
        if(firstString[i] == secondString[j]){
            int k = i;
            while (firstString[i] == secondString[j] && j < secondString.size()){
                j++;
                i++;
            }
            if (j == secondString.size())
                return true;
            else // Re-initialize i to its original value
                i = k;
        }
    }
    return false;
}

int main(){
    std::string firstString, secondString;

    std::cout << "Enter first string:";
    std::getline(std::cin, firstString);

    std::cout << "Enter second string:";
    std::getline(std::cin, secondString);

    if(CheckSubstring(firstString, secondString))
        std::cout << "Second string is a substring of the frist string.\n";
    else
        std::cout << "Second string is not a substring of the first string.\n";

    return 0;
}

6
您已经在使用std::string,因此您的代码已经依赖于std lib。我真的不明白为什么要避免使用std::string::find这个被接受的解决方案。 - b00n12
是的,那是个好观点。我写这个的时候没有考虑到这一点。我想我写这个的时候可能在考虑如何避免使用std::find。 - Testing123
3
仅供未来的访问者参考:这个算法实际上是不正确的。因为在一个未匹配成功的子字符串匹配后,"i"指针从不回溯,所以有些情况下无法匹配成功,例如考虑以下情况: aaabc, aab - sAm_vdP
1
这段代码有几个漏洞。CheckSubstring(std::string firstString, std::string secondString)会深度复制传递给函数的两个字符串,这是很昂贵的,特别是对于需要堆分配的较长字符串。此外,假设你调用了CheckSubstring("XYZab", "ab\0\0") - while循环将比较aabb,第一个字符串末尾的隐式NUL和第二个字符串中的显式NUL,然后它将读取超出第一个字符串缓冲区,导致未定义的行为。要修复这个问题,请使用for (... i <= firstString.size() - secondString().size(); ...)`。 - Tony Delroy

3

使用 std::regex_search 是个不错的选择,也是将搜索更加通用化的基石。下方是带有注释的示例:

//THE STRING IN WHICH THE SUBSTRING TO BE FOUND.
std::string testString = "Find Something In This Test String";

//THE SUBSTRING TO BE FOUND.
auto pattern{ "In This Test" };

//std::regex_constants::icase - TO IGNORE CASE.
auto rx = std::regex{ pattern,std::regex_constants::icase };

//SEARCH THE STRING.
bool isStrExists = std::regex_search(testString, rx);

需要包含#include <regex>

假设输入的字符串为"Find Something In This Example String",如果希望搜索 "In This Test" 或 "In This Example",可以通过调整模式来增强搜索,如下所示。

//THE SUBSTRING TO BE FOUND.
auto pattern{ "In This (Test|Example)" };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接