3

RegEx.Matchesを使用して、一致する値を異なる(アルファベット順)順序で検索して書き戻す方法はありますか?

今のところ私は次のようなものを持っています:

var pattern = @"(KEY `[\w]+?` \(`.*`*\))";
var keys = Regex.Matches(line, pattern);

Console.WriteLine("\n\n");
foreach (Match match in keys)
{
    Console.WriteLine(match.Index + " = " + match.Value.Replace("\n", "").Trim());
}

しかし、私が本当に必要としているのは、table.sqlダンプを取得し、既存のINDEXESをアルファベット順に並べ替えることです。サンプルコードは次のとおりです。

line = "...PRIMARY KEY (`communication_auto`),\n  KEY `idx_current` (`current`),\n  KEY `idx_communication` (`communication_id`,`current`),\n  KEY `idx_volunteer` (`volunteer_id`,`current`),\n  KEY `idx_template` (`template_id`,`current`)\n);"

ありがとうJ


更新: ありがとう、m.buettnerソリューションは私が先に進むために使用できる基本を私に与えました。悲しいことに、私は正規表現があまり得意ではありませんが、それでも改善できると信じているコードになってしまいました。

...
//sort INDEXES definitions alphabetically
if (line.Contains("  KEY `")) line = Regex.Replace(
    line,
    @"[ ]+(KEY `[\w]+` \([\w`,]+\),?\s*)+",
    ReplaceCallbackLinq
);

static string ReplaceCallbackLinq(Match match) 
{
    var result = String.Join(",\n  ",
        from Capture item in match.Groups[1].Captures
        orderby item.Value.Trim()
        select item.Value.Trim().Replace("),", ")")
    );
    return "  " + result + "\n";
}


更新: インデックスフィールドが255文字より長い場合もあります。mysqlはインデックスを255までトリミングし、次のように書き込みます。

KEY `idx3` (`app_property_definition_id`,`value`(255),`audit_current`),

したがって、このケースにも一致させるために、いくつかのコードを変更する必要がありました:ReplaceCallbackLinq:

select item.Value.Trim().Replace("`),", "`)")

および正規表現の定義:

@"[ ]+(KEY `[\w]+` \([\w`(\(255\)),]+\),?\s*)+",
4

2 に答える 2

2

これは、正規表現だけでは実行できません。ただし、コールバック関数を使用して、同じキャプチャグループで複数のものをキャプチャする.NET独自の機能を利用することもできます。このようにしてMatches、すべてを自分で使用したり書き戻したりすることを避けます。代わりに、組み込み関数を使用できます。Replace以下の私の例では、KEYフレーズを並べ替えて元に戻します(つまり、SQLステートメント内でフレーズを並べ替えるだけです)。別の出力が必要な場合は、パターンのさまざまな部分をキャプチャJoinし、最後に操作を調整することで、簡単にそれを実現できます。

まず、コールバックを渡すための一致評価者が必要です。

MatchEvaluator evaluator = new MatchEvaluator(ReplaceCallback);

次に、インデックスのセット全体に一度に一致する正規表現を記述し、キャプチャグループ内のインデックス名をキャプチャします。これを過負荷にReplaceすると、評価者が必要になります。

output = Regex.Replace(
    input,
    @"(KEY `([\w]+)` \(`[^`]*`(?:,`[^`]*`)*\),?\s*)+",
    evaluator
);

現在、ほとんどの言語では、これは役に立ちません。繰り返しキャプチャグループ1には、キャプチャされた最初または最後のもののみが常に含まれるためです(キャプチャグループ2と同じ)。しかし、幸いなことに、C#を使用しており、.NETの正規表現エンジンは強力な獣の1つにすぎません。それでは、コールバック関数と複数のキャプチャの使用方法を見てみましょう。

static string ReplaceCallback(Match match)
{
    int captureCount = match.Groups[1].Captures.Count;
    string[] indexNameArray = new string[captureCount];
    string[] keyBlockArray = new string[captureCount];
    for (int i = 0; i < captureCount; i++)
    {
        keyBlockArray[i] = match.Groups[1].Captures[i].Value;
        indexNameArray[i] = match.Groups[2].Captures[i].Value;
    }
    Array.Sort(indexNameArray, keyBlockArray);
    return String.Join("\n  ", keyBlockArray);
}

match.Groups[i].Captures lets us access the multiple captures of a single group. Since these are Capture objects which do not seem really useful right now, we build two string arrays from their values. Then we use Array.Sort which sorts two arrays based on the values of one (which is considered the key). As the "key" we use the capturing of the table name. As the "value" we use the full capture of one complete KEY ..., block. This sorts the full blocks by their names. Then we can simply join together the blocks, add in the whitespace separator that was used before and return them.

于 2012-11-10T01:15:32.023 に答える
1

質問を完全に理解しているかどうかはわかりませんが、foreachを次のように変更します。

foreach (Match match in keys.Cast<Match>().OrderBy(m => m.Value))

あなたがしたいことをしますか?

于 2012-11-10T00:34:25.133 に答える