我正在使用以下列格式返回文本的API:
#start
#p 12345 foo
#p 12346 bar
#end
#start
#p 12345 foo2
#p 12346 bar2
#end
我的解析功能:
function parseApiResponse(data) {
var results = [], match, obj;
while (match = CST.REGEX.POST.exec(/(#start)|(#end)|#p\s+(\S+)\s+(\S+)/ig)) {
if (match[1]) { // #start
obj = {};
} else if (match[2]) { // #end
results.push(obj);
obj = null; // prevent accidental reuse
// if input is malformed
} else { // #p something something
obj[match[3]] = match[4];
}
}
return results;
}
这将给我一个对象列表,如下所示:
[{ '12345': 'foo', '12346': 'bar'}, /* etc... */]
但是,如果某行格式如此
#start
#p 12345
#p 12346 bar
#end
该行实际上是#p 12345\n
,我的match[4]
将包含下一行的#p
。
如何调整模式以适应这种情况?
答案 0 :(得分:1)
假设每行有一个#start
,#end
或#p
元素,您可以让正则表达式识别这一点,并添加一个额外的非捕获组以指示最后一个一行中的\s+(\S+)
是可选的:
/(#start)|(#end)|#p\s+(\S+)(?:\s+(\S+))?$/igm
(?: )
说“将此视为一个组,但不捕获它匹配的模式”(因此它不会在match
中创建一个元素)。该组后面的?
表示“此组是可选的,可能与模式中的任何内容匹配也可能不匹配”。在此之后的$
与m
标志一起匹配该行的结尾。
您还可以使用*而不是+ quantiers来避免(?: )
欺骗,即“匹配零次或多次”:将\s+(\S+)
更改为\s*(\S*)
。这样做的副作用是数字和它后面的数据之间的空格现在是可选的。
我会重写正则表达式并重构代码如下:
while (match = CST.REGEX.POST.exec(/^#(start|end|p)(?:\s+(\d+)(?:[^\S\r\n]+([^\r\n]+))?)?$/igm)) {
switch (match[1]) {
case 'start':
obj = {};
break;
case 'end':
results.push(obj);
obj = null;
break;
case 'p':
obj[match[2]] = match[3];
break;
}
}
我喜欢在一个捕获组中捕获start
,end
或p
,因此我可以在switch
语句中使用它。我在这里使用的正则表达式的版本更具辨别力(期望#p
之后的令牌是数字)并且更宽容(允许#p
行上的最后一个令牌包含任何非-linebreak空格,例如#p 1138 this is only a test
)。