为什么find_first_not_of在遇到字符串结尾时失败?

时间:2017-11-21 21:15:24

标签: c++ string stl

std::string::find_first_not_of的{​​{3}}上,它提供了文本宏替换的示例:

#include <string>
#include <iostream>

int main() {
    std::string to_search = "Some data with %MACROS to substitute";

    std::cout << "Before: " << to_search << '\n';

    auto pos = std::string::npos;
    while ((pos = to_search.find('%')) != std::string::npos) {
        // Permit uppercase letters, lowercase letters and numbers in macro names
        const auto after = to_search.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", pos + 1);

        // Now to_search[pos] == '%' and to_search[after] == ' ' (after the 'S')

        if(after != std::string::npos)
        to_search.replace(pos, after - pos, "some very nice macros");
    }

    std::cout << "After: " << to_search << '\n';
}

这很有效,除非%MACROS位于字符串的末尾(如下所示),在这种情况下find_first_not_of返回std::string::npos,这将变成无限循环。

std::string to_search = "Some data with %MACROS";

为什么这个特定的代码块完全跳过替换%MACROS,因为它位于字符串的末尾?不考虑\ 0作为&#34;不是&#34;它给出的字符集是什么?

1 个答案:

答案 0 :(得分:2)

find_first_not_of()在找不到匹配项时会返回npos。在您的情况下,当宏位于字符串的末尾时,没有匹配的字符,因此npos。要处理这种情况,只需在执行替换之前将npos替换为size()

试试这个:

#include <string>
#include <iostream>

int main() {
    std::string to_search = "Some data with %MACROS to substitute";
    std::string replace_with = "some very nice macros";
    std::string macro_name;

    std::cout << "Before: " << to_search << '\n';

    std::string::size_type pos = 0;
    while ((pos = to_search.find('%', pos)) != std::string::npos)
    {
        // Permit uppercase letters, lowercase letters and numbers in macro names
        auto after = to_search.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", pos + 1);

        // if no matching chars, set after past the last char...
        if (after == std::string::npos)
            after = to_search.size();

        auto count = after - pos;
        if (count > 1)
        {
            // extract the macro name and replace the macor only if
            // the name actually maps to something worth replacing...

            macro_name = to_search.substr(pos + 1, count - 1);
            if (macro_name == "MACROS")
            {
                // found a macro!
                to_search.replace(pos, count, replace_with);

                // start the next search after the replaced text...
                pos += replace_with.size();

                continue;
            }

            // check for other macros as needed...
        }

        // no matching macro, skip the '%' and search again from the next char...
        ++pos;
    }

    std::cout << "After: " << to_search << '\n';
}

Live Demo