简体   繁体   English

用值替换正则表达式中的命名组

[英]Replace named group in regex with value

I want to use regular expression same way as string.Format.我想使用与 string.Format 相同的正则表达式。 I will explain我会解释

I have:我有:

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)));

This works, but i must provide "input" to regex.Replace.这有效,但我必须向 regex.Replace 提供“输入”。 I do not want that.我不要那个。 I want to use pattern for matching but also for creating strings same way as with string format, replacing named group "ID" with value.我想使用模式进行匹配,但也想以与字符串格式相同的方式创建字符串,用值替换命名组“ID”。 Is that possible?那可能吗?

I'm looking for something like:我正在寻找类似的东西:

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

Result will contain "abc_999_def".结果将包含“abc_999_def”。 How to accomplish this?如何做到这一点?

Yes, it is possible:对的,这是可能的:

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;
    }
}

Usage:用法:

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

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

Result is: abc_456_def结果是:abc_456_def

No, it's not possible to use a regular expression without providing input.不,不能在不提供输入的情况下使用正则表达式。 It has to have something to work with, the pattern can not add any data to the result, everything has to come from the input or the replacement.它必须有一些东西可以使用,模式不能向结果添加任何数据,一切都必须来自输入或替换。

Intead of using String.Format, you can use a look behind and a look ahead to specify the part between "abc_" and "_def", and replace it: Intead 使用 String.Format,您可以使用后视和前视来指定“abc_”和“_def”之间的部分,并将其替换:

string result = Regex.Replace(input, @"(?<=abc_)\d+(?=_def)", "999");

There was a problem in user1817787 answer and I had to make a modification to the ReplaceNamedGroup function as follows. 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;
}

Another edited version of the original method by @user1817787, this one supports multiple instances of the named group (also includes similar fix to the one @Justin posted (returns result using {match.Index, match.Length} instead of {0, input.Length})): @user1817787 对原始方法的另一个编辑版本,该版本支持命名组的多个实例(还包括与 @Justin 发布的类似修复程序(使用 {match.Index, match.Length} 而不是 {0, input 。长度})):

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();
}

I shortened ReplaceNamedGroup, still supporting multiple captures.我缩短了 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;
}

The simple solution is to refer to the matched groups in replacement.简单的解决方案是在替换中引用匹配的组。 So the Prefix is $1 and Postfix is $3 .所以 Prefix 是$1 ,Postfix 是$3

I've haven't tested the code below but should work similar to a regEx I've written recently:我还没有测试下面的代码,但应该与我最近编写的正则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)));

In case this helps anyone, I enhanced the answer with the ability to replace multiple named capture groups in one go, which this answer helped massively to achieve.万一这对任何人都有帮助,我通过一次性替换多个命名捕获组的能力增强了答案,这个答案极大地帮助实现了这一点。

public static class RegexExtensions
    {
        public static string Replace(this string input, Regex regex, Dictionary<string, string> captureGroupReplacements)
        {
            string temp = input;

            foreach (var key in captureGroupReplacements.Keys)
            {
                temp = regex.Replace(temp, m =>
                {
                    return ReplaceNamedGroup(key, captureGroupReplacements[key], m);
                });
            }
            return temp;
        }

        private static string ReplaceNamedGroup(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;
        }
    }

Usage:用法:

var regex = new Regex(@"C={BasePath:""(?<basePath>[^\""].*)"",ResultHeadersPath:""ResultHeaders"",CORS:(?<cors>true|false)");

content = content.Replace(regex, new Dictionary<string, string>
{
   { "basePath", "www.google.com" },
   { "cors", "false" }
};

All credit should go to user1817787 for this one.所有功劳都应该归功于 user1817787。

You should check the documentation about RegEx replace here您应该在此处查看有关 RegEx 替换的文档

I created this to replace a named group.我创建它是为了替换一个命名组。 I cannot use solution that loop on all groups name because I have case where not all expression is grouped.我不能使用在所有组名上循环的解决方案,因为我的情况并非所有表达式都被分组。

    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);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM