C++ 从字符串中间提取数字

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30073839/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:42:00  来源:igfitidea点击:

C++ Extract number from the middle of a string

c++stringstringstream

提问by fakeaccount

I have a vectorcontaining stringsthat follow the format of text_number-number

我有一个vector包含strings下面的格式text_number-number

Eg:Example_45-3

例如:Example_45-3

I only want the first number (45in the example) and nothing else which I am able to do with my current code:

我只想要第一个数字(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); 
}

Are there more streamlined and simpler ways of doing this? and if so what are they?

是否有更精简和更简单的方法来做到这一点?如果是,它们是什么?

I ask purely out of desire to learn how to code better as at the end of the day, the code above does successfully extract just the first number, but it seems long winded and round-about.

我问纯粹是出于学习如何更好地编码的愿望,因为在一天结束时,上面的代码确实成功地提取了第一个数字,但它似乎冗长而迂回。

采纳答案by Trevor Boyd Smith

updated for C++11

为 C++11 更新

(important note for compiler regex support: for gcc. you need version 4.9 or later. i tested this on g++ version 4.9[1], and 9.2. cppreference.com has in browser compiler that i used.)

(编译器正则表达式支持的重要说明:对于 gcc。您需要 4.9 版或更高版本。我在 g++ 4.9[1] 版和 9.2 版上对此进行了测试。cppreference.com 有我使用的浏览器编译器。)

Thanks to user @2b-t who found a bug in the c++11 code!

感谢在 c++11 代码中发现错误的用户@2b-t!

Here is the C++11 code:

这是 C++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("")
        );
    cout << input << endl;
    cout << output << endl;
}


boost solution that only requires C++98

只需要 C++98 的 boost 解决方案

Minimal implementation example that works on many strings (not just strings of the form "text_45-text":

适用于许多字符串的最小实现示例(不仅仅是“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;
}

console output:

控制台输出:

Example_45-3
45

Other example strings that this would work on:

这将适用于其他示例字符串:

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

For this example I used g++ on Linux with #include <boost/regex.hpp>and -lboost_regex. You could also use C++11x regex.

在这个例子中,我在 Linux 上使用了 g++#include <boost/regex.hpp>-lboost_regex。您也可以使用 C++11x 正则表达式。

Feel free to edit my solution if you have a better regex.

如果您有更好的正则表达式,请随时编辑我的解决方案。



Commentary:

评论:

If there aren't performance constraints, using Regex is ideal for this sort of thing because you aren't reinventing the wheel (by writing a bunch of string parsing code which takes time to write/test-fully).

如果没有性能限制,那么使用 Regex 是此类事情的理想选择,因为您不会重新发明轮子(通过编写一堆需要时间来编写/完全测试的字符串解析代码)。

Additionally if/when your strings become more complex or have more varied patterns regex easily accommodates the complexity. (The question's example pattern is easy enough. But often times a more complex pattern would take 10-100+ lines of code when a one line regex would do the same.)

此外,如果/当您的字符串变得更复杂或具有更多变化的模式时,正则表达式很容易适应复杂性。(问题的示例模式很简单。但是,当一行正则表达式执行相同操作时,更复杂的模式通常需要 10-100 多行代码。)



[1]

[1]

[1] Apparently full support for C++11 <regex>was implemented and released for g++ version 4.9.x and on Jun 26, 2015. Hat tip to SO questions #1and #2for figuring out the compiler version needing to be 4.9.x.

[1] 显然,在 2015 年 6 月 26 日,g++ 版本 4.9.x<regex>实现并发布了对 C++11 的完全支持。SO 问题#1#2 的帽子提示,用于确定需要为 4.9.x 的编译器版本。

回答by Pixelchemist

You can also use the built in find_first_ofand find_first_not_ofto find the first "numberstring" in any string.

您还可以使用内置的find_first_ofandfind_first_not_of查找任何字符串中的第一个“数字字符串”。

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

回答by Lingxi

This should be more efficient than Ashot Khachatryan's solution. Note the use of '_'and '-'instead of "_"and "-". And also, the starting position of the search for '-'.

这应该比 Ashot Khachatryan 的解决方案更有效。请注意使用'_'and'-'代替"_"and "-"。而且,搜索的起始位置'-'

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

If you need a number instead of a string, like what Alexandr Lapenkov's solution has done, you may also want to try the following:

如果您需要数字而不是字符串,就像 Alexandr Lapenkov 的解决方案所做的那样,您可能还想尝试以下操作:

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

回答by Alexander Lapenkov

Check this out

看一下这个

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

回答by Diogo Cunha

I can think of two ways of doing it:

我可以想到两种方法:

  • Use regular expressions
  • Use an iterator to step through the string, and copy each consecutive digit to a temporary buffer. Break when it reaches an unreasonable length or on the first non-digit after a string of consecutive digits. Then you have a string of digits that you can easily convert.
  • 使用正则表达式
  • 使用迭代器遍历字符串,并将每个连续的数字复制到临时缓冲区。当它达到不合理的长度或一串连续数字后的第一个非数字时中断。然后你就有了一串可以轻松转换的数字。

回答by Ashot Khachatryan

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

回答by Thierry

The 'best' way to do this in C++11 and later is probably using regular expressions, which combine high expressiveness and high performance when the test is repeated often enough.

在 C++11 及更高版本中执行此操作的“最佳”方法可能是使用正则表达式,当测试重复得足够频繁时,它结合了高表现力和高性能。

The following code demonstrates the basics. You should #include <regex>for it to work.

以下代码演示了基础知识。你应该#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;
}

回答by darkbit

Using @Pixelchemist's answer and e.g. std::stoul:

使用@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;
}