正则表达式,字符串中只有数字 C++

Posted

技术标签:

【中文标题】正则表达式,字符串中只有数字 C++【英文标题】:regex with only numbers in a string c++ 【发布时间】:2015-11-04 11:40:44 【问题描述】:

我正在寻找一个正则表达式来查找字符串中的数字;如果我有这样的字符串:

li 12.12 si 43,23 45 31 uf 889 uf31 3.12345

我只想找到数字:

12.12 45 31 889 3.12345

我尝试了以下模式:

((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?

但输出包括uf3143,23

我试过了:

(?!([a-z]*((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?[a-z]*))?((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?

但这给出了相同的结果。

解决办法是什么?

解决方案 留给后代解决方案:

如果您正在寻找不使用正则表达式的简单有效的解决方案,请参阅Jonathan Mee's post below

如果您正在寻找使用 RegEx 的解决方案,请参阅 the wonderful regex from stribizhev

R"((?:^|\s)([+-]?[[:digit:]]+(?:\.[[:digit:]]+)?)(?=$|\s))"

【问题讨论】:

您能否定义要用于分隔符的内容?我了解您确实想要包含除空格分隔的数字以外的任何内容。对吗? 你要支持.5吗? 科学记数法怎么样?您需要具体说明您希望能够捕获的内容。您的示例字符串不包含 + 或 - 并且您没有在文本中提及它们,但您的正则表达式尝试这样做。 @stribizhev 如果 OP 明确表示他在寻找什么,我不介意提供支持。 @user3641602:对不起,我不明白你的回答。打印'5'?? 【参考方案1】:

实际上,C++ 正则表达式模块支持前瞻

这是我的建议:

#include <iostream>
#include <regex>
using namespace std;

int main() 
    std::string buffer = " li 12.12 si 43,23 45 31 uf 889 uf31 3.12345";
    std::regex rx(R"((?:^|\s)([+-]?[[:digit:]]+(?:\.[[:digit:]]+)?)(?=$|\s))"); // Declare the regex with a raw string literal
    std::smatch m;
    std::string str = buffer;
    while (regex_search(str, m, rx)) 
        std::cout << "Number found: " << m[1] << std::endl; // Get Captured Group 1 text
        str = m.suffix().str(); // Proceed to the next match
      
    return 0;

见IDEONE demo

由于原始字符串字面量声明,\s 无需使用双反斜杠。

lookahead (?=$|\s) 检查是否存在,但不占用空格,并且可以提取连续数字。

注意,如果你需要提取像.5这样的十进制值,你需要

R"((?:^|\s)([+-]?[[:digit:]]*\.?[[:digit:]]+)(?=$|\s))"

【讨论】:

我的立场是正确的,我上次测试环视时使用的是 Visual Studio 2013。看来 C++ 现在完全支持 ECMAScript!但是,我仍然认为环顾四周是最昂贵的正则表达式操作。除非绝对必要,否则应避免使用它们,但它们不在这里。 在这种情况下,按照这个逻辑,前瞻是必须的。如果没有前瞻,您将无法匹配 &lt;SPACE&gt;41&lt;SPACE&gt;31&lt;SPACE&gt; 中的数字。 @JonathanMee:请查看your results - 您的正则表达式与预期的31 不匹配。 @user3641602:很高兴它对你有用,请考虑接受答案。 这是一个原始字符串文字。符号是R"()"。在括号内,\ 符号表示文字 \ 符号,而不是 C 转义符号。【参考方案2】:

你需要这个正则表达式:

(?<!,)\b([\d\.]+)\b(?!,)

【讨论】:

谢谢!但是用你的正则表达式我打印token . token , token . @user3641602 这将匹配 1.2.3...您要对您的号码强制执行正确编号吗? 那么你只需要修改当前的正则表达式:\b([\d\.]+)\b 这有你原来的正则表达式的错误:它捕获 1.2.3 ......但现在它也被@KarolyHorvath 接受了正则表达式中对 Boost 的需求 @user2079303 更正,我的意思是输入这将从逗号以外的任何符号中提取:例如"12#3" 将捕获 12 和 3。【参考方案3】:

就像stated by stribizhev 一样,这只能通过环视来完成。因为在搜索空格前后的数字时,将需要使用单个空格分隔数字。

user2079303 poses a viable option to regexes 可以简化到与正则表达式相媲美的程度:

for_each(istream_iterator<string>(istringstream(" li 12.12 si 43,23 45 31 uf 889 uf31 3.12345")),
         istream_iterator<string>(),
         [](const string& i) 
            char* it;
            double num = strtod(i.c_str(), &it);
            if (distance(i.c_str(), const_cast<const char*>(it)) == i.size()) cout << num << endl; );

但是,无需istringstream 或正则表达式的权重,只需使用strtok 即可完成此操作:

char buffer[] = " li 12.12 si 43,23 45 31 uf 889 uf31 3.12345";

for (auto i = strtok(buffer, " \f\n\r\t\v"); i != nullptr; i = strtok(nullptr, " \f\n\r\t\v")) 
    char* it;
    double num = strtod(i, &it);

    if (*it == '\0') cout << num << endl;

请注意,对于我的分隔符参数,我只是使用默认的 isspace 值。

【讨论】:

如果使用原始字符串文字,则无需转义。 31 不匹配,顺便说一句。 @KarolyHorvath 错了,注意那些是非捕获括号。 +1 感谢您简化使用strtod 的第二个参数。我花了一些时间来理解文档。 @user2079303 看来我们是唯一使用strtod 的人 :( 好吧,如果您正在寻找有关如何使用它的更好解释,您可能想查看:***.com/q/32991193/2642059【参考方案4】:

正则表达式通常不可读且难以证明是正确的。仅匹配有效有理数的正则表达式需要复杂且容易混淆。因此,我提出了一种替代方法。而不是正则表达式,使用 c++ 标记您的字符串并使用std::strtod 来测试输入是否为有效数字。这是示例代码:

std::vector<std::string> split(const std::string& str) 
    std::istringstream iss(str);
    return 
        std::istream_iterator<std::string>iss,
        std::istream_iterator<std::string>
    ;


bool isValidNumber(const std::string& str) 
    char* end;
    std::strtod(str.data(), &end);
    return *end == '\0';


// ...
auto tokens = split(" li 12.12 si 43,23 45 31 uf 889 uf31 3.12345");
std::vector<std::string> matches;
std::copy_if(tokens.begin(), tokens.end(), std::back_inserter(matches), isValidNumber);

【讨论】:

你打败我了strtod+1 是的,这是一种可能的方式。但我有问题的解决方案。我想通过正则表达式减少我的代码,因为如果您使用正则表达式,那么您将拥有一个强大的手动工具! :) 但是,正如您之前提到的,“正则表达式通常不可读且难以证明正确。” :) @user3641602 我认为他的解决方案首先比正则表达式解决方案更简单。我在回答中提供的选项之一中简化了他的代码:***.com/a/33521413/2642059【参考方案5】:

使用否定的lookahead 和lookbehind 来断言数字的两边都没有有趣的字符:

(?<![^\\s])(\\+|-)?[0-9]+(\\.[0-9]*)?(?![^\\s])

不幸的是,您将需要Boost.Regex 来完成该任务,因为内置的不支持这些结构。

您最好将输入拆分为单词,然后对每个单词使用简单的正则表达式。

【讨论】:

C++ 不支持向前看或向后看 ATM 我真的没有看到其他方法。 请注意:[^\\s] 正在寻找不是'\\''s' 的字符。你真正的意思是\S @JonathanMee cplusplus.com/reference/regex/ECMAScript c++ 支持前瞻 我拒绝并且一直拒绝使用 Boost。为了团队的兼容性,我更喜欢使用标准。【参考方案6】:

您可以使用trick 来消费您不想要的东西。像这样。

(?:\d+,|[a-z]+)\d+|(\d+[.\d]*)

修改管道预捕获和第一组抓取捕获中应排除的所有内容。

See demo at regex101。不知道 (: 非捕获组是否适用于 c++。如果没有,请删除。

【讨论】:

令人印象深刻的思考方式,但这将捕获:“123abc”和“12#3”你有办法解决这个问题吗? @JonathanMee 这种方法只有在已知可能发生的情况下才有意义。对于您的样品必须add those cases like this。【参考方案7】:

两次尝试:

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


int main()

    using namespace std;

    string buffer(" li 12.12 si 43,23 45 31 uf 889 uf31 3.12345 .5");

    regex num_regex("(^|\\s)([\\+-]?([0-9]+\\.?[0-9]*|\\.?[0-9]+))(\\s|$)");
    smatch num_match;
    while (regex_search(buffer, num_match, num_regex))
    
        if (num_match.size() >= 4) //3 groups = 4 matches
        
            //We only need the second group
            auto token = num_match[2].str();
            cout << token << endl;
        

        buffer = num_match.suffix().str();
    
    return 0;


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


int main()

    using namespace std;

    string buffer(" li 12.12 si 43,23 45 31 uf 889 uf31 3.12345 .5");

    istringstream iss(buffer);
    vector<string> tokens istream_iterator<string>iss, istream_iterator<string> ;

    regex num_regex("^[\\+-]?([0-9]+\\.?[0-9]*|\\.?[0-9]+)$");
    for(auto token : tokens)
    
        if (regex_search(token, num_regex))
        
            //Valid entry
            cout << token << endl;
        
    

    return 0;

【讨论】:

first 是正确的,但忽略 31 和 0.5 秒总是忽略 31 没有被忽略——我只是测试了这两个变体。 .5 你说得对 - 我会更新我的答案

以上是关于正则表达式,字符串中只有数字 C++的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中使用正则表达式检查字符串

只有在字符串中找到一组数字时,C# 正则表达式才匹配

Java 只有数字,字母,下划线,横杆,英文逗号的正则表达式

用正则表达式验证只能有数字

正则表达式格式

python正则表达式