RegEx以逗号分隔,但不包括大括号,括号和括号内的逗号

时间:2013-02-18 18:27:12

标签: javascript regex

我正在尝试解析以逗号分隔的列表,同时省略属于由大括号,括号或括号定义的内部结构的逗号。例如,这个字符串:

'text:firstName,css:{left:x,top:y},values:["a","b"],visible:(true,false),broken:["str", 1, {}, [],()]'

应分为:

text:firstName
css:{left:x,top:y}
values:["a","b"]
visible:(true,false)
broken:["str", 1, {}, [],()]

到目前为止,我已经得到了以下内容......这种情况很接近,但在嵌套结构上会中断:

[^,\[\]{}]+(({|\[)[^\[\]{}]*(}|\]))?

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:1)

除非您愿意更改数据格式,或者您可以在接收后找到一种简单的方法将其转换为正确的JSON,但最好的办法是手动解析。

最简单的匹配器(假设“漂亮”值):

On ([{   - increment parens
On )]}   - decrement parens or emit error if parens is zero
On ,     - emit and reset the buffer if parens is zero (finish a match)
If not , - push into the output buffer

这不适用于“丑陋”的字符串(引用的parens,转义引号,转义转义......)。这个解析器应该正确地解析所有有效的输入,同时仍然相对简单:

On ([{ - increment parens if the state is "start". Push to buffer.
On )]} - decrement parens if the state is "start" and parens is positive.
         Emit an error if parens is zero. Push to buffer.
On ,   - emit and reset the buffer if parens is zero and the state is "start"
         (finish a match). Push to buffer.
On \   - Push to buffer, and push and read the next symbol as well.
On '   - If the state is "start", change the state to "squote", and vice versa.
         Push to buffer.
On "   - If the state is "start", change the state to "dquote", and vice versa.
         Push to buffer.
On EOF - Emit error if parens is not zero or the state is not "start".

以下是Javascript中实现的草图:

function splitLiteralBodyByCommas(input){
  var out = [];
  var iLen = input.length;
  var parens = 0;
  var state = "";
  var buffer = ""; //using string for simplicity, but an array might be faster

  for(var i=0; i<iLen; i++){
    if(input[i] == ',' && !parens && !state){
      out.push(buffer);
      buffer = ""; 
    }else{
      buffer += input[i];
    }
    switch(input[i]){
      case '(':
      case '[':
      case '{':
        if(!state) parens++;
        break;
      case ')':
      case ']':
      case '}':
        if(!state) if(!parens--)
          throw new SyntaxError("closing paren, but no opening");
        break;
      case '"':
        if(!state) state = '"';
        else if(state === '"') state = '';
        break;
      case "'":
        if(!state) state = "'";
        else if(state === "'") state = '';
        break;
      case '\\':
        buffer += input[++i];
        break;
    }//end of switch-input
  }//end of for-input
  if(state || parens)
    throw new SyntaxError("unfinished input");
  out.push(buffer);
  return out;
}

这个解析器仍然存在缺陷:

它允许用括号等关闭parens。要解决此问题,请使parens成为一堆符号;如果开始和结束符号不匹配,则引发异常。

它允许格式错误的unicode转义字符串。解析器接受\utest

它允许转义顶级逗号。这可能不是错误:\,,\,是一个有效的字符串,包含两个由未转义的字符分隔的顶级转义逗号。

尾随反斜杠会产生意外输出。同样,这将通过读取我们正在逃避的数据来解决。更简单的修复方法是buffer += input[++i] || ''(附加一个空字符串而不是undefined,但这会导致无效输入。

它允许各种其他无效输入:[""'']{'\\'}"a"只是一个例子。修复需要更好(更复杂)的语法,并伴随着更复杂的解析器。


话虽如此,使用JSON传输数据不是更好吗?

选项1:真实对象:{"text":"firstName", "css":{ ...
选项2(仅当你真的希望如此)时:字符串数组:["text:firstName, css:{ ...

在这两种情况下,JSON.parse(input)都是你的朋友。

答案 1 :(得分:0)

使用递归匹配:

(?>[^(){}[\], ]+:)?(?>(?>\([^()]*(?R)?[^()]*\))|(?>\[[^[\]]*(?R)?[^[\]]*\])|(?>{[^{}]*(?R)?[^{}]*})|(?>[^(){}[\], ]+))

第一部分匹配并包括冒号:

(?>[^(){}[\], ]+:)?

其余部分包含平衡 ()[]{} 以及除逗号或空格以外的任何其他选项:

(?>(?>\([^()]*(?R)?[^()]*\))|
(?>\[[^[\]]*(?R)?[^[\]]*\])|
(?>{[^{}]*(?R)?[^{}]*})|
(?>[^(){}[\], ]+))