web-dev-qa-db-ja.com

C ++文字列の途中から数値を抽出

text_number-numberの形式に従うvectorを含むstringsがあります

例: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); 
}

これを行うためのより合理化されたシンプルな方法はありますか?もしそうなら、彼らは何ですか?

結局のところ、上記のコードは最初の数だけを正常に抽出するので、コードの改善方法を学びたいと純粋に願っていますが、長く曲がりくねっています。

23
fakeaccount

組み込みの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();
}
19
Pixelchemist

これは、Ashot Khachatryanのソリューションよりも効率的です。 '_''-'の代わりに"_""-"を使用していることに注意してください。また、'-'の検索の開始位置。

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

Alexandr Lapenkovのソリューションが行ったように、文字列ではなく数字が必要な場合は、次のことも試してください。

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

これをチェックしてください

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

私はそれを行う2つの方法を考えることができます:

  • 正規表現を使用する
  • イテレータを使用して文字列をステップスルーし、連続する各数字を一時バッファにコピーします。不合理な長さに達するか、連続する数字の列の後の最初の非数字でブレークします。次に、簡単に変換できる数字の文字列があります。
12
Diogo Cunha
std::string s = "Example_45-3";
int p1 = s.find("_");
int p2 = s.find("-");
std::string number = s.substr(p1 + 1, p2 - p1 - 1)
9

C++ 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's 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
Thierry

@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;
}
0
darkbit