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に変換する簡単な方法が見つからない場合を除いて、最善の策は手動で解析することです。

最も単純なマッチャー(「適切な」値を想定):

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パーサーによって受け入れられます。

最上位のコンマをエスケープできます。これはおそらく障害ではありません。これは\,,\,有効な文字列であり、エスケープされていないコンマで区切られた2つのトップレベルのエスケープされたコンマが含まれています。

末尾の円記号は予期しない出力を生成します。繰り返しますが、これは、エスケープしているデータを読み取ることで修正されます。より簡単な修正はbuffer += input[++i] || ''(の代わりに空の文字列を追加しますundefinedが、それは無効な入力を許可します。

それは他のあらゆる種類の無効な入力を許可します:[""'']{'\\'}"a"単なる例です。修正には、より優れた(より複雑な)文法と、それに伴うより複雑なパーサーが必要になります。


そうは言っても、データの送信にはJSONを使用する方が良いのではないでしょうか。

オプション1:実際のオブジェクト:{"text":"firstName", "css":{...オプション2(本当に
必要な場合のみ):文字列の配列:..。 ["text:firstName, css:{

どちらの場合も、JSON.parse(input)あなたの友達です。

于 2013-02-18T19:37:15.943 に答える