Как проверить, начинается ли C++ с определенной строки, и преобразовать подстроку в int?
Как сделать следующее (псевдокод Python) в C++?
if argv[1].startswith('--foo='):
foo_value = int(argv[1][len('--foo='):])
(например, если argv[1] '--foo=98', то foo_value равно 98.)
обновление: Я не решаюсь заглянуть в Boost, так как я просто смотрю на внесение очень небольшого изменения в простой инструмент командной строки. (Я бы предпочел не учиться подключаться и использовать Boost для незначительных изменений.)
19 ответов
Если вы используете Boost, вы можете сделать это с помощью повысить строковые алгоритмы + повысить лексический состав:
#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
}
Как и большинство библиотек boost, строковый алгоритм и лексическое приведение являются заголовочными, нет ничего, чтобы связать.
такой подход, как и многие другие ответы, приведенные здесь, подходит для очень простых задач, но в долгосрочной перспективе вам обычно лучше использовать библиотеку синтаксического анализа командной строки. Импульс имеет одну (импульс.Program_options), что может иметь смысл, если вы уже используете Boost.
в противном случае поиск "синтаксического анализатора командной строки c++" даст несколько вариантов.
вы бы сделали это так:
std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
foo_value = atoi(arg.substr(prefix.size()).c_str());
Поиск lib, такого как Boost.ProgramOptions, что делает это для вас тоже хорошая идея.
просто для полноты, я упомяну способ C сделать это:
если
str
ваша исходная строка,substr
подстрока вы хотите проверьте, тогда
strncmp(str, substr, strlen(substr))
вернутся
0
еслиstr
начинается сsubstr
. Функцииstrncmp
иstrlen
находятся в C заголовочный файл<string.h>
(первоначально опубликовано Yaseen Rauf здесь, разметки добавлено)
Для сравнения без учета регистра, используйте strnicmp
вместо strncmp
.
это способ C сделать это, для строк C++ вы можете использовать ту же функцию, что и эта:
strncmp(str.c_str(), substr.c_str(), substr.size())
std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) {
// s starts with prefix
}
кому еще что-нибудь нужно? Чистый STL!
код, который я использую сам:
std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
std::string argumentValue = argument.substr(prefix.size());
}
учитывая, что обе строки - argv[1]
и "--foo"
- Это строки C,@FelixDombek это это hands-down лучшее решение.
видя другие ответы, однако, я подумал, что стоит отметить, что, если ваш текст уже доступен как std::string
, тогда существует простое, нулевое, максимально эффективное решение, которое до сих пор не упоминалось:
const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
foo_value = text.substr(strlen(foo));
и если foo уже строка:
std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
foo_value = text.substr(foo.length());
рискуя вспыхнуть за использование конструкций C, я думаю, что это sscanf
пример более элегантен, чем большинство решений Boost. И вам не нужно беспокоиться о связи, если вы работаете в любом месте, где есть интерпретатор 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;
}
вот пример вывода, который демонстрирует, что решение обрабатывает ведущий / конечный мусор так же правильно, как эквивалентный код Python, и более правильно, чем что-либо с помощью atoi
(который ошибочно будет игнорировать нечисловой суффикс.)
$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number
использование STL это может выглядеть так:
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;
}
почему бы не использовать gnu getopts? Вот основной пример (без проверки безопасности):
#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);
}
для следующей команды:
$ ./a.out --foo=33
вы получаете
33
Я использую std::string::compare
обернутый в метод утилиты, как показано ниже:
static bool startsWith(const string& s, const string& prefix) {
return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}
вы также можете использовать strstr
:
if (strstr(str, substr) == substr) {
// 'str' starts with 'substr'
}
но я думаю, что это хорошо только для коротких строк, потому что он должен проходить через всю строку, когда строка фактически не начинается с "substr".
Ok почему сложное использование библиотек и прочее? Строковые объекты C++ перегружают оператор [], поэтому вы можете просто сравнить символы.. Как и то, что я только что сделал, потому что я хочу перечислить все файлы в каталоге и игнорировать невидимые файлы и т. д.. и. псевдофайлов.
while ((ep = readdir(dp)))
{
string s(ep->d_name);
if(!(s[0] == '.')) // Omit invisible files and .. or .
files.push_back(s);
}
Это очень просто..
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;
}
С C++17 вы можете использовать std::basic_string_view
& С C++20 std::basic_string::starts_with
или std::basic_string_view::starts_with
.
пользу std::string_view
по сравнению с std::string
- Что касается управления памятью-это то, что он содержит только указатель на "строку" (непрерывную последовательность char-подобных объектов) и знает ее размер. Пример без перемещения / копирования исходных строк, чтобы получить целочисленное значение:
#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 лет опоздал на вечеринку, вот самый простой, DIY способ сделать это:
#include <iostream>
#include <string>
using namespace std; // for clarity's sake.
bool startswith(string prefix, string text) {
if (prefix.length() > 0 && text.length() > prefix.length()) {
int i = 0;
while (i < prefix.length()) {
if (text[i] != prefix[i]) return false;
i++;
}
return true;
}
else return false;
}
int main (int argc, char* argv) {
if(startswith("--foo=", argv[1]))
// do something about it!
}
Так как C++11 также std:: regex_search может использоваться, например, следующим образом (возвращает пустую строку при сбое):
#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();
}
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()));
Это совершенно непроверенные. Принцип тот же, что и у Python. Требуется Boost.StringAlgo и Boost.Лексикаст.
Проверьте, начинается ли строка с другой строки, а затем получите подстроку ('slice') первой строки и преобразуйте ее с помощью лексического приведения.