Подтвердить что ты не робот

С++ Извлечь число из середины строки

У меня есть vector, содержащий strings, который соответствует формату text_number-number

Например: Example_45-3

Мне нужно только первое число (45 в примере) и ничего другого, что я могу сделать с моим текущим кодом:

std::vector<std::string> imgNumStrVec;
for(size_t i = 0; i < StrVec.size(); i++){
    std::vector<std::string> seglist;
    std::stringstream ss(StrVec[i]);
    std::string seg, seg2;
    while(std::getline(ss, seg, '_')) seglist.push_back(seg);
    std::stringstream ss2(seglist[1]);
    std::getline(ss2, seg2, '-');
    imgNumStrVec.push_back(seg2); 
}

Есть ли более простые и простые способы сделать это? и если да, то каковы они?

Я прошу чисто из желания узнать, как лучше кода лучше, чем в конце дня, приведенный выше код успешно извлекает только первое число, но кажется, что он длинный и округлый.

4b9b3361

Ответ 1

обновлено для С++ 11 2018-12-04

Я попытался обновить этот ответ, чтобы использовать С++ 11 на моей машине, но не смог, потому что у моего компилятора g++ нет полной поддержки <regex>... поэтому я продолжал получать непроверенный std::regex_error code=4 (т.е. bracket ") для любого регулярного выражения с символьными символьными классами std::regex("[0-9]").

По-видимому, полная поддержка С++ 11 <regex> была реализована и выпущена для g++ версии 4.9.x и 26 июня 2015 года. Совет Hat для вопросов # 1 и # 2 для выяснения версии компилятора, которая должна быть 4.9.x.

Вот код С++ 11, который должен работать, но я не смог его протестировать:

#include <iostream>
#include <string>
#include <regex>

using std::cout;
using std::endl;

int main() {
    std::string input = "Example_45-3";
    std::string output = std::regex_replace(
        input,
        std::regex("[^0-9]*([0-9]+).*"),
        std::string("\\1")
        );
    cout << input << endl;
    cout << output << endl;
}

что требует только С++ 98

Минимальный пример реализации, который работает на многих строках (а не только на строки формы "text_45-text":

#include <iostream>
#include <string>
using namespace std;
#include <boost/regex.hpp>

int main() {
    string input = "Example_45-3";
    string output = boost::regex_replace(
        input,
        boost::regex("[^0-9]*([0-9]+).*"),
        string("\\1")
        );
    cout << input << endl;
    cout << output << endl;
}

консольный выход:

Example_45-3
45

Другие примеры строк, в которых это будет работать:

  • "asdfasdf 45 sdfsdf"
  • "X = 45, sdfsdf"

В этом примере я использовал g++ в Linux с #include <boost/regex.hpp> и -lboost_regex. Вы также можете использовать регулярное выражение С++ 11x.

Не стесняйтесь редактировать мое решение, если у вас есть лучшее регулярное выражение.


Комментарий:

Если ограничений производительности нет, использование Regex идеально подходит для такого рода вещей, потому что вы не изобретаете колесо (написав кучу кода синтаксического анализа строки, который требует времени для записи/тестирования).

Кроме того, если/когда ваши строки становятся более сложными или имеют более разнообразные шаблоны, регулярное выражение легко усложняет сложность. (Пример шаблона вопроса достаточно прост. Но часто, когда более сложный шаблон будет принимать 10-100 + строк кода, если одно регулярное выражение будет делать то же самое.)

Ответ 2

Вы также можете использовать встроенные find_first_of и find_first_not_of, чтобы найти первую строку "numberstring" в любой строке.

std::string first_numberstring(std::string const & str)
{
  std::size_t const n = str.find_first_of("0123456789");
  if (n != std::string::npos)
  {
    std::size_t const m = str.find_first_not_of("0123456789", n);
    return str.substr(n, m != std::string::npos ? m-n : m);
  }
  return std::string();
}

Ответ 3

Это должно быть более эффективным, чем решение Ашота Хачатряна. Обратите внимание на использование '_' и '-' вместо "_" и "-". А также начальную позицию поиска '-'.

inline std::string mid_num_str(const std::string& s) {
    std::string::size_type p  = s.find('_');
    std::string::size_type pp = s.find('-', p + 2); 
    return s.substr(p + 1, pp - p - 1);
}

Если вам нужно число вместо строки, например, что сделал Александр Лапенков, вы также можете попробовать следующее:

inline long mid_num(const std::string& s) {
    return std::strtol(&s[s.find('_') + 1], nullptr, 10);
}

Ответ 4

Проверьте это

std::string ex = "Example_45-3";
int num;
sscanf( ex.c_str(), "%*[^_]_%d", &num );

Ответ 5

Я могу представить два способа сделать это:

  • Использовать регулярные выражения
  • Используйте итератор для ввода строки и скопируйте каждую последовательную цифру во временный буфер. Разрыв, когда он достигает необоснованной длины или на первой нецифровой строке после строки последовательных цифр. Затем у вас есть строка цифр, которую вы можете легко конвертировать.

Ответ 6

std::string s = "Example_45-3";
int p1 = s.find("_");
int p2 = s.find("-");
std::string number = s.substr(p1 + 1, p2 - p1 - 1)

Ответ 7

"Лучший" способ сделать это в С++ 11 и более поздних версиях, вероятно, использует регулярные выражения, которые сочетают высокую выразительность и высокую производительность когда тест повторяется достаточно часто.

Следующий код демонстрирует основы. Вы должны #include <regex> работать.

// The example inputs
std::vector<std::string> inputs {
    "Example_0-0", "Example_0-1", "Example_0-2", "Example_0-3", "Example_0-4",
    "Example_1-0", "Example_1-1", "Example_1-2", "Example_1-3", "Example_1-4"
};

// The regular expression. A lot of the cost is incurred when building the
// std::regex object, but when it reused a lot that cost is amortised.
std::regex imgNumRegex { "^[^_]+_([[:digit:]]+)-([[:digit:]]+)$" };

for (const auto &input: inputs){
    // This wil contain the match results. Parts of the regular expression
    // enclosed in parentheses will be stored here, so in this case: both numbers
    std::smatch matchResults;

    if (!std::regex_match(input, matchResults, imgNumRegex)) {
        // Handle failure to match
        abort();
    }

    // Note that the first match is in str(1). str(0) contains the whole string
    std::string theFirstNumber = matchResults.str(1);
    std::string theSecondNumber = matchResults.str(2);

    std::cout << "The input had numbers " << theFirstNumber;
    std::cout << " and " << theSecondNumber << std::endl;
}

Ответ 8

Используя ответ @Pixelchemist и, например, std::stoul:

bool getFirstNumber(std::string const & a_str, unsigned long & a_outVal)
{
    auto pos = a_str.find_first_of("0123456789");

    try
    {
        if (std::string::npos != pos)
        {
            a_outVal = std::stoul(a_str.substr(pos));

            return true;
        }
    }
    catch (...)
    {
        // handle conversion failure
        // ...
    }

    return false;
}