2015-04-15 3 views
2

Рассмотрим два регулярных выражения:Заменить регулярное выражение с регулярным выражением

var regex_A = "Main\.(.+)\.Value"; 
var regex_B = "M_(.+)_Sp"; 

Я хочу, чтобы иметь возможность заменить строку, используя regex_A в качестве входных данных, и regex_B как строка замены. Но и наоборот. И без, предоставляя дополнительную информацию, такую ​​как строка формата для каждого регулярного выражения.

В частности, я хочу создать строку replace_B из строки input_A. Итак:

var input_A = "Main.Rotating.Value"; 
var replaced_B = input_A.RegEx_Awesome_Replace(regex_A, regex_B); 
Assert.AreEqual("M_Rotating_Sp", replaced_B); 

И это также должно работать в обратном направлении (то причина я не могу использовать простой string.format для regex_B). Потому что я не хочу предоставлять строку формата для каждого регулярного выражения (я ленив).

var input_B = "M_Skew_Sp"; 
var replaced_A = input_B.RegEx_Awesome_Replace(regex_B, regex_A); 
Assert.AreEqual("Main.Skew.Value", replaced_A); 

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

Update:

Поэтому в основном я нужен способ, чтобы преобразовать регулярное выражение в строку формата.

var regex_A_format = Regex2Format(regex_A); 
Assert.AreEqual("Main.$1.Value", regex_A_format); 

и

var regex_B_format = Regex2Format(regex_B); 
Assert.AreEqual("M_$1_Sp", regex_B_format); 

Так что если RegEx_Awesome_Replace и/или функция Regex2Format выглядеть?

Update 2:

Я думаю, RegEx_Awesome_Replace должен выглядеть примерно так (с помощью кода из ответов ниже):

public static class StringExtenstions 
{ 
    public static string RegExAwesomeReplace(this string inputString,string searchPattern,string replacePattern) 
    { 
     return Regex.Replace(inputString, searchPattern, Regex2Format(replacePattern)); 
    } 
} 

Что бы оставить Regex2Format как открытый вопрос.

+0

ли Regex2Format (replacePattern) возвращает строку или другой заменить шаблон? Если он возвращает форматированную строку, как вы хотите, чтобы строка была отформатирована? и где находится информация о * форматах *? Если вы хотите, чтобы формат находился в 'replacePattern', как он выглядит? – ANewGuyInTown

+0

Вы сузили свое требование к разрешимой проблеме? –

+0

Нет, я не думаю, что регулярные выражения действительно решают любую проблему реальной жизни. Имеет смысл более просто определить оба выражения. –

ответ

2

Определенный способ для одного регулярного выражения не ссылаться на совпадение, найденное в другом регулярном выражении. Regexes не являются строками формата.

Что вы можете сделать, это использовать Tuple s строки формата вместе с ее регулярным выражением. например

var a = new Tuple<Regex,string>(new Regex(@"(?<=Main\.).+(?=\.Value)"), @"Main.{0}.Value") 
var b = new Tuple<Regex,string>(new Regex(@"(?<=M_).+(?=_Sp)"), @"M_{0}_Sp")` 

Затем вы можете передать эти объекты к общему методу замещения в любом порядке, как это:

private string RegEx_Awesome_Replace(string input, Tuple<Regex,string> toFind, Tuple<Regex,string> replaceWith) 
{ 
    return string.Format(replaceWith.Item2, toFind.Item1.Match(input).Value); 
} 

Вы заметите, что я использовал zero-width positive lookahead assertion and zero-width positive lookbehind assertions в моих регулярных выражениях, чтобы гарантировать, что Value содержит ровно текст, который я хочу заменить.

Возможно, вам захочется добавить обработку ошибок, в случаях, когда совпадение не найдено. Может быть, читать о Regex.Match

+0

Мне это нравится. Но это не то, что мне нужно. Теперь мне все равно нужно преобразовать регулярное выражение в строку формата (например, string.format). –

+1

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

+0

Вне пользовательского ввода, из файлов конфигурации. Я добавил, что мне нужен код для функций. Чтобы было ясно, что я не хочу добавлять данные. Но, в конечном счете, о его краткости из точки конфигурации. –

1

Что-то подобное может работать

var replaced_B = Regex.Replace(input_A, @"Main\.(.+)\.Value", @"M_$1_Sp"); 
+0

Также классный/более короткий способ замены параметризованного регулярного выражения. Но не то, что мне нужно. И я снова изменил свой вопрос, чтобы было более ясно, что я не хочу предоставлять/писать строку формата для каждого регулярного выражения. –

0

Вы ищете что-то вроде этого?

public static class StringExtenstions 
{ 
    public static string RegExAwesomeReplace(this string inputString,string searchPattern,string replacePattern) 
    { 
     Match searchMatch = Regex.Match(inputString,searchPattern); 
     Match replaceMatch = Regex.Match(inputString, replacePattern); 

     if (!searchMatch.Success || !replaceMatch.Success) 
     { 
      return inputString; 
     } 

     return inputString.Replace(searchMatch.Value, replaceMatch.Value); 
    } 
} 

Метод расширения строки возвращает строку с замененным значением для шаблона поиска и заменяет шаблон.

Это, как вы звоните:

input_A.RegEx_Awesome_Replace(regex_A, regex_B); 
+1

Шаблон замены несовместим со строкой ввода, они представляют собой разные форматы. Так что это не сработает. –

2

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

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

private static readonly Regex CaptureGroupMatcher = new Regex(@"(?<!\\)\([^\)]+\)"); 

Реализация Regex2Format здесь в основном пишет все вне групп захвата в выходную строку и заменяет значение группы захвата по {x}.

static string Regex2Format(string pattern) 
{ 
    var targetBuilder = new StringBuilder(); 
    int previousEndIndex = 0; 
    int formatIndex = 0; 
    foreach (Match match in CaptureGroupMatcher.Matches(pattern)) 
    { 
     var group = match.Groups[0]; 
     int endIndex = group.Index; 
     AppendPart(pattern, previousEndIndex, endIndex, targetBuilder); 
     targetBuilder.Append('{'); 
     targetBuilder.Append(formatIndex++); 
     targetBuilder.Append('}'); 
     previousEndIndex = group.Index + group.Length; 
    } 
    AppendPart(pattern, previousEndIndex, pattern.Length, targetBuilder); 
    return targetBuilder.ToString(); 
} 

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

static void AppendPart(string pattern, int previousEndIndex, int endIndex, StringBuilder targetBuilder) 
{ 
    for (int i = previousEndIndex; i < endIndex; i++) 
    { 
     char c = pattern[i]; 
     if (c == '\\' && i < pattern.Length - 1 && pattern[i + 1] != '\\') 
     { 
      //backslash not followed by another backslash - it's an escape char 
     } 
     else 
     { 
      targetBuilder.Append(c); 
     } 
    } 
} 

Тестовые

static void Test() 
{ 
    var cases = new Dictionary<string, string> 
    { 
     { @"Main\.(.+)\.Value", @"Main.{0}.Value" }, 
     { @"M_(.+)_Sp(.*)", "M_{0}_Sp{1}" }, 
     { @"M_\(.+)_Sp", @"M_(.+)_Sp" }, 
    }; 

    foreach (var kvp in cases) 
    { 
     if (PatternToStringFormat(kvp.Key) != kvp.Value) 
     { 
      Console.WriteLine("Test failed for {0} - expected {1} but got {2}", kvp.Key, kvp.Value, PatternToStringFormat(kvp.Key)); 
     } 
    } 

} 

Чтобы обернуть, вот использование:

private static string AwesomeRegexReplace(string input, string sourcePattern, string targetPattern) 
{ 
    var targetFormat = PatternToStringFormat(targetPattern); 
    return Regex.Replace(input, sourcePattern, match => 
    { 
     var args = match.Groups.OfType<Group>().Skip(1).Select(g => g.Value).ToArray<object>(); 
     return string.Format(targetFormat, args); 
    }); 
} 
Смежные вопросы