4

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

'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, {}, [],()]

到目前为止,我有以下内容......它很接近但在嵌套结构上中断:

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

任何帮助将不胜感激!

4

2 回答 2

2

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

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

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

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

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一堆符号;如果开始和结束符号不匹配,则引发异常。

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

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

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

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


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

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

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

于 2013-02-18T19:37:15.943 回答
0

使用递归匹配:

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

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

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

其余部分包含 balance ()[]{}和除逗号或空格之外的任何其他选项:

(?>(?>\([^()]*(?R)?[^()]*\))|
(?>\[[^[\]]*(?R)?[^[\]]*\])|
(?>{[^{}]*(?R)?[^{}]*})|
(?>[^(){}[\], ]+))
于 2021-04-01T23:54:21.257 回答