it-swarm.com.ru

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

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

191
Daryl Spitzer

Если вы уже используете 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 имеет один ( Boost.Program_options ), что может иметь смысл, если вы уже используете Boost.

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

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

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

249
user3046585

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

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

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

177
Thomas

Просто для полноты я упомяну способ 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())
133
Felix Dombek

Код я использую сам:

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

Никто еще не использовал функцию STL алгоритм/несоответствие . Если это возвращает 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 лучше подходит для вопроса: Является ли A префиксом 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;
  }
}
44
matiu

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

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

При использовании 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;
}
11
razvanco13
text.substr(0, start.length()) == start
8
Macsinus

С риском для того, чтобы быть воспламененным для использования конструкций 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
8
Tom

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

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

#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

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

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

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

В случае, если вам нужна совместимость с C++ 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;
}
3
vallismortis

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

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

но я думаю, что это хорошо только для коротких строк, потому что он должен перебирать всю строку, когда строка на самом деле не начинается с '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

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

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

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

1
Nils

Поскольку 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();
}
1
alextoind

С C++ 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
}

Удачи!

0
danger89