使用正则表达式将文本解析为对象

时间:2014-04-09 15:18:40

标签: javascript regex

我正在使用以下列格式返回文本的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

如何调整模式以适应这种情况?

1 个答案:

答案 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;
  }
}

我喜欢在一个捕获组中捕获startendp,因此我可以在switch语句中使用它。我在这里使用的正则表达式的版本更具辨别力(期望#p之后的令牌是数字)并且更宽容(允许#p行上的最后一个令牌包含任何非-linebreak空格,例如#p 1138 this is only a test)。