351

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

if argv[1].startswith('--foo='):
    foo_value = int(argv[1][len('--foo='):])

(例如,如果argv[1]--foo=98,那么foo_value98。)

更新:我对研究 Boost 犹豫不决,因为我只是想对一个简单的小命令行工具做一个很小的改变(我宁愿不必学习如何链接和使用 Boost 作为未成年人改变)。

4

23 回答 23

694

使用rfind带有搜索位置参数的重载pos,并为其传递零:

std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) { // pos=0 limits the search to the prefix
  // s starts with prefix
}

谁还需要什么?纯STL!

许多人将其误读为“向后搜索整个字符串以查找前缀”。这会给出错误的结果(例如string("tititito").rfind("titi")返回 2,因此当与它进行比较时== 0会返回 false)并且效率低下(查看整个字符串而不是仅仅查看开头)。但它并没有这样做,因为它传递pos参数 as 0,这将搜索限制为仅匹配该位置或更早的位置。例如:

std::string test = "0123123";
size_t match1 = test.rfind("123");    // returns 4 (rightmost match)
size_t match2 = test.rfind("123", 2); // returns 1 (skipped over later match)
size_t match3 = test.rfind("123", 0); // returns std::string::npos (i.e. not found)
于 2016-11-05T17:36:08.503 回答
203

你会这样做:

std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
    foo_value = std::stoi(arg.substr(prefix.size()));

寻找像 Boost.ProgramOptions 这样为您执行此操作的库也是一个好主意。

于 2009-12-10T01:07:31.337 回答
163

为了完整起见,我将提到 C 的方式来做到这一点:

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

strncmp(str, substr, strlen(substr))

0如果以 .str 开头,将返回substr。函数strncmpstrlen在 C 头文件中<string.h>

(最初由 Yaseen Rauf在这里发布,添加了标记)

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

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

strncmp(str.c_str(), substr.c_str(), substr.size())
于 2011-08-22T02:58:31.363 回答
93

如果你已经在使用 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
}

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

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

于 2009-12-10T01:03:41.690 回答
88

我自己使用的代码:

std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
    std::string argumentValue = argument.substr(prefix.size());
}
于 2011-12-29T19:41:26.970 回答
53

还没有人使用 STL算法/不匹配功能。如果返回 true,prefix 是 'toCheck' 的前缀:

std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()

完整的示例程序:

#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;
    }
}

编辑:

正如@James T. Huggett 建议的那样, std::equal 更适合这个问题:A 是 B 的前缀吗?并且代码略短:

std::equal(prefix.begin(), prefix.end(), toCheck.begin())

完整的示例程序:

#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;
  }
}
于 2013-01-13T00:09:43.623 回答
30

对于 C++17,您可以将std::basic_string_view& 与 C++20std::basic_string::starts_withstd::basic_string_view::starts_with.

std::string_view与 - 关于内存管理 - 相比的好处std::string是它只保存一个指向“字符串”(类似字符的对象的连续序列)的指针并且知道它的大小。不移动/复制源字符串以获得整数值的示例:

#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
}
于 2018-05-04T20:49:34.560 回答
26

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

但是,看到其他答案,我认为值得注意的是,如果您的文本已经作为std::string.

const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(strlen(foo));

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

std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(foo.length());
于 2013-07-28T01:11:38.513 回答
12

使用 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;
}
于 2011-12-21T21:56:25.383 回答
12
text.substr(0, start.length()) == start
于 2018-04-13T10:04:22.293 回答
11

冒着因使用 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;
}

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

$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number
于 2009-12-10T03:29:00.327 回答
10

我使用std::string::compare包装在实用程序中的方法,如下所示:

static bool startsWith(const string& s, const string& prefix) {
    return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}
于 2017-11-23T02:13:41.593 回答
8

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

std::string s = "abcd";
if (s.starts_with("abc")) {
    ...
}
于 2020-05-16T22:44:27.320 回答
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;

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

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

if (s.starts_with("--foo="))
于 2020-11-18T10:18:21.580 回答
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;
}
于 2018-11-16T15:51:33.953 回答
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);
}

对于以下命令:

$ ./a.out --foo=33

你会得到

33
于 2013-06-11T12:08:06.383 回答
5

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"});
于 2021-04-15T15:44:54.063 回答
3

好的,为什么要复杂使用库和东西?C++ String 对象重载了 [] 运算符,所以你可以只比较字符。就像我刚刚做的那样,因为我想列出目录中的所有文件并忽略不可见的文件和 .. 和 . 伪文件。

while ((ep = readdir(dp)))
{
    string s(ep->d_name);
    if (!(s[0] == '.')) // Omit invisible files and .. or .
        files.push_back(s);
}

就这么简单。。

于 2011-02-15T11:56:01.620 回答
2

您还可以使用strstr

if (strstr(str, substr) == substr) {
    // 'str' starts with 'substr'
}

但我认为它只适用于短字符串,因为当字符串实际上不是以“substr”开头时,它必须遍历整个字符串。

于 2012-08-26T23:41:24.090 回答
1
std::string text = "--foo=98";
std::string start = "--foo=";

if (text.find(start) == 0)
{
    int n = stoi(text.substr(start.length()));
    std::cout << n << std::endl;
}
于 2017-10-09T07:44:49.033 回答
0

由于 C++11std::regex_search也可用于提供更复杂的表达式匹配。下面的示例还处理浮点数std::stof,并通过后续转换为int.

但是,如果前缀不匹配,parseInt下面显示的方法可能会引发异常;std::invalid_argument这可以根据给定的应用程序轻松调整:

#include <iostream>
#include <regex>

int parseInt(const std::string &str, const std::string &prefix) {
  std::smatch match;
  std::regex_search(str, match, std::regex("^" + prefix + "([+-]?(?=\\.?\\d)\\d*(?:\\.\\d*)?(?:[Ee][+-]?\\d+)?)$"));
  return std::stof(match[1]);
}

int main() {
    std::cout << parseInt("foo=13.3", "foo=") << std::endl;
    std::cout << parseInt("foo=-.9", "foo=") << std::endl;
    std::cout << parseInt("foo=+13.3", "foo=") << std::endl;
    std::cout << parseInt("foo=-0.133", "foo=") << std::endl;
    std::cout << parseInt("foo=+00123456", "foo=") << std::endl;
    std::cout << parseInt("foo=-06.12e+3", "foo=") << std::endl;

//    throw std::invalid_argument
//    std::cout << parseInt("foo=1", "bar=") << std::endl;

    return 0;
}

正则表达式模式的魔力在以下答案中有详细说明。

编辑:上一个答案没有执行到整数的转换。

于 2018-05-07T15:43:35.227 回答
0

使用 C++11 或更高版本,您可以使用find()find_first_of()

使用 find 查找单个字符的示例:

#include <string>
std::string name = "Aaah";
size_t found_index = name.find('a');
if (found_index != std::string::npos) {
    // Found string containing 'a'
}

使用 find 从位置 5 开始查找完整字符串的示例:

std::string name = "Aaah";
size_t found_index = name.find('h', 3);
if (found_index != std::string::npos) {
    // Found string containing 'h'
}

仅使用第一个字符的示例find_first_of(),仅在开头进行搜索:

std::string name = ".hidden._di.r";
size_t found_index = name.find_first_of('.');
if (found_index == 0) {
    // Found '.' at first position in string
}

祝你好运!

于 2019-06-08T00:36:42.337 回答
-4
if(boost::starts_with(string_to_search, string_to_look_for))
    intval = boost::lexical_cast<int>(string_to_search.substr(string_to_look_for.length()));

这是完全未经测试的。原理与 Python 相同。需要 Boost.StringAlgo 和 Boost.LexicalCast。

检查字符串是否以另一个字符串开头,然后获取第一个字符串的子字符串('slice')并使用词法转换对其进行转换。

于 2009-12-10T01:08:15.283 回答