Как проверить, начинается ли C++ std :: string с определенной строки, и преобразовать подстроку в int?

Как мне реализовать следующее (псевдокод Python) в C++?

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

(Например, если argv[1] равно --foo=98, тогда foo_value равно 98)

Обновление: я не решаюсь взглянуть на Boost, так как я просто смотрю на внесение очень небольших изменений в простой маленький инструмент командной строки (я бы предпочел не изучать, как связать и использовать Boost для незначительного менять).

Ответ 1

Если вы уже используете Boost, вы можете сделать это с помощью алгоритмов boost string + 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
}

Такой подход, как и многие другие ответы, представленные здесь, подходит для очень простых задач, но в долгосрочной перспективе обычно лучше использовать библиотеку синтаксического анализа командной строки. Boost имеет один (Boost.Program_options), который может иметь смысл, если вы уже используете Boost.

В противном случае поиск "синтаксического анализатора командной строки c++" даст ряд опций.

Ответ 2

Используйте перегрузку rfind с параметром pos. Здесь pos является последним индексом, который будет считаться частью предмета поиска (поэтому, если ваша строка будет titititi, последняя titi не будет рассматриваться, поскольку она начинается позже, чем выбранный pos=0):

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

Кому еще что-нибудь нужно? Чистый STL!

Ответ 3

Вы бы сделали это так:

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

Поиск библиотеки, такой как Boost.ProgramOptions, которая делает это для вас, также является хорошей идеей.

Ответ 4

Просто для полноты я упомяну способ C сделать это:

Если str - ваша исходная строка, substr - подстрока, которую вы хотите проверить, тогда

strncmp(str, substr, strlen(substr))

вернет 0 если str начинается с substr. Функции strncmp и strlen находятся в заголовочном файле C <string.h>

(первоначально размещено здесь Ясином Рауфом, добавлена разметка)

Для сравнения без strnicmp регистра используйте strnicmp вместо strncmp.

Это способ C сделать это, для строк C++ вы можете использовать ту же функцию, например:

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

Ответ 5

Код, который я использую сам:

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

Ответ 6

Никто еще не использовал функцию STL algorithm/mismatch. Если это возвращает true, префикс является префиксом "toCheck":

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

Полный пример:

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

Edit:

Как говорит @James T. Huggett, std:: equal лучше подходит для вопроса: является ли префиксом B? и имеет небольшой короткий код:

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

Полный пример:

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

Ответ 7

Учитывая, что обе строки - argv[1] и "--foo" - являются строками C, @FelixDombek answer - лучшее решение.

Однако, учитывая другие ответы, я подумал, что стоит отметить, что если ваш текст уже доступен как 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());

Ответ 8

Использование 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;
}

Ответ 9

Из-за риска пламени для использования конструкций 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

Ответ 10

Я использую std::string::compare в служебном методе, как показано ниже:

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

Ответ 11

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

Ответ 12

С С++ 17 вы можете использовать std::basic_string_view & с С++ 20 std::basic_string::starts_with или std::basic_string_view::starts_with.

Преимущество std::string_view по сравнению с std::string - в отношении управления памятью - заключается в том, что он содержит только указатель на "строку" (непрерывную последовательность символов, подобных символу) и знает ее размер. Пример без перемещения/копирования исходных строк только для получения целочисленного значения:

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

Ответ 13

Почему бы не использовать 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

Ответ 14

В случае, если вам нужна совместимость с С++ 11 и вы не можете использовать boost, вот совместимое с boost усиление с примером использования:

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

Ответ 15

Хорошо, почему сложное использование библиотек и прочего? C++ Строковые объекты перегружают оператор [], так что вы можете просто сравнивать символы.. Как и то, что я только что сделал, потому что я хочу перечислить все файлы в каталоге и игнорировать невидимые файлы и... и. pseudofiles.

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

Это так просто..

Ответ 16

Вы также можете использовать strstr:

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

но я думаю, что это хорошо только для коротких строк, потому что он должен перебирать всю строку, когда строка фактически не начинается с 'substr'.

Ответ 17

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

Ответ 18

хотя я опаздываю на вечеринку на 8 лет, вот простейший способ сделать это:

#include <iostream>
#include <string>

using namespace std; // for clarity 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!
}

Ответ 19

Поскольку в С++ 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();
}

Ответ 20

С С++ 11 или выше вы можете использовать find() и find_first_of()

Пример использования find для поиска одного символа:

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

Пример использования find для поиска полной строки и начиная с позиции 5:

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

Пример использования find_first_of() и только первого символа для поиска только в начале:

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

Удачи!

Ответ 21

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.LexicalCast.

Проверить, начинается ли строка с другой строкой, а затем получить подстроку ('slice') первой строки и преобразовать ее с помощью лексического перевода.