[英]Parsing text to object with regex
I'm using an API which returns text in the following format: 我正在使用API,该API以以下格式返回文本:
#start
#p 12345 foo
#p 12346 bar
#end
#start
#p 12345 foo2
#p 12346 bar2
#end
My parsing function: 我的解析功能:
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;
}
This will give me a list of objects which looks something like this: 这会给我一个看起来像这样的对象列表:
[{ '12345': 'foo', '12346': 'bar'}, /* etc... */]
However, if a line is formatted like this 但是,如果一行的格式如下
#start
#p 12345
#p 12346 bar
#end
The line would actually be #p 12345\\n
and my match[4]
would contain the next row's #p
. 该行实际上是
#p 12345\\n
而我的match[4]
将包含下一行的#p
。
How do I adjust the pattern to adapt to this? 如何调整样式以适应这种情况?
Assuming you have one #start
, #end
, or #p
element per line, you can make your regex aware of this and add an additional non-capturing group to indicate that the last \\s+(\\S+)
in a line is optional: 假设每行有一个
#end
#start
, #end
或#p
元素,则可以使正则表达式意识到这一点,并添加一个附加的非捕获组以指示一行中的最后一个\\s+(\\S+)
是可选的:
/(#start)|(#end)|#p\\s+(\\S+)(?:\\s+(\\S+))?$/igm
(?: )
is saying "treat this as a group, but don't capture the pattern it matches" (so it won't create an element in match
). (?: )
:)表示“将其作为一个整体进行处理,但不要捕获其匹配的模式”(因此它不会在match
创建元素)。 The ?
?
that follows that group means "this group is optional and may or may not match anything in the pattern". 该组后面的意思是“该组是可选的,可能与该模式中的任何内容都不匹配”。 The
$
right after that, in conjunction with the m
flag, matches the end of the line. 其后的
$
与m
标志一起匹配行尾。
You can also avoid the (?: )
trickery by using * instead of + quantifiers, meaning "match zero or more times": change \\s+(\\S+)
to \\s*(\\S*)
. 您还可以通过使用*而不是+量词来避免
(?: )
:)欺骗,这意味着“匹配零次或多次”:将\\s+(\\S+)
更改为\\s*(\\S*)
。 This has the side effect that the space between the number and the data that follows it is now optional. 这样做的副作用是,数字和其后的数据之间的空格现在是可选的。
I would rewrite the regex and refactor the code a bit as follows: 我将重写正则表达式并重构代码,如下所示:
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;
}
}
I like capturing start
, end
, or p
in the one capture group so I can use it in a switch
statement. 我喜欢在一个捕获组中捕获
start
, end
或p
,因此可以在switch
语句中使用它。 The version of the regex I use here is a little more discriminating (expects the token that follows #p
to be numeric) and a little more forgiving (allows the last token on a #p
line to contain any non-linebreak whitespace, eg #p 1138 this is only a test
). 我在这里使用的正则表达式的版本更具区分性(期望
#p
后面的标记为数字)和更多的宽容(允许#p
行上的最后一个标记包含任何非换行符空白,例如#p 1138 this is only a test
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.