Замените именованную группу в регулярном выражении значением - PullRequest
9 голосов
/ 17 апреля 2010

Я хочу использовать регулярное выражение так же, как string.Format.Я объясню

У меня есть:

string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$";
string input = "abc_123_def";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
string replacement = "456";
Console.WriteLine(regex.Replace(input, string.Format("${{PREFIX}}{0}${{POSTFIX}}", replacement)));

Это работает, но я должен предоставить "вход" для регулярного выражения. Заменить.Я не хочу этого.Я хочу использовать шаблон для сопоставления, но также и для создания строк так же, как с форматом строки, заменив именованную группу "ID" значением.Это возможно?

Я ищу что-то вроде:

string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$";
string result = ReplaceWithFormat(pattern, "ID", 999);

Результат будет содержать "abc_999_def".Как это сделать?

Ответы [ 7 ]

17 голосов
/ 12 ноября 2012

Да, это возможно:

public static class RegexExtensions
{
    public static string Replace(this string input, Regex regex, string groupName, string replacement)
    {
        return regex.Replace(input, m =>
        {
            return ReplaceNamedGroup(input, groupName, replacement, m);
        });
    }

    private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m)
    {
        string capture = m.Value;
        capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length);
        capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement);
        return capture;
    }
}

Использование:

Regex regex = new Regex("^(?<PREFIX>abc_)(?<ID>[0-9]+)(?<POSTFIX>_def)$");

string oldValue = "abc_123_def";
var result = oldValue.Replace(regex, "ID", "456");

Результат: abc_456_def

12 голосов
/ 17 апреля 2010

Нет, использование регулярного выражения без ввода невозможно. У него должно быть что-то для работы, шаблон не может добавить какие-либо данные к результату, все должно исходить от ввода или замены.

Вместо использования String.Format, вы можете использовать взгляд назад и взгляд вперед, чтобы указать часть между "abc_" и "_def" и заменить ее:

string result = Regex.Replace(input, @"(?<=abc_)\d+(?=_def)", "999");
5 голосов
/ 22 января 2013

В ответе user1817787 возникла проблема, и мне пришлось внести изменения в функцию ReplaceNamedGroup следующим образом.

private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m)
{
    string capture = m.Value;
    capture = capture.Remove(m.Groups[groupName].Index - m.Index, m.Groups[groupName].Length);
    capture = capture.Insert(m.Groups[groupName].Index - m.Index, replacement);
    return capture;
}
1 голос
/ 08 июня 2016

Я сократил ReplaceNamedGroup, все еще поддерживая несколько захватов.

private static string ReplaceNamedGroup(string input, string groupName, string replacement, Match m)
{
  string result = m.Value;
  foreach (Capture cap in m.Groups[groupName]?.Captures)
  {
    result = result.Remove(cap.Index - m.Index, cap.Length);
    result = result.Insert(cap.Index - m.Index, replacement);
  }
return result;
}
1 голос
/ 07 июня 2014

Другая отредактированная версия исходного метода @ user1817787, эта поддерживает несколько экземпляров именованной группы (также включает в себя исправление, аналогичное опубликованному @Justin (возвращает результат, используя {match.Index, match).Length} вместо {0, input.Length})):

public static string ReplaceNamedGroup(
    string input, string groupName, string replacement, Match match)
{
    var sb = new StringBuilder(input);
    var matchStart = match.Index;
    var matchLength = match.Length;

    var captures = match.Groups[groupName].Captures.OfType<Capture>()
        .OrderByDescending(c => c.Index);

    foreach (var capt in captures)
    {
        if (capt == null)
            continue;

        matchLength += replacement.Length - capt.Length;

        sb.Remove(capt.Index, capt.Length);
        sb.Insert(capt.Index, replacement);
    }

    var end = matchStart + matchLength;
    sb.Remove(end, sb.Length - end);
    sb.Remove(0, matchStart);

    return sb.ToString();
}
0 голосов
/ 12 января 2017

Вам следует проверить документацию о замене RegEx здесь

Я создал это, чтобы заменить именованную группу. Я не могу использовать решение этого цикла для всех имен групп, потому что у меня есть случай, когда не все выражения сгруппированы.

    public static string ReplaceNamedGroup(this Regex regex, string input, string namedGroup, string value)
    {
        var replacement = Regex.Replace(regex.ToString(), 
            @"((?<GroupPrefix>\(\?)\<(?<GroupName>\w*)\>(?<Eval>.[^\)]+)(?<GroupPostfix>\)))", 
            @"${${GroupName}}").TrimStart('^').TrimEnd('$');
        replacement = replacement.Replace("${" + namedGroup + "}", value);
        return Regex.Replace(input, regex.ToString(), replacement);
    }
0 голосов
/ 25 февраля 2016

Простое решение - обратиться к соответствующим группам в замене. Таким образом, префикс $1 и постфикс $3.

Я не тестировал приведенный ниже код, но должен работать аналогично regEx, который я недавно написал:

string pattern = "^(?<PREFIX>abc_)(?<ID>[0-9])+(?<POSTFIX>_def)$";
string input = "abc_123_def";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
string replacement = String.Format("$1{0}$3", "456");
Console.WriteLine(regex.Replace(input, string.Format("${{PREFIX}}{0}${{POSTFIX}}", replacement)));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...