How do I check if a C++ std::string starts with a certain string, and convert a substring to an int?

406,872

Solution 1

Use rfind overload that takes the search position pos parameter, and pass zero for it:

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

Who needs anything else? Pure STL!

Many have misread this to mean "search backwards through the whole string looking for the prefix". That would give the wrong result (e.g. string("tititito").rfind("titi") returns 2 so when compared against == 0 would return false) and it would be inefficient (looking through the whole string instead of just the start). But it does not do that because it passes the pos parameter as 0, which limits the search to only match at that position or earlier. For example:

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)

Solution 2

You would do it like this:

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

Looking for a lib such as Boost.ProgramOptions that does this for you is also a good idea.

Solution 3

Just for completeness, I will mention the C way to do it:

If str is your original string, substr is the substring you want to check, then

strncmp(str, substr, strlen(substr))

will return 0 if str starts with substr. The functions strncmp and strlen are in the C header file <string.h>

(originally posted by Yaseen Rauf here, markup added)

For a case-insensitive comparison, use strnicmp instead of strncmp.

This is the C way to do it, for C++ strings you can use the same function like this:

strncmp(str.c_str(), substr.c_str(), substr.size())

Solution 4

If you're already using Boost, you can do it with boost string algorithms + 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
}

This kind of approach, like many of the other answers provided here is ok for very simple tasks, but in the long run you are usually better off using a command line parsing library. Boost has one (Boost.Program_options), which may make sense if you happen to be using Boost already.

Otherwise a search for "c++ command line parser" will yield a number of options.

Solution 5

Code I use myself:

std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
    std::string argumentValue = argument.substr(prefix.size());
}
Share:
406,872
Daryl Spitzer
Author by

Daryl Spitzer

Father of three, husband, computer programmer (Pythonista), skeptic, atheist, podcast listener, baseball fan, Canadian (in the United States).

Updated on July 08, 2022

Comments

  • Daryl Spitzer
    Daryl Spitzer almost 2 years

    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).

  • Tom
    Tom over 14 years
    The biggest problem with this is that atoi("123xyz") returns 123, whereas Python's int("123xyz") throws an exception.
  • Roopesh Majeti
    Roopesh Majeti over 14 years
    The workaround, we can do, is to a sscanf() and compare the result and the original, to decide whether to proceed or throw exception.
  • Tom
    Tom over 14 years
    Or just replace atoi with strtol or strtoll, which lets us detect error conditions in the input value.
  • Keith Thompson
    Keith Thompson over 12 years
    If argv[i] is "--foo=9999999999999999999999999", the behavior is undefined (though most or all implementations should behave sanely). I'm assuming 9999999999999999999999999 > INT_MAX.
  • Ben Bryant
    Ben Bryant about 12 years
    the most concise and only depends on std::string, except remove the optional and misleading argument.size() at the end of the final substr.
  • Hüseyin Yağlı
    Hüseyin Yağlı about 12 years
    @ben-bryant: Thanks for the heads up. Didn't know it was optional.
  • cyrilchampier
    cyrilchampier almost 12 years
    "boost::algorithm::starts_with" instead of "boost::starts_with" ?
  • Ferruccio
    Ferruccio almost 12 years
    @nerith: Seems they both work. I've always used the shorter form.
  • Jared Grubb
    Jared Grubb over 11 years
    That should be if (prefix.size()<=arg.size() && std::equal(...)).
  • Alex Bitek
    Alex Bitek over 11 years
    boost::starts_with and boost::algorithm::starts_with are the same thing. If you look at the end of this file svn.boost.org/svn/boost/trunk/boost/algorithm/string/… you will see this: // pull names to the boost namespace using algorithm::starts_with; [...]
  • Felix Dombek
    Felix Dombek over 10 years
    Using substr leads to unnecessary copying. The str.compare(start, count, substr) method used in Thomas' answer is more efficient. razvanco13's answer has another method which avoids copying by using std::equal.
  • robertwb
    robertwb almost 10 years
  • Brice M. Dempsey
    Brice M. Dempsey almost 9 years
    Why not use std::equal?
  • matiu
    matiu almost 9 years
    Sounds good to me. It would be shorter code too. I spose, I'll have to edit the answer now :p
  • Parthian Shot
    Parthian Shot over 8 years
    @HüseyinYağlı Thomas uses atoi which is only for windows Huh? atoi has been a C standard library function since... ever. In point of fact, atoi is bad- not because it's Windows-specific- but because it's (1) C, not C++, and (2) deprecated even in C (you should be using strtol or one of the other, related functions. Because atoi has no error handling. But, again, that's only in C, anyway).
  • Tobi
    Tobi almost 8 years
    Pulling in huge dependencies for a string prefix check is like shooting birds with canons.
  • Felix Dombek
    Felix Dombek almost 8 years
    Using std::equal for strings has the downside that it doesn't detect the string end, so you need to manually check whether the prefix is shorter than the whole string. (As correctly done in the example prog, but omitted in the one-liner above.)
  • porges
    porges almost 8 years
    rfind(x, 0) == 0 should really be defined in the standard as starts_with
  • Glenn Maynard
    Glenn Maynard over 7 years
    "Use Boost" is always the wrong answer when someone asks how to do a simple string operation in C++.
  • uglycoyote
    uglycoyote over 7 years
    minus 1 for suggesting Boost
  • osjerick
    osjerick about 7 years
    Using boost here is right, if you already use boost in your project.
  • paxdiablo
    paxdiablo almost 7 years
    @sweisgerber.dev, I'm confused on your first contention. The return value from find will only be zero if titi is at the start of the string. If it's found somewhere else, you'll get a non-zero return value and, if it's not found, you'll get npos which is also non-zero. Assuming I'm right, I'd prefer this answer since I don't have to bring in any non-standard stuff (yes, I know Boost is everywhere, I'd just prefer core C++ libs for simple stuff like this).
  • sweisgerber.dev
    sweisgerber.dev almost 7 years
    @paxdiablo: you are right, it does indeed check if it starts with titi, but the conversion part is missing.
  • Malakai
    Malakai over 6 years
    It would be great, if you avoid pasting code without code explanation. Thank you.
  • Андрей Вахрушев
    Андрей Вахрушев over 6 years
    So, no benefit over rfind?
  • qwattash
    qwattash over 6 years
    Using boost for this is wrong if this is the reason for which you would decide to link boost in the first place, if you are already using boost for other reasons I don't see why this would be huge a problem.
  • NuSkooler
    NuSkooler over 6 years
    The answer is prefixed with "If you're using Boost...". Clearly this is the right answer "...if you're using Boost". If not, look the suggestion by @Thomas
  • etarion
    etarion about 6 years
    @GregorDoroschenko it does answer the "check if string starts with another" part.
  • ankostis
    ankostis about 6 years
    Inefficient code, would continue searching past the start of the string.
  • ankostis
    ankostis about 6 years
    No, because rfind() (in place of startswith()) is very inefficient - it keeps searching till the end of the string.
  • Force Gaia
    Force Gaia about 6 years
    indeed, everyone seems to just go "use boost" and i for one am thankful for an stl or OS library version
  • Michael B
    Michael B about 5 years
    Efficient and elegant using std::string. I learnt the most from this.
  • Superziyi
    Superziyi about 5 years
    Do we have any evidence that this is optimized in most compilers? I don't find elsewhere mentioning either "find" or "rfind" optimization is common practice based on the return value it's checking against.
  • Adam.at.Epsilon
    Adam.at.Epsilon about 5 years
    extra points for being a one-liner suitable for use with if (one-liner)
  • Ayxan Haqverdili
    Ayxan Haqverdili almost 5 years
    @robertwb Google+ is no longer available
  • Anonymous Coward
    Anonymous Coward almost 5 years
    @ankostis rfind(x) searchs from the end till the start until it finds x, indeed. But rfind(x,0) starts searching from the start (position=0) till the start; so it only searches where it needs searching; does not search from/till the end.
  • Avishai Y
    Avishai Y almost 5 years
    Yes. However, it assumes the string has no null characters in it. If it is not the case - one should use memcmp()
  • Calmarius
    Calmarius over 4 years
    This is better solution than the rfind one which depends on optimization to work.
  • Adham Zahran
    Adham Zahran over 4 years
    why would anyone use anything other than this simple beautiful solution?
  • Roi Danton
    Roi Danton over 4 years
    @RolandIllig No, std::atoi is completely fine. It throws exceptions on bad input (which is handled in this code). Did you have something else in mind?
  • Roland Illig
    Roland Illig over 4 years
    Are you talking about the atoi from <cstdlib>? The documentation says "it never throws exceptions".
  • Roi Danton
    Roi Danton over 4 years
    @RolandIllig I'm referring to your first comment. It seems, you are mistakenly talking about atoi instead of std::atoi. The first is unsafe to use, while the latter is fine. I'm using the latter in the code here.
  • Roland Illig
    Roland Illig over 4 years
    Please prove to me that std::atoi indeed throws an exception, by citing a suitable reference. Until you do, I don't believe you since it would be very confusing to have both ::atoi and std::atoi acting in a completely different way.
  • Roi Danton
    Roi Danton over 4 years
    @RolandIllig Thanks for being persistent! You are right, it was an oversight that std::atoi was used instead of std::stoi. I've fixed that.
  • user2864740
    user2864740 over 4 years
    Why not rfind? rfind(str, 0) will not needlessly scan an entire string to make a selection as it cannot advance. See others.
  • Macsinus
    Macsinus over 4 years
    @Roland Illig Why do you believe that the behaviour in that case is undefined? The expression will return false because substr returns a string of the same length as text according to en.cppreference.com/w/cpp/string/basic_string/substr
  • Arthur Tacca
    Arthur Tacca almost 4 years
    @alcoforado "rfind will start from the back of the string ..." No, that only applies to the overload of rfind() that does not take a pos parameter. If you use the overload that does take a pos parameter then it will not search the whole string, only that position and earlier. (Just like regular find() with pos parameter only looks in that position or later.) So if you pass pos == 0, as shown in this answer, then it will literally only consider for matches at that one position. That was already explaining in both the answer and comments.
  • brita_
    brita_ almost 4 years
    This answer is the most modern way to do it. +1 for the use of string_view. Thanks for such a curated and robust code sample!
  • Yuval
    Yuval over 3 years
    @Calmarius the rfind solution does not depend on any optimization. rfind's behavior by definition is to only look at a single index when given pos=0, hence it is always an efficient check. Which syntax is more pleasant is a matter of preference.
  • Alexis Paques
    Alexis Paques over 3 years
    I like to use ::compare, which gives identical result: return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0;
  • Jepessen
    Jepessen over 3 years
    Boost is always a solution if you use C++. There's no need to reinvent the wheel when there's no need to do it. We work, we are not doing homework assigments.
  • Jepessen
    Jepessen over 3 years
    Boost is not a BIG library.. is a set of libraries big and smalls. If a small boost library can solve the problem I don't see the reason to avoid it.
  • Aleksandr
    Aleksandr almost 3 years
    I was going to post the same thing. @Reborn Simple explanation: - This is the equivalent of saying if string start is found inside of string text at index 0 go into the if statement. - More specifically std::string find returns the first index of the original std::string that matches the string in parentheses. When it is equal to 0 it is the start of the string.
  • Aleksandr
    Aleksandr almost 3 years
    @ankostis this code would be very efficient since it is optimized by the compiler and improved in subsequent compiler enhancements.
  • Vishal Sahu
    Vishal Sahu over 2 years
    that position or earlier is the important phrase here.