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).
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.
std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
// s starts with prefix
}
Qui a besoin de quelque chose d'autre? STL pure!
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.
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
sistr
commence parsubstr
. Les fonctionsstrncmp
etstrlen
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())
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());
}
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;
}
}
É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());
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;
}
text.substr(0, start.length()) == start
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
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();
}
}
_
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;
}
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
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;
}
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..
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'.
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;
}
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();
}
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!