如何检查C++ std :: string是否以某个字符串开头,并将子字符串转换为int?

Dar*_*zer 213 c++ string parsing substring startswith

如何在C++中实现以下(Python伪代码)?

if argv[1].startswith('--foo='):
    foo_value = int(argv[1][len('--foo='):])
Run Code Online (Sandbox Code Playgroud)

(例如,如果argv[1]--foo=98,则foo_value98.)

更新:我对调查Boost犹豫不决,因为我只是想对一个简单的小命令行工具进行一个非常小的改动(我宁愿不必学习如何链接并使用Boost作为一个小的更改).

Lud*_*ert 344

std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
  // s starts with prefix
}
Run Code Online (Sandbox Code Playgroud)

谁还需要什么?纯STL!

  • 这个答案应该是投票最多而不是提升者:D为什么在你已经拥有STL时使用另一个库. (24认同)
  • 这段代码很糟糕,因为效率很低.`find`将继续寻找匹配,即使已经清楚它的参数不是`s`的前缀.这是一个巨大的性能下降. (19认同)
  • @MSalters好的,我的评论也被我的编辑淘汰了,因为`rfind(s,0)`首先没有这个问题.我似乎忘了删除它.感谢您指出`find`如何仍然可以优化; 我不知道这种可能性. (6认同)
  • @alcoforado“rfind 将从字符串的后面开始......”不,这只适用于不采用“pos”参数的“rfind()”重载。如果您使用带有“pos”参数的重载,那么它将不会搜索整个字符串,只会搜索该位置及更早的位置。(就像带有 `pos` 参数的常规 `find()` 只在该位置或稍后查找。)因此,如果您传递 `pos == 0`,如本答案所示,那么它实际上只会考虑该位置的匹配一个位置。这已经在答案和评论中得到了解释。 (6认同)
  • @luliu这个答案是错的,应该被投票. (5认同)
  • @sweisgerber.dev,我对你的第一个争论感到困惑。如果`titi` 位于字符串的*start*,`find` 的返回值只会为零。如果在其他地方找到它,您将获得非零返回值,如果未找到,您将获得同样非零的 `npos`。假设我是对的,我更喜欢这个答案,因为我不必引入任何非标准的东西(是的,我知道 Boost 无处不在,对于像这样的简单东西,我更喜欢核心 C++ 库)。 (2认同)
  • 我们是否有证据表明大多数编译器对此进行了优化?我在其他地方找不到提到“查找”或“ rfind”优化的常见做法,这是基于要检查的返回值的。 (2认同)
  • “那个位置或更早”是这里的重要短语。 (2认同)
  • 要查找一个字符串是否以另一个字符串结尾,您可以执行`s.find("toto", s.length() - 4) != std::string::npos`。将数字 4 替换为您要查找的后缀的长度。 (2认同)

Tho*_*mas 184

你会这样做:

std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
    foo_value = atoi(arg.substr(prefix.size()).c_str());
Run Code Online (Sandbox Code Playgroud)

寻找像你这样做的Boost.ProgramOptions这样的lib也是一个好主意.

  • 最大的问题是`atoi("123xyz")`返回`123`,而Python的`int("123xyz")`抛出异常. (7认同)
  • @Calmarius“rfind”解决方案不依赖于任何优化。根据定义,“rfind”的行为是在给定“pos=0”时仅查看单个索引,因此它始终是一种有效的检查。哪种语法更令人愉快是一个偏好问题。 (2认同)

Fel*_*bek 141

为了完整起见,我将提到C方式:

如果str是您的原始字符串,substr则是您要检查的子字符串

strncmp(str, substr, strlen(substr))

0如果str 以...开头将返回substr.的功能strncmpstrlen在C头文件<string.h>

(原帖由拉乌夫·亚辛在这里,标记加)

对于不区分大小写的比较,请使用strnicmp而不是strncmp.

这是C方式,对于C++字符串,您可以使用相同的函数,如下所示:

strncmp(str.c_str(), substr.c_str(), substr.size())
Run Code Online (Sandbox Code Playgroud)

  • 事实上,每个人似乎只是去"使用提升",我感谢一个stl或OS库版本 (9认同)
  • 是的。但是,它假设字符串中没有空字符。如果不是这种情况 - 应该使用 ```memcmp()``` (2认同)
  • 为什么有人会使用除了这个简单而美丽的解决方案之外的任何东西? (2认同)
  • @AvishaiY`它假设字符串中没有空字符。`这与假设 ìnt` 不包含值“-0.5”一样假设 - C 字符串恰好包含一个空字符作为结束标记。根据定义,如果它在任何其他位置包含 \0,则它不是 C 字符串。 (2认同)

Fer*_*cio 84

如果你正在使用Boost,你可以使用boost字符串算法 + boost lexical cast:

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

try {    
    if (boost::starts_with(argv[1], "--foo="))
        foo_value = boost::lexical_cast<int>(argv[1]+6);
} catch (boost::bad_lexical_cast) {
    // bad parameter
}
Run Code Online (Sandbox Code Playgroud)

像大多数boost库一样,字符串算法和词法转换只是标题,没有什么可以链接的.

像这里提供的许多其他答案一样,这种方法适用于非常简单的任务,但从长远来看,通常最好使用命令行解析库.Boost有一个(Boost.Program_options),如果您恰好使用Boost可能会有意义.

否则,搜索"c ++命令行解析器"将产生许多选项.

  • 当有人问如何在C++中进行简单的字符串操作时,"使用Boost"总是错误的答案. (140认同)
  • 为字符串前缀检查引入巨大的依赖关系就像用大炮射击鸟类一样. (94认同)
  • 减1表示Boost (86认同)
  • 如果你已经在项目中使用了boost,那么在这里使用boost是正确的. (28认同)
  • 答案的前缀是"如果你正在使用Boost ......".显然,这是正确的答案"......如果你正在使用Boost".如果没有,请查看@Thomas的建议 (10认同)
  • Boost 不是一个大的库..是一组大大小小的库。如果一个小的 boost 库可以解决这个问题,我不认为有理由避免它。 (4认同)
  • **boost :: starts_with和boost :: algorithm :: starts_with是一回事.**如果你看一下这个文件的结尾https://svn.boost.org/svn/boost/trunk/boost/algorithm/ string/predicate.hpp你会看到:**//使用algorithm :: starts_with将名称拉到boost命名空间; [...]** (3认同)
  • "boost :: algorithm :: starts_with"而不是"boost :: starts_with"? (2认同)
  • 如果这是您首先决定链接boost的原因,那么使用boost是错误的,如果您已经出于其他原因已经使用boost,我不明白为什么这会是一个大问题。 (2认同)
  • 如果您使用 C++,Boost 始终是一个解决方案。当没有必要的时候,就没有必要重新发明轮子。我们工作,我们不做家庭作业。 (2认同)

Hüs*_*ğlı 81

我自己使用的代码:

std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
    std::string argumentValue = argument.substr(prefix.size());
}
Run Code Online (Sandbox Code Playgroud)

  • 使用`substr`会导致不必要的复制.在[Thomas的回答](http://stackoverflow.com/a/1878038/481061)中使用的`str.compare(start,count,substr)`方法效率更高.[razvanco13的回答](http://stackoverflow.com/a/8596924/481061)有另一种方法可以避免使用`std :: equal`进行复制. (15认同)
  • @HüseyinYağlı`Thomas使用的atoi只适用于windows`嗯?`atoi`自从......以来一直是C标准库函数.事实上,`atoi`很糟糕 - 不是因为它是特定于Windows的 - 而是因为它是(1)C,而不是C++,并且(2)即使在C中也被弃用(你应该使用`strtol`或其中一个,相关函数.因为`atoi`没有错误处理.但是,无论如何,这只是在C中. (4认同)
  • 最简洁,只依赖于std :: string,除了在最终substr的末尾删除可选和误导性的argument.size(). (2认同)

mat*_*tiu 46

还没有人使用过STL 算法/不匹配函数.如果返回true,则前缀是'toCheck'的前缀:

std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()
Run Code Online (Sandbox Code Playgroud)

完整示例编程:

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

int main(int argc, char** argv) {
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "Will print true if 'prefix' is a prefix of string" << std::endl;
        return -1;
    }
    std::string prefix(argv[1]);
    std::string toCheck(argv[2]);
    if (prefix.length() > toCheck.length()) {
        std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
                  << "'prefix' is longer than 'string'" <<  std::endl;
        return 2;
    }
    if (std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()) {
        std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck << '"' << std::endl;
        return 0;
    } else {
        std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"' << toCheck << '"' << std::endl;
        return 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

正如@James T. Huggett所说,std :: equal更适合这个问题:A是B的前缀吗?并且是稍微短的代码:

std::equal(prefix.begin(), prefix.end(), toCheck.begin())
Run Code Online (Sandbox Code Playgroud)

完整示例编程:

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

int main(int argc, char **argv) {
  if (argc != 3) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "Will print true if 'prefix' is a prefix of string"
              << std::endl;
    return -1;
  }
  std::string prefix(argv[1]);
  std::string toCheck(argv[2]);
  if (prefix.length() > toCheck.length()) {
    std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
              << "'prefix' is longer than 'string'" << std::endl;
    return 2;
  }
  if (std::equal(prefix.begin(), prefix.end(), toCheck.begin())) {
    std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck
              << '"' << std::endl;
    return 0;
  } else {
    std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"'
              << toCheck << '"' << std::endl;
    return 1;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么不使用std :: equal? (2认同)
  • 对字符串使用 `std::equal` 的缺点是它不会检测字符串结尾,因此您需要手动检查前缀是否比整个字符串短。(在示例程序中正确完成,但在上面的单行中省略。) (2认同)

Mar*_*tos 25

鉴于字符串 - argv[1]"--foo"- 都是C字符串,@ FelixDombek的答案是最好的解决方案.

然而,看到其他答案,我认为值得注意的是,如果您的文本已经作为a提供std::string,那么存在一个迄今为止尚未提及的简单,零拷贝,最有效的解决方案:

const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(strlen(foo));
Run Code Online (Sandbox Code Playgroud)

如果foo已经是一个字符串:

std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(foo.length());
Run Code Online (Sandbox Code Playgroud)

  • `rfind(x,0)== 0`应该在标准中真正定义为`starts_with` (4认同)
  • @ankostis rfind(x) 从末尾开始搜索,直到找到 x,确实如此。但rfind(x,0)从start(position=0)开始搜索直到start;所以它只搜索需要搜索的地方;不从/直到结束进行搜索。 (4认同)

Gus*_*uss 15

C++20 更新:

  • 使用std::string::starts_with

https://en.cppreference.com/w/cpp/string/basic_string/starts_with

std::string str_value = /* smthg */;
const auto starts_with_foo = str_value.starts_with(std::string_view{"foo"});
Run Code Online (Sandbox Code Playgroud)


小智 11

使用STL这可能看起来像:

std::string prefix = "--foo=";
std::string arg = argv[1];
if (prefix.size()<=arg.size() && std::equal(prefix.begin(), prefix.end(), arg.begin())) {
  std::istringstream iss(arg.substr(prefix.size()));
  iss >> foo_value;
}
Run Code Online (Sandbox Code Playgroud)

  • 那应该是`if(prefix.size()<= arg.size()&& std :: equal(...))`. (2认同)

Roi*_*ton 11

在C ++ 17中,可以将std::basic_string_view&与C ++ 20 std::basic_string::starts_with或一起使用std::basic_string_view::starts_with

与内存管理std::string_view相比,这样做的好处std::string是它仅持有指向“字符串”(char型对象的连续序列)的指针,并且知道其大小。没有移动/复制源字符串只是为了获取整数值的示例:

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

int main()
{
    constexpr auto argument = "--foo=42"; // Emulating command argument.
    constexpr auto prefix = "--foo=";
    auto inputValue = 0;

    constexpr auto argumentView = std::string_view(argument);
    if (argumentView.starts_with(prefix))
    {
        constexpr auto prefixSize = std::string_view(prefix).size();
        try
        {
            // The underlying data of argumentView is nul-terminated, therefore we can use data().
            inputValue = std::stoi(argumentView.substr(prefixSize).data());
        }
        catch (std::exception & e)
        {
            std::cerr << e.what();
        }
    }
    std::cout << inputValue; // 42
}
Run Code Online (Sandbox Code Playgroud)

  • @RolandIllig感谢您的坚持!没错,这是使用`std :: atoi`而不是`std :: stoi`的疏忽。我已经解决了。 (2认同)

Tom*_*Tom 10

冒着使用C构造的风险,我认为这个sscanf例子比大多数Boost解决方案更优雅.如果您在任何有Python解释器的地方运行,您不必担心链接!

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    for (int i = 1; i != argc; ++i) {
        int number = 0;
        int size = 0;
        sscanf(argv[i], "--foo=%d%n", &number, &size);
        if (size == strlen(argv[i])) {
            printf("number: %d\n", number);
        }
        else {
            printf("not-a-number\n");
        }
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

下面是一些示例输出,它演示了解决方案处理前导/尾随垃圾与正确的Python代码一样正确,并且比使用的任何东西更准确atoi(错误地忽略非数字后缀).

$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number
Run Code Online (Sandbox Code Playgroud)

  • 如果`argv [i]`是``--foo = 9999999999999999999999999"`,行为是未定义的(尽管大多数或所有实现都应该表现得很好).我假设`9999999999999999999999999> INT_MAX`. (7认同)

Shi*_*hah 9

我使用std::string::compare包裹在下面的实用方法:

static bool startsWith(const string& s, const string& prefix) {
    return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}
Run Code Online (Sandbox Code Playgroud)


Mac*_*nus 9

text.substr(0, start.length()) == start
Run Code Online (Sandbox Code Playgroud)

  • @GregorDoroschenko它确实回答“检查字符串是否以另一个开头”部分。 (3认同)
  • 使用 std::string 高效而优雅。我从中学到的最多。 (2认同)

Sam*_*mar 8

从 C++20 开始,您可以使用该starts_with方法。

std::string s = "abcd";
if (s.starts_with("abc")) {
    ...
}
Run Code Online (Sandbox Code Playgroud)


小智 7

在 C++20 中,现在有starts_with一个成员函数可用,std::string定义为:

constexpr bool starts_with(string_view sv) const noexcept;

constexpr bool starts_with(CharT c) const noexcept;

constexpr bool starts_with(const CharT* s) const;
Run Code Online (Sandbox Code Playgroud)

所以你的代码可能是这样的:

std::string s{argv[1]};

if (s.starts_with("--foo="))
Run Code Online (Sandbox Code Playgroud)


val*_*tis 6

如果您需要 C++11 兼容性并且不能使用 boost,这里是一个与 boost 兼容的插件,其中包含一个使用示例:

#include <iostream>
#include <string>

static bool starts_with(const std::string str, const std::string prefix)
{
    return ((prefix.size() <= str.size()) && std::equal(prefix.begin(), prefix.end(), str.begin()));
}

int main(int argc, char* argv[])
{
    bool usage = false;
    unsigned int foos = 0; // default number of foos if no parameter was supplied

    if (argc > 1)
    {
        const std::string fParamPrefix = "-f="; // shorthand for foo
        const std::string fooParamPrefix = "--foo=";

        for (unsigned int i = 1; i < argc; ++i)
        {
            const std::string arg = argv[i];

            try
            {
                if ((arg == "-h") || (arg == "--help"))
                {
                    usage = true;
                } else if (starts_with(arg, fParamPrefix)) {
                    foos = std::stoul(arg.substr(fParamPrefix.size()));
                } else if (starts_with(arg, fooParamPrefix)) {
                    foos = std::stoul(arg.substr(fooParamPrefix.size()));
                }
            } catch (std::exception& e) {
                std::cerr << "Invalid parameter: " << argv[i] << std::endl << std::endl;
                usage = true;
            }
        }
    }

    if (usage)
    {
        std::cerr << "Usage: " << argv[0] << " [OPTION]..." << std::endl;
        std::cerr << "Example program for parameter parsing." << std::endl << std::endl;
        std::cerr << "  -f, --foo=N   use N foos (optional)" << std::endl;
        return 1;
    }

    std::cerr << "number of foos given: " << foos << std::endl;
}
Run Code Online (Sandbox Code Playgroud)


Car*_*ook 5

为什么不使用gnu getopts?这是一个基本的例子(没有安全检查):

#include <getopt.h>
#include <stdio.h>

int main(int argc, char** argv)
{
  option long_options[] = {
    {"foo", required_argument, 0, 0},
    {0,0,0,0}
  };

  getopt_long(argc, argv, "f:", long_options, 0);

  printf("%s\n", optarg);
}
Run Code Online (Sandbox Code Playgroud)

对于以下命令:

$ ./a.out --foo=33
Run Code Online (Sandbox Code Playgroud)

你会得到

33
Run Code Online (Sandbox Code Playgroud)