用于解析数据的正则表达式

9

我是一个正在编写应用程序的人,它可以从一个简单的文本文件中读取一些数据。我感兴趣的数据文件包含以下格式的行:

Mem(100) = 120
Mem(200) = 231
Mem(43) = 12
...
Mem(1293) = 12.54

因此,您可以理解,每行的模式类似于:
(\s)*(\t)*Mem([0-9]*) (\s,\t)*= (\s,\t)*[0-9]*(.)*[0-9]*

我希望能匹配在字符序列“Mem”之前有任意数量的空格,后跟一个左括号。然后是一个数字和一个右括号。此后,在遇到“=”(等于)字符之前有任意数量的空格。然后,任意数量的空格直到我遇到(可能是)浮点数。

如何在C++正则表达式中表示这个模式?我对C++中的正则表达式概念非常陌生,所以需要一些帮助。

谢谢


我不太明白这个问题。如果你想知道如何在C++中使用正则表达式,那么有很多例子可以参考。顺便说一下,你应该转义你的括号 - ...Mem\([0-9]*\)... - Bernhard Barker
@Dukeling 那就是我在这里问的原因。我既找不到类似的例子,也搞不清正则表达式模式匹配的工作原理。 - nick.katsip
1
正则表达式对于如此简单的模式来说有些过头了。把这一行读入一个字符串中,然后搜索 (,搜索 ),最后搜索下一个数字。 - Pete Becker
1个回答

31

首先,记得要 #include <regex>

C++ 中的 std::regex_match 与其他语言中的正则表达式类似。

让我们从一个简单的例子开始:

std::string str = "Mem(100)=120";
std::regex regex("^Mem\\([0-9]+\\)=[0-9]+$");
std::cout << std::regex_match(str, regex) << std::endl;

在这种情况下,我们的正则表达式是^Mem\([0-9]+\)=[0-9]+$。让我们看看它的作用:
  • 在开头的^告诉C++这是行的起始位置,所以AMem(1)=2不能匹配。
  • 在结尾的$告诉C++这是行的结束位置,所以Mem(1)=2x不能匹配。
  • \\(是一个字面上的(字符。 (在正则表达式中有非常特殊的含义,所以我们需要转义它为\(。然而,在C++字符串中,\字符也有特殊的含义,所以我们使用\\(来告诉C++将\(传递给正则表达式引擎。
  • [0-9]匹配数字。 \\d也应该可以工作,但也许不行
  • [0-9]+意味着至少一个数字。如果Mem()可接受,则使用[0-9]*代替。

如您所见,这就像其他语言(如Java或C#)中所见的正则表达式一样。

现在,为了考虑空格,请使用std::regex regex("^\\s*Mem\\([0-9]+\\)\\s*=\\s*[0-9]+\\s*$");

请注意,\s包括\t,因此无需指定两者。如果不是这样,您将使用(\s|\t)[\s\t],而不是(\s,\t)

最后,要包括浮点数,我们首先需要考虑Mem(1) = 1.(即没有数字的句点)是否可接受。

如果不是,则1.23中的.23可选的。在正则表达式中,我们使用?来表示。

std::regex regex("^[\\s]*Mem\\([0-9]+\\)\\s*=\\s*[0-9]+(\\.[0-9]+)?\\s*$");

请注意,我们使用 \. 而不是只有 .。在正则表达式中,. 有特殊含义 - 它匹配任何字符 - 所以我们需要转义它。
如果您有支持原始字符串的编译器(例如 Visual Studio 2013GCC 4.5Clang 3.0),您可以简化正则表达式字符串:
std::regex regex(R"(^[\s]*Mem\([0-9]+\)\s*=\s*[0-9]+(\.[0-9]+)?\s*$)")

为了提取匹配字符串的信息,您可以使用std::smatchgroups
让我们从一个小变化开始:
std::string str = " Mem(100)=120";
std::regex regex("^[\\s]*Mem\\(([0-9]+)\\)\\s*=\\s*([0-9]+(\\.[0-9]+)?)\\s*$");
std::smatch m;

std::cout << std::regex_match(str, m, regex) << std::endl;

请注意以下三点:
  1. 我们添加了smatch。这个类存储有关匹配的额外结果信息。
  2. 我们在[0-9]*周围添加了额外的括号。这定义了一个组。组告诉正则表达式引擎跟踪其中的任何内容。
  3. 浮点数周围有更多的括号。这定义了第二个组。

非常重要的是,定义组的括号不需要转义,因为我们不希望它们匹配实际的括号字符。我们实际上想要特殊的正则表达式含义。

现在我们有了这些组,我们可以使用它们:

for (auto result : m) {
    std::cout << result << std::endl;
}

这将首先打印整个字符串,然后是 Mem() 中的数字,最后是最终数字。
换句话说,m[0] 给出了整个匹配,m[1] 给出了第一个组,m[2] 给出了第二个组,如果有第三个组,则 m[3] 将给出它。

3
你也可以使用原始字符串字面值来摆脱转义序列。R"regex(hello\world)regex" - dyp
1
@DyP 是的,由于regex本来就是C++11的一部分,因此原始字符串字面值应该可用。不幸的是,一些支持正则表达式的C++实现(如Visual Studio)不支持原始字符串字面值。 - luiscubal
@luiscubal 非常感谢您的回答。它确实帮了我很多。我的后续问题是如何获取括号内的数字。例如,如果我有Mem(Num),如何隔离Num字符串? - nick.katsip
1
@popanik 看看我的新答案。你需要的是群组。 - luiscubal
@oLen 已修复。谢谢。 - luiscubal
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接