用正则表达式替换正则表达式

时间:2015-04-15 23:18:34

标签: c# regex

考虑两个正则表达式:

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

我希望能够使用regex_A作为输入替换字符串,并将regex_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 for 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);

我不知道这是否存在,或者如何调用它。谷歌搜索找到了各种其他正则表达式替换...不是这一个。

更新

所以基本上我需要一种方法将正则表达式转换为格式字符串。

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函数应该是什么样的?

更新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成为一个悬而未决的问题。

4 个答案:

答案 0 :(得分:2)

一个正则表达式没有定义的方法来引用在另一个正则表达式中找到的匹配项。正则表达式不是格式字符串。

您可以做的是将Tuple的格式字符串与其正则表达式一起使用。 e.g。

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

答案 1 :(得分:2)

由于您已将问题缩小到需要将Regex更改为字符串格式(实施Regex2Format),因此我将把重点放在该部分上。请注意,我的答案是不完整的,因为它没有解决解析正则表达式捕获组的全部问题,但它适用于简单的情况。

首先需要的是与Regex捕获组匹配的正则表达式。有一个负面的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);
    });
}

答案 2 :(得分:1)

这样的事情可能会起作用

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

答案 3 :(得分: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);