Как проверить, начинается ли 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());
}

никто не использовал STL алгоритм/несоответствие


учитывая, что обе строки - 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;
}

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

С 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') первой строки и преобразуйте ее с помощью лексического приведения.