4

オブジェクトグラフの状態を保持しているカスタム文字列形式を解析したいと思います。これはASP.NETシナリオであり、クライアント(JavaScript)とサーバー(C#)で使いやすいものが必要でした。

私は次のようなフォーマットを持っています

{Name1|Value1|Value2|...|ValueN}{Name2|Value1|...}{...}{NameN|...}

この形式では、3つの区切り文字、、、、{および}があり|ます。さらに、これらの文字は名前/値で考えられるため、非常に一般的な、などを使用してエスケープシーケンスを定義しました。これらは\すべて、通常のバージョンとして解釈され、もちろんバックスラッシュです。すべてかなり標準的です。\{\}\|\\

もともと私は正規表現を使用して、このようなオブジェクトの文字列表現を解析しようとしました(?<!\\)\{(.*?)(?<!\\)\}\、、{および}はすべて正規表現で予約されていることに注意してください。{category|foo\}|bar\{}もちろん、これにより、のようなものを正しく解析できるようになります。しかし、私はそれがのようなもので失敗することに気づきました{category|foo|bar\\}

(?<!(?<!\\)\\)\{(.*?)(?<!(?<!\\)\\)\}これを試して、潜在的な無限の数のエスケープシーケンスを処理するために無限の数のネガティブルックビハインドが必要になることを考えると、このアプローチが不可能であることに気付くのに1分ほどかかりました。もちろん、1つまたは2つ以上のレベルがある可能性は低いので、おそらくハードコーディングすることができます。ただし、これは十分に一般的な問題であり、明確に定義された解決策が必要だと思います。

次のアプローチは、入力バッファーを実際にスキャンし、転送専用メソッドで各文字を消費する、定義済みのパーサーを作成することでした。私はまだこれを実際に終えていませんが、それは過度に複雑であるように思われ、私は明らかな何かを見逃しているに違いないと感じています。つまり、コンピューター言語がある限り、パーサーがあります。

だから私の質問は、可能なエスケープシーケンスでこのような入力バッファをデコードするための最も簡単で効率的でエレガントな方法は何ですか?

4

2 に答える 2

7
(?<!\\)(?:\\\\)*\{(.*?(?<!\\)(?:\\\\)*)\}

(?<!\\)\この時点より前にすべてを防ぎます。

(?:\\\\)*任意の数のエスケープを許可します\

\{左中括弧に一致します。

(キャプチャ グループを開始します。

.*?は、任意の を含むコンテンツに一致します|

(?<!\\)\この時点より前にすべてを防ぎます。

(?:\\\\)*任意の数のエスケープを許可します\

)キャプチャ グループを終了します。

\}右中括弧に一致します。

于 2008-12-19T17:48:34.050 に答える
2

この種のパーサーは、最小限の状態追跡でかなり簡単に実行できます。以下は数分かかり、かなり見苦しく、ほんの少しのエラーチェックさえ行います。:)

おそらく、複雑な正規表現よりも読みやすいアプローチですが、前者の方が少し簡潔です。

struct RECORD
{
    public string[] Entries;
}
struct FILE
{
    public RECORD[] Records;
}

static FILE parseFile(string input)
{
    List<RECORD> records = new List<RECORD>();
    List<string> entries = new List<string>();
    bool escaped = false;
    bool inRecord = false;
    StringBuilder sb = new StringBuilder();
    foreach (char c in input)
    {
        switch (c)
        {
            case '|':
                if (escaped)
                {
                    sb.Append('|');
                    escaped = false;
                }
                else if (inRecord)
                {
                    entries.Add(sb.ToString());
                    sb = new StringBuilder();
                }
                else
                    throw new Exception("Invalid sequence");
                break;
            case '{':
                if (escaped)
                {
                    sb.Append('{');
                    escaped = false;
                }
                else if (inRecord)
                    throw new Exception("Invalid sequence");
                else
                {
                    inRecord = true;
                    sb = new StringBuilder();
                }
                break;
            case '}':
                if (escaped)
                {
                    sb.Append('}');
                    escaped = false;
                }
                else if (inRecord)
                {
                    inRecord = false;
                    entries.Add(sb.ToString());
                    sb = new StringBuilder();
                    records.Add(new RECORD(){Entries = entries.ToArray()});
                    entries.Clear();
                }
                else
                    throw new Exception("Invalid sequence");
                break;
            case '\\':
                if (escaped)
                {
                    sb.Append('\\');
                    escaped = false;
                }
                else if (!inRecord)
                    throw new Exception("Invalid sequence");
                else
                    escaped = true;
                break;
            default:
                if (escaped)
                    throw new Exception("Unrecognized escape sequence");
                else
                    sb.Append(c);
                break;
        }
    }
    if (inRecord)
        throw new Exception("Invalid sequence");
    return new FILE() { Records = records.ToArray() };
}
于 2009-03-09T19:37:33.593 に答える