web-dev-qa-db-fra.com

Comment vérifier si une chaîne std :: string C ++ commence par une chaîne donnée et convertit une sous-chaîne en un entier?

Comment implémenter les éléments suivants (pseudocode Python) en C++?

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

(Par exemple, si argv[1] est --foo=98, alors foo_value est 98.)

Mise à jour: J’hésite à me tourner vers Boost car je ne fais que modifier très légèrement un simple outil en ligne de commande (je préférerais ne pas avoir à apprendre à relier et utilisez Boost pour un changement mineur).

202
Daryl Spitzer

Si vous utilisez déjà Boost, vous pouvez le faire avec algorithmes de renforcement de chaîne + 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
}

Ce type d'approche, comme beaucoup d'autres réponses fournies ici, convient pour des tâches très simples, mais à long terme, il est généralement préférable d'utiliser une bibliothèque d'analyse en ligne de commande. Boost en a un ( Boost.Program_options ), ce qui peut avoir un sens si vous utilisez déjà Boost.

Sinon, une recherche sur "analyseur de ligne de commande c ++" donnera plusieurs options.

85
Ferruccio
std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
  // s starts with prefix
}

Qui a besoin de quelque chose d'autre? STL pure!

299
Ludovic Aubert

Vous le feriez comme ceci:

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

Rechercher une bibliothèque telle que Boost.ProgramOptions qui le fait pour vous est également une bonne idée.

179
Thomas

Juste pour être complet, je vais mentionner la manière de faire C:

Si str est votre chaîne d'origine, substr est la sous-chaîne que vous souhaitez vérifier, puis

strncmp(str, substr, strlen(substr))

retournera 0 si str commence par substr. Les fonctions strncmp et strlen se trouvent dans le fichier d’en-tête C <string.h>

(posté à l'origine par Yaseen Rauf ici , balise ajoutée)

Pour une comparaison insensible à la casse, utilisez strnicmp au lieu de strncmp.

C’est la manière de le faire en C; pour les chaînes C++, vous pouvez utiliser la même fonction, comme ceci:

strncmp(str.c_str(), substr.c_str(), substr.size())
137
Felix Dombek

Code je m'utilise moi-même:

std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
    std::string argumentValue = argument.substr(prefix.size());
}
79
Hüseyin Yağlı

Personne n'a encore utilisé la fonction STL algorithme/décalage . Si cela retourne true, prefix est le préfixe 'toCheck':

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

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

Modifier:

Comme @James T. Huggett le suggère, std :: equal convient mieux à la question: A est-il un préfixe de B? et est-il légèrement plus court:

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

Exemple complet 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::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;
  }
}
45
matiu

Étant donné que les deux chaînes - argv[1] et "--foo" - sont des chaînes de caractères C, la réponse de @ FelixDombek est de loin la meilleure solution.

Voyant les autres réponses, cependant, j’ai pensé que cela valait la peine de noter que, si votre texte est déjà disponible en tant que std::string, il existe alors une solution simple, sans copie, extrêmement efficace et qui n’a pas encore été mentionnée:

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

Et si foo est déjà une chaîne:

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

En utilisant STL, cela pourrait ressembler à:

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;
}
11
razvanco13
text.substr(0, start.length()) == start
9
Macsinus

Au risque d’être enflammé pour avoir utilisé des constructions C, je pense que cet exemple sscanf est plus élégant que la plupart des solutions Boost. Et vous n'avez pas à vous soucier des liens si vous utilisez un interpréteur 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;
}

Voici un exemple de sortie qui montre que la solution traite correctement les déchets de menuiserie de fin/fin, comme le code équivalent Python, et plus correctement que tout ce qui utilise atoi (qui ignorera par erreur un suffixe non numérique).

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

Avec C++ 17, vous pouvez utiliser std::basic_string_view & avec C++ 20 std::basic_string::starts_with ou std::basic_string_view::starts_with =.

L'avantage de _std::string_view_ par rapport à _std::string_ - en ce qui concerne la gestion de la mémoire - est qu'il ne contient qu'un pointeur sur une "chaîne" (séquence contiguë d'objets de type caractère) et qu'il connaît sa taille. Exemple sans déplacer/copier les chaînes source juste pour obtenir la valeur entière:

_#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();
    }
}
_
8
Roi Danton

J'utilise std::string::compare enveloppé dans une méthode utilitaire comme ci-dessous:

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

Pourquoi ne pas utiliser Gnu Getopts? Voici un exemple de base (sans contrôles de sécurité):

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

Pour la commande suivante:

$ ./a.out --foo=33

Tu auras

33
5
Carl Cook

Si vous avez besoin de la compatibilité C++ 11 et que vous ne pouvez pas utiliser boost, voici un exemple d'installation compatible avec ce dernier avec un exemple d'utilisation:

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

Ok, pourquoi l'utilisation compliquée de bibliothèques et d'autres choses? Les objets C++ String surchargent l'opérateur [], vous pouvez donc simplement comparer les caractères .. Comme ce que je viens de faire, car je veux lister tous les fichiers d'un répertoire et ignorer les fichiers invisibles et les .. et. pseudofiles.

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

C'est si simple..

2
Nils

Vous pouvez également utiliser strstr:

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

mais je pense que ce n'est utile que pour les chaînes courtes car il doit parcourir toute la chaîne lorsque celle-ci ne commence pas par 'substr'.

2
szx
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;
}
1
mois

Depuis C++ 11 également std :: regex_search peut être utilisé, par exemple. comme suit (retourne une chaîne vide en cas d'échec):

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

Avec C++ 11 ou supérieur, vous pouvez utiliser find() et find_first_of()

Exemple utilisant find pour trouver un seul caractère:

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

Exemple utilisant find pour trouver une chaîne complète et commençant à la position 5:

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

Exemple utilisant la find_first_of() et uniquement le premier caractère, pour rechercher au début uniquement:

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

Bonne chance!

1
danger89