C# сортировать и возвращать регулярное выражение.спички


Существует ли какой-либо способ использования регулярных выражений.Совпадения, чтобы найти и записать обратно совпадающие значения, но в другом (алфавитном) порядке?

На данный момент у меня есть что-то вроде:

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

Console.WriteLine("nn");
foreach (Match match in keys)
{
    Console.WriteLine(match.Index + " = " + match.Value.Replace("n", "").Trim());
}
Но что мне действительно нужно, так это сесть за стол.SQL дамп и сортировка существующих индексов по алфавиту, пример кода:
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);"

Спасибо Дж


Обновление: Спасибо, M. buettner solution дал мне основы, которые я мог бы использовать, чтобы двигаться дальше. К сожалению, я не очень хорошо разбираюсь в регулярных выражениях, но в итоге у меня получился код, который я верить можно еще лучше:

...
//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*)+",
2 3

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 - это всего лишь один мощный зверь. Итак, давайте посмотрим на функцию обратного вызова и как использовать несколько захватов:

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 позволяет получить доступ к нескольким захватам одной группы. Поскольку это объекты Capture, которые сейчас не кажутся действительно полезными, мы строим два строковых массива из их значений. Тогда мы используем Array.Sort который сортирует два массива по значениям одного (который считается ключевым). В качестве "ключа" мы используем захват имени таблицы. В качестве "значения" мы используем полный захват одного полного блока KEY ...,. Это сортирует полные блоки по их названиям. Затем мы можем просто объединить блоки, добавить разделитель пробелов, который использовался ранее, и вернуть их.

Не уверен, что я полностью понимаю вопрос, но делает ли изменение foreach:

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

Делай, что хочешь?