正则表达式,字符串中只有数字 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:]]+)?))?
但输出包括uf31
和43,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!但是,我仍然认为环顾四周是最昂贵的正则表达式操作。除非绝对必要,否则应避免使用它们,但它们不在这里。 在这种情况下,按照这个逻辑,前瞻是必须的。如果没有前瞻,您将无法匹配<SPACE>41<SPACE>31<SPACE>
中的数字。
@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++的主要内容,如果未能解决你的问题,请参考以下文章