How do I check if a C++ std::string starts with a

2019-01-12 18:34发布

How do I implement the following (Python pseudocode) in C++?

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

(For example, if argv[1] is --foo=98, then foo_value is 98.)

Update: I'm hesitant to look into Boost, since I'm just looking at making a very small change to a simple little command-line tool (I'd rather not have to learn how to link in and use Boost for a minor change).

19条回答
Root(大扎)
2楼-- · 2019-01-12 18:55

Using STL this could look like:

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;
}
查看更多
甜甜的少女心
3楼-- · 2019-01-12 18:55

Since C++11 also std::regex_search can be used, e.g. as follows (returns an empty string on failure):

#include <regex>

std::string startsWith(const std::string &str, const std::string &prefix) {
  std::smatch match;
  std::regex_search(str, match, std::regex("^" + prefix));
  return match.suffix();
}
查看更多
做个烂人
4楼-- · 2019-01-12 18:57

With C++17 you can use std::basic_string_view & with C++20 std::basic_string::starts_with or std::basic_string_view::starts_with.

The benefit of std::string_view in comparison to std::string - regarding memory management - is that it only holds a pointer to a "string" (contiguous sequence of char-like objects) and knows its size. Example without moving/copying the source strings just to get the integer value:

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

const char * argument = "--foo=42"; // Emulating command argument.
const char * argumentPrefix = "--foo";
int inputValue = 0;

std::string_view argView = argument;
if (argView.starts_with(argumentPrefix))
{
    std::string_view prefixView = argumentPrefix; // Helper for getting the size of argumentPrefix.
    try
    {
        // The underlying data of argView is nul-terminated, therefore we can use data().
        inputValue = std::atoi(argView.substr(prefixView.size() + 1).data());
    }
    catch (std::exception& e)
    {
        std::cerr << e.what();
    }
}
查看更多
时光不老,我们不散
5楼-- · 2019-01-12 18:58

At the risk of being flamed for using C constructs, I do think this sscanf example is more elegant than most Boost solutions. And you don't have to worry about linkage if you're running anywhere that has a Python interpreter!

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

Here's some example output that demonstrates the solution handles leading/trailing garbage as correctly as the equivalent Python code, and more correctly than anything using atoi (which will erroneously ignore a non-numeric suffix).

$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number
查看更多
ゆ 、 Hurt°
6楼-- · 2019-01-12 18:59

Given that both strings — argv[1] and "--foo" — are C strings, @FelixDombek's answer is hands-down the best solution.

Seeing the other answers, however, I thought it worth noting that, if your text is already available as a std::string, then a simple, zero-copy, maximally efficient solution exists that hasn't been mentioned so far:

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

And if foo is already a string:

std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(foo.length());
查看更多
Deceive 欺骗
7楼-- · 2019-01-12 18:59

Ok why the complicated use of libraries and stuff? C++ String objects overload the [] operator, so you can just compare chars.. Like what I just did, because I want to list all files in a directory and ignore invisible files and the .. and . pseudofiles.

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

It's that simple..

查看更多
登录 后发表回答