Hoe controleer ik of een C++ STD :: String begint met een bepaalde string en een substring omzet naar een int?

Hoe implementeer ik het volgende (Python Pseudocode) in C++?

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

(Bijvoorbeeld als argv[1]--foo=98is, dan foo_valueis 98.)

Update: Ik ben aarzelend om in boost te kijken, omdat ik gewoon kijk naar een zeer kleine verandering in een eenvoudige kleine opdrachtregelgereedschap (ik hoef liever niet te leren Hoe u kunt koppelen en boost gebruiken voor een kleine wijziging).


Antwoord 1, Autoriteit 100%

Gebruik rfindoverbelasting die de Zoeken Positie posParameter, en passeer nul voor IT:

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

wie heeft iets anders nodig? Pure stl!

Velen hebben dit verkeerd gelezen om “naar achteren door de hele reeks te zoeken naar het voorvoegsel”. Dat zou het verkeerde resultaat geven (bijv. string("tititito").rfind("titi")RETOURNEREN 2 Dus in vergelijking met == 0zou false retourneren) en het zou inefficiënt zijn (kijkend door de hele reeks in plaats van alleen het begin). Maar het doet dat niet, omdat het de posparameter als 0passeert, die de zoekopdracht beperkt tot alleen overeenkomen op die positie of eerder . Bijvoorbeeld:

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)

Antwoord 2, Autoriteit 31%

u zou het zo doen:

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

Op zoek naar een lib, zoals boost.programopties die dit voor jou ook doet, is ook een goed idee.


Antwoord 3, Autoriteit 25%

Alleen voor volledigheid, zal ik de C-manier noemen om het te doen:

Indien stris uw originele string, substris de substring die u wilt
Controleer, dan

strncmp(str, substr, strlen(substr))

retourneert 0indien str
Begint met substr. De functies strncmpEN strlenZIJN IN DE C
Header-bestand <string.h>

(Oorspronkelijk gepost door Yaseen Rauf Hier , markeringen toegevoegd)

Gebruik voor een geval-ongevoelige vergelijking strnicmpin plaats van strncmp.

Dit is de c-manier om het te doen, voor C++ -snaren kunt u dezelfde functie gebruiken als volgt:

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

Antwoord 4, Autoriteit 14%

Als u al Boost gebruikt, kunt u het doen met Boost String Algoritmen + 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
}

Dit soort aanpak, zoals veel van de andere antwoorden hier is OK voor zeer eenvoudige taken, maar op de lange termijn bent u meestal beter af met behulp van een opdrachtregel Parring-bibliotheek. Boost heeft één (boost.program_options ), wat logisch zijn als u toevallig al boost gebruikt.

Anders zal een zoekopdracht naar “C++ -opdrachtregel-parser” een aantal opties opleveren.


Antwoord 5, Autoriteit 13%

code ik gebruik mezelf:

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

Antwoord 6, Autoriteit 8%

Niemand heeft de STL algoritme / mismatch -functie nog steeds gebruikt. Als dit true retourneert, is voorvoegsel een voorvoegsel van ‘Tocheck’:

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

Volledig voorbeeld PROG:

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

Bewerken:

Zoals @James T. Huggett suggereert, past std::equal beter bij de vraag: Is A een prefix van B?en is een iets kortere code:

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

Volledig voorbeeldprogramma:

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

Antwoord 7, autoriteit 4%

Aangezien beide strings — argv[1]en "--foo"— C-strings zijn, @FelixDombeks antwoordis zonder meer de beste oplossing.

Toen ik de andere antwoorden zag, vond ik het echter de moeite waard om op te merken dat, als uw tekst al beschikbaar is als een std::string, er een eenvoudige, maximaal efficiënte oplossing bestaat die is tot nu toe niet genoemd:

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

En als foo al een string is:

std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
    foo_value = text.substr(foo.length());

Antwoord 8, autoriteit 4%

Met C++17 kun je std::basic_string_view& met C++20 std::basic_string::starts_withof std::basic_string_view::starts_with.

Het voordeel van std::string_viewin vergelijking met std::string– met betrekking tot geheugenbeheer – is dat het alleen een verwijzing naar een “string” bevat (aaneengesloten opeenvolging van char-achtige objecten) en kent de grootte ervan. Voorbeeld zonder de bronstrings te verplaatsen/kopiëren om de integerwaarde te krijgen:

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

Antwoord 9, autoriteit 2%

text.substr(0, start.length()) == start

Antwoord 10, autoriteit 2%

Op het gevaar af te worden gevlamd voor het gebruik van C-constructies, denk ik dat dit sscanf-voorbeeld eleganter is dan de meeste Boost-oplossingen. En u hoeft zich geen zorgen te maken over koppeling als u ergens werkt met een 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;
}

Hier is een voorbeelduitvoer die laat zien dat de oplossing voorloop-/achtervolging-rommel even correct afhandelt als de equivalente Python-code, en correcter dan wat dan ook met behulp van atoi(die ten onrechte een niet-numeriek achtervoegsel negeert).

$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number

Antwoord 11, autoriteit 2%

Met STL kan dit er als volgt uitzien:

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

Antwoord 12, autoriteit 2%

Ik gebruik std::string::compareverpakt in een hulpprogramma-methode zoals hieronder:

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

Antwoord 13

Vanaf C++20 kunt u de methode starts_withgebruiken.

std::string s = "abcd";
if (s.starts_with("abc")) {
    ...
}

Antwoord 14

In C++20 is nu starts_withbeschikbaar als lidfunctie van std::stringgedefinieerd als:

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;

Dus je code zou er ongeveer zo uit kunnen zien:

std::string s{argv[1]};
if (s.starts_with("--foo="))

Antwoord 15

Als je C++11-compatibiliteit nodig hebt en boost niet kunt gebruiken, is hier een boost-compatibele drop-in met een gebruiksvoorbeeld:

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

Antwoord 16

Waarom geen gnu getopts gebruiken? Hier is een eenvoudig voorbeeld (zonder veiligheidscontroles):

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

Voor de volgende opdracht:

$ ./a.out --foo=33

Je krijgt

33

Antwoord 17

C++20-update:

  • Gebruik 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"});

Antwoord 18

U kunt ook strstr:

gebruiken

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

Maar ik denk dat het alleen goed is voor korte snaren omdat het door de hele reeks moet worden geleid wanneer de string niet echt begint met ‘substr’.


Antwoord 19

OK Waarom het gecompliceerde gebruik van bibliotheken en spullen? C++ String Objects Overbelast de [] -exploitant, zodat u nu Chars kunt vergelijken .. Zoals wat ik gewoon deed, omdat ik alle bestanden in een map wil opgeven en onzichtbare bestanden en de .. en. pseudofiles.

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

Het is zo eenvoudig ..


Antwoord 20

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

Antwoord 21

Met C++ 11 of hoger kunt u find()en find_first_of()

gebruiken

Voorbeeld met behulp van het vinden om een ​​enkele char te vinden:

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

Voorbeeld van het gebruik van find om een volledige string te vinden & vanaf positie 5:

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

Voorbeeld van het gebruik van de find_first_of()en alleen het eerste teken, om alleen aan het begin te zoeken:

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

Veel succes!


Antwoord 22

Sinds C++11 std::regex_searchkan ook worden gebruikt om nog complexere expressies te matchen. Het volgende voorbeeld behandelt ook zwevende getallen via std::stofen een daaropvolgende cast naar int.

De onderstaande parseInt-methode kan echter een std::invalid_argumentuitzondering als het voorvoegsel niet overeenkomt; dit kan gemakkelijk worden aangepast, afhankelijk van de gegeven toepassing:

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

Het soort magie van het regex-patroon wordt goed beschreven in de volgende antwoord.

EDIT:het vorige antwoord heeft de conversie naar integer niet uitgevoerd.


Antwoord 23

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()));

Dit is volledig niet getest. Het principe is hetzelfde als dat van Python. Vereist Boost.StringAlgo en Boost.LexicalCast.

Controleer of de tekenreeks begint met de andere tekenreeks, en verkrijg dan de subtekenreeks (‘slice’) van de eerste tekenreeks en converteer deze met lexicale cast.

Other episodes