正则表达式只有字符串中的数字 C++
regex with only numbers in a string c++
我正在寻找一个正则表达式来查找字符串中的数字;如果我有这样的字符串:
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:]]+)?))?
但这给出了相同的结果。
解决方法是什么?
解决方案
留给后人解决办法:
- 如果您正在寻找不使用正则表达式的简单有效的解决方案,请参阅
如果您正在寻找使用 RegEx 的解决方案,请参阅
R"((?:^|\s)([+-]?[[:digit:]]+(?:\.[[:digit:]]+)?)(?=$|\s))"
你需要这个正则表达式:
(?<!,)\b([\d\.]+)\b(?!,)
使用否定前瞻和后视来断言数字的两边都没有有趣的字符:
(?<![^\s])(\+|-)?[0-9]+(\.[0-9]*)?(?![^\s])
不幸的是,您将需要 Boost.Regex 来完成这项任务,因为内置的不支持这些结构。
您最好将输入拆分为 个词,然后对每个词使用简单的正则表达式。
与 一样,这只能通过环顾四周来完成。由于在搜索空格前后的数字时需要使用单个空格分隔数字。
可以简化到可以与正则表达式的简单性相媲美的程度:
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 == '[=11=]') cout << num << endl;
}
请注意,对于我的定界符参数,我只是使用默认的 isspace
值。
实际上,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;
}
由于原始字符串文字声明,\s
不需要使用双反斜杠。
先行(?=$|\s)
检查是否存在,但不消耗空格,可以提取连续数字。
注意,如果需要像.5
这样提取十进制值,则需要
R"((?:^|\s)([+-]?[[:digit:]]*\.?[[:digit:]]+)(?=$|\s))"
两次尝试:
#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;
}
您可以玩 trick 来消耗您不想要的东西。像这样。
(?:\d+,|[a-z]+)\d+|(\d+[.\d]*)
修改第一组管道预捕获和抓取中应排除的所有内容。
See demo at regex101。不知道 (:
非捕获组是否适用于 C++。删除,如果没有。
正则表达式通常不可读且难以证明正确。只匹配有效有理数的正则表达式需要很复杂并且很容易搞砸。因此,我提出了一种替代方法。代替正则表达式,使用 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 == '[=10=]';
}
// ...
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);
我正在寻找一个正则表达式来查找字符串中的数字;如果我有这样的字符串:
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:]]+)?))?
但这给出了相同的结果。
解决方法是什么?
解决方案 留给后人解决办法:
- 如果您正在寻找不使用正则表达式的简单有效的解决方案,请参阅
如果您正在寻找使用 RegEx 的解决方案,请参阅
R"((?:^|\s)([+-]?[[:digit:]]+(?:\.[[:digit:]]+)?)(?=$|\s))"
你需要这个正则表达式:
(?<!,)\b([\d\.]+)\b(?!,)
使用否定前瞻和后视来断言数字的两边都没有有趣的字符:
(?<![^\s])(\+|-)?[0-9]+(\.[0-9]*)?(?![^\s])
不幸的是,您将需要 Boost.Regex 来完成这项任务,因为内置的不支持这些结构。
您最好将输入拆分为 个词,然后对每个词使用简单的正则表达式。
与
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 == '[=11=]') cout << num << endl;
}
请注意,对于我的定界符参数,我只是使用默认的 isspace
值。
实际上,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;
}
由于原始字符串文字声明,\s
不需要使用双反斜杠。
先行(?=$|\s)
检查是否存在,但不消耗空格,可以提取连续数字。
注意,如果需要像.5
这样提取十进制值,则需要
R"((?:^|\s)([+-]?[[:digit:]]*\.?[[:digit:]]+)(?=$|\s))"
两次尝试:
#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;
}
您可以玩 trick 来消耗您不想要的东西。像这样。
(?:\d+,|[a-z]+)\d+|(\d+[.\d]*)
修改第一组管道预捕获和抓取中应排除的所有内容。
See demo at regex101。不知道 (:
非捕获组是否适用于 C++。删除,如果没有。
正则表达式通常不可读且难以证明正确。只匹配有效有理数的正则表达式需要很复杂并且很容易搞砸。因此,我提出了一种替代方法。代替正则表达式,使用 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 == '[=10=]';
}
// ...
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);