结合积极的外观和前瞻

时间:2015-02-03 02:32:24

标签: java regex lookahead lookbehind

我想从自定义键值协议解析数组。看起来像这样

RESPONSE GAMEINFO OK
NAME: "gamelobby"
PLAYERS: "alice", "bob", "hodor"
FLAGS: 1, 2, 3

在Java中,String看起来像这样(它使用CRLF作为换行符):

RESPONSE GAMEINFO OK\\r\\nNAME: \"gamelobby\"\\r\\nPLAYERS: \"alice\", \"bob\", \"hodor\"FLAGS: 1, 2, 3\\r\\n

我想按原样捕捉"alice", "bob", "hodor"。所以我使用了这个regexp,它在Sublime Text和regex101.com上测试过(键不区分大小写)

(?<=(?i:PLAYERS): )([A-Za-z0-9\s\.,:;\?!\n"_-]*)(?=\r\n)

这是Sublime Text的截图(注意:我遗漏了\ r这里):

enter image description here

当我尝试捕获该组时,我也得到了下一行:

Pattern p = Pattern.compile("(?<=(?i:"+key+"): )([A-Za-z0-9\\s\\.,:;\\?!\\n\"_-]*)(?=\\r\\n)");
Matcher matcher = p.matcher(message);
matcher.find();
String value = new String();
try {
    value = matcher.group(); // = "\"alice\", \"bob\", \"hodor\"\\r\\nFLAGS: 1, 2, 3"
} ...

注意:\"\\\"似乎无法发挥作用。

为什么在FLAGS: 1, 2, 3之前捕获\\r\\n,但不在上面的行中?积极的外观和前瞻可能吗?首先评估哪个lookhead / lookbehind?

编辑:字符串数组的定义是

values        = string*("," WSP string)
string        = DQUOTE *(ALPHA / DIGIT / WSP / punctuation / "\n") DQUOTE
punctuation   = "." / ":" / "," / ";" / "?" / "!" / "-" / "_"

2 个答案:

答案 0 :(得分:1)

根据你的语法编写代码。语法对我来说似乎并不含糊,所以如果你只是按照它来拼写你的正则表达式,你就会好起来的:

String WHITESPACE_RE = "[ ]"; // Modify this according to your grammar
String PUNCTUATION_RE = "[.:,;?!_-]";
String STRING_RE = "\"(?:[A-Za-z0-9" + WHITESPACE_RE + PUNCTUATION_RE + "\n])*\"";
String VALUES_RE = STRING_RE + "(?:," + WHITESPACE_RE + STRING_RE + ")*";
String PLAYERS_RE = "PLAYERS:" +  WHITESPACE_RE + "(" + VALUES_RE + ")(?=\r\n)";

目前,\r\n用于检查PLAYERS条目末尾的行分隔符。将其更改为您的规范中指定的任何内容。

买者

此解决方案仅适用于解析有效输入。解析无效输入取决于您的恢复算法和行分隔符。

如果行分隔符允许\n以及\r\n ,则很难从错误中恢复。例如,如果有一个名为ABC\nFLAGS: 1, 2, 3的用户(根据语法允许),但缺少结束双引号,那么玩家列表将被破坏,您将无法判断是否{{1 }}是前一行的一部分或不同的标题。

FLAGS:

完整示例

RESPONSE GAMEINFO OK
NAME: "gamelobby"
PLAYERS: "alice", "bob", "hodor", "ABC
FLAGS: 1, 2, 3
FLAGS: 1, 2, 3

答案 1 :(得分:0)

您可以在括号表达式上使用非贪婪的乘数:

(?<=(?i:PLAYERS): )([A-Za-z0-9\s\.,:;\?!\n"_-]*?)(?=\r\n)

当您使用贪心倍数\r\n时,匹配原因不会停留在*,因为括号表达式包含\s\s的定义(根据Pattern类的文档)是[ \t\n\x0B\f\r],因此括号表达式实际上是通过CRLF行终止符和其路径中的其他所有内容,直到它到达整个字符串的结尾。

我想如果您可以明确地防止单词CR出现在引用词列表中,那么另一个可行的解决方案是用明确的\s替换[\n\t\f ],但我&#39我会把它留给你。

非贪婪乘数*?解决方案有效,因为当正则表达式引擎命中第一个CRLF以满足最终前瞻声明时,它会停止匹配,即使括号表达式可能会吞噬它。

由于网站似乎不支持CR,test code on regex101因字符串包含新行而失败,因此我们无法在那里进行全面测试。但是在Java代码中的真正正则表达式中,前瞻断言需要CRLF才能终止搜索,因此最终会匹配整个引用词列表。