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

fak*_*unt 23 c++ string stringstream

我有一个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); 
}
Run Code Online (Sandbox Code Playgroud)

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

我纯粹是出于在一天结束时学习如何更好地编写代码的愿望,上面的代码确实成功地提取了第一个数字,但它看起来很长并且很圆润.

Pix*_*ist 19

您还可以使用内置的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();
}
Run Code Online (Sandbox Code Playgroud)


Lin*_*gxi 18

这应该比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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • @TrevorBoydSmith是的,但根据OP,数据_will_将处于这种模式. (4认同)
  • 此解决方案仅适用于特定于SPECIFIC的模式...即,数字必须在其前面带有"_"字符,并且在数字后面带有" - "字符.像"asdfasdf 45 sdfsdf"或"X = 45,sdfsdf"这样的字符串不起作用. (3认同)

Ale*_*kov 13

看一下这个

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

  • @Alex--一个标记为C++但不是C的帖子是一个想要C++解决方案而不是C的公平暗示.并且没有理由认为这比其他选择更快. (3认同)
  • 当OP明确寻求C++解决方案时,AC解决方案. (2认同)
  • @black 感谢您的建议 :) 我认为在这里大喊“这不是 C++ !!!!”真的很愚蠢。 (2认同)
  • sscanf的主要优点是您的编译器供应商已经调试过 - 获得正确的格式表达式并完成您的工作.其他"C++"解决方案(我同意这是一个红色鲱鱼)涉及索引操作/减法/关闭{+ 1/-1/+ 2} fencepost错误,并且需要在依赖它之前进行相当好的调试.请注意,@ lingxi的答案和这个都没有正确解释字符串中的格式错误.这个答案是通过检查sscanf的返回值来解决的,另一个答案需要在执行string :: substr之前检查std :: string :: npos的两个值. (2认同)

Dio*_*nha 12

我可以想到两种方法:

  • 使用正则表达式
  • 使用迭代器逐步执行字符串,并将每个连续数字复制到临时缓冲区.当它达到不合理的长度时断开或在一串连续数字后的第一个非数字上断.然后你有一串数字,你可以轻松转换.


Tre*_*ith 12

针对C++ 11 2018-12-04进行了更新

我试图更新这个答案,在我的机器上使用C++ 11,但因为我的g ++编译器没有完全的<regex>支持而失败了...所以我一直在std::regex_error code=4使用括号字符类的任何正则表达式的未被捕获(即"缺少括号")异常std::regex("[0-9]").

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

这是应该工作的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("\\1")
        );
    cout << input << endl;
    cout << output << endl;
}
Run Code Online (Sandbox Code Playgroud)

提升只需要C++ 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;
}
Run Code Online (Sandbox Code Playgroud)

控制台输出:

Example_45-3
45
Run Code Online (Sandbox Code Playgroud)

其他可用的示例字符串:

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

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

如果你有更好的正则表达式,请随意编辑我的解决方案.


评论:

如果没有性能限制,使用Regex是理想的做法,因为你没有重新发明轮子(通过编写一堆字符串解析代码,需要时间来编写/测试完全).

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


Ash*_*yan 9

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


Thi*_*rry 8

在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;
}
Run Code Online (Sandbox Code Playgroud)