正则表达式 C# 用多个结果替换匹配的字段

Regex C# replace matched fields with multiple results

我有这段文字:

iif(instr(|Wellington, New Zealand|,|,|)>0,|Wellington, New Zealand|,|Wellington, New Zealand| & |, | & |New Zealand|) & | to | & iif(instr(|Jeddah, Saudi Arabia|,|,|)>0,|Jeddah, Saudi Arabia|,|Jeddah, Saudi Arabia| & |, | & |Saudi Arabia|) & iif(|Jeddah, Saudi Arabia|=||,||,| via | & |Jeddah, Saudi Arabia|)

我可以对文本(下方)进行正则表达式以获得 | 字符之间的所有元素的集合。我得到 18 场比赛,比赛 #1 是 |,|

MatchCollection fields = Regex.Matches(str, @"\|.*?\|");

然后我想用 ~0~~1~~2~ 等占位符替换每个匹配项,直到 ~17~ 这样我就可以 运行 我的其余代码。我不在乎是否所有常用文本都替换为相同的占位符,如果我使用所有 18 个,这会在占位符中留下空白。

我的问题是我不能直接替换字符串 |Jeddah, Saudi Arabia|,|,| 的这一部分中的元素 #1 (|,|) 将替换它找到的第一个实例,其中正则表达式正确地将 |Jeddah, Saudi Arabia| 识别为一个匹配项,将 |,| 识别为另一个匹配项。

我求的结果是这样的:

iif(instr(~0~,~1~)>0,~0~,~0~ & ~2~ & ~3~) & ~4~ & iif(instr(~5~,~1~)>0,~5~,~5~ & ~2~ & ~6~) & iif(~5~=~7~,~7~,~8~ & ~5~)

一旦我知道我有多少匹配项,就会在我构建的数组中增加数字。我保留原始值并稍后将它们交换回来,这是简单的部分。

\|,\|(?=[^(|]*(\|[^(|]*\|)*[^(|]*\))

您可以使用 lookahead 来检查 |,| 被捕获以替换 ) 之前是否没有遗漏 |。参见演示。

https://regex101.com/r/mT0iE7/14

嗯...有点难以解释,但基本上是根据您所获得的构建...

鉴于 匹配项 ,我将它们添加到列表中并使用 Distinct LINQ 函数获得唯一的匹配项,并使用 OrderBy 从最长到最短的顺序对它们进行排序LINQ 函数。然后循环遍历生成的 RouteMap 并替换原始字符串。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

public class Program
{
    public static void Main()
    {
        var input = "iif(instr(|Wellington, New Zealand|,|,|)>0,|Wellington, New Zealand|,|Wellington, New Zealand| & |, | & |New Zealand|) & | to | & iif(instr(|Jeddah, Saudi Arabia|,|,|)>0,|Jeddah, Saudi Arabia|,|Jeddah, Saudi Arabia| & |, | & |Saudi Arabia|) & iif(|Jeddah, Saudi Arabia|=||,||,| via | & |Jeddah, Saudi Arabia|)";

        Console.WriteLine(input);

        var re = new Regex(@"\|.*?\|");

        var matches = re.Matches(input);

        var mz = new List<string>();

        foreach(Match m in matches) 
        {
            mz.Add(m.Groups[0].ToString());
        }

        var routeMap = mz.Distinct().OrderByDescending(n => n.Length).ToList(); //Get distinct, and sort it longest to shortest... need it this way or it won't do the replacement correctly.

        for (var i = 0; i < routeMap.Count; i++) 
        {
            input = input.Replace(routeMap[i], string.Format("~{0}~", i));          
        }

        Console.WriteLine(input);

        Console.WriteLine();
        Console.WriteLine("The route map replacement key:");
        var idx = 0;
        routeMap.ForEach(m => Console.WriteLine("{0}: {1}", idx++, m));

    }
}

运行 样本位于:https://dotnetfiddle.net/PIuuae

我想出了一个建议来获得你的第二个输出选项。

您可以使用 MatchEvaluator 将匹配传递给单独的方法,并在该方法中增加一个 "global" 计数器:

    public string ReplaceMatch(Match m)
    {
        i++;
        return "~" + i.ToString() + "~";

    }
    public static int i = -1;
    // ... then, in your calling method

    var txt = "iif(instr(|Wellington, New Zealand|,|,|)>0,|Wellington, New Zealand|,|Wellington, New Zealand| & |, | & |New Zealand|) & | to | & iif(instr(|Jeddah, Saudi Arabia|,|,|)>0,|Jeddah, Saudi Arabia|,|Jeddah, Saudi Arabia| & |, | & |Saudi Arabia|) & iif(|Jeddah, Saudi Arabia|=||,||,| via | & |Jeddah, Saudi Arabia|)";
    var fields = Regex.Matches(txt, @"\|.*?\|");
    var txt2 = Regex.Replace(txt, @"\|.*?\|", new MatchEvaluator(ReplaceMatch));

输出:

iif(instr(~0~,~1~)>0,~2~,~3~ & ~4~ & ~5~) & ~6~ & iif(instr(~7~,~8~)>0,~9~,~10~ & ~11~ & ~12~) & iif(~13~=~14~,~15~,~16~ & ~17~)

与这些占位符对应的匹配值保存在 fields 变量中,因此稍后您将能够匹配它们。

编辑:对于选项 1(这是您编辑问题后的唯一选项),答案是创建一个包含不同项目的字典,并在替换中使用它方法:

var txt = "iif(instr(|Wellington, New Zealand|,|,|)>0,|Wellington, New Zealand|,|Wellington, New Zealand| & |, | & |New Zealand|) & | to | & iif(instr(|Jeddah, Saudi Arabia|,|,|)>0,|Jeddah, Saudi Arabia|,|Jeddah, Saudi Arabia| & |, | & |Saudi Arabia|) & iif(|Jeddah, Saudi Arabia|=||,||,| via | & |Jeddah, Saudi Arabia|)";
var fields = Regex.Matches(txt, @"\|.*?\|").Cast<Match>().Select(p=> p.Value).Distinct().Select((s, i) => new { s, i }).ToDictionary(x => x.s, x => x.i);
var txt3 = Regex.Replace(txt, @"\|.*?\|", m => string.Format("~{0}~", fields[m.Value]));

输出:

iif(instr(~0~,~1~)>0,~0~,~0~ & ~2~ & ~3~) & ~4~ & iif(instr(~5~,~1~)>0,~5~,~5~ & ~2~ & ~6~) & iif(~5~=~7~,~7~,~8~ & ~5~)

我会使用一些 lambda 函数:

// This one gets the index from the list of matches
private static string LookupReplace(string text, List<string> newList)
{
    var result = "~" + newList.IndexOf(text).ToString() + "~";
    return result;
}

// This one just increments a global counter
private static string NumberedReplace()
{
    i++;
    return "~" + i.ToString() + "~";
}

public static int i = -1;

public static void Main()
{   
    string text = "iif(instr(|Wellington, New Zealand|,|,|)>0,|Wellington, New Zealand|,|Wellington, New Zealand| & |, | & |New Zealand|) & | to | & iif(instr(|Jeddah, Saudi Arabia|,|,|)>0,|Jeddah, Saudi Arabia|,|Jeddah, Saudi Arabia| & |, | & |Saudi Arabia|) & iif(|Jeddah, Saudi Arabia|=||,||,| via | & |Jeddah, Saudi Arabia|)";
    var re = new Regex(@"\|.*?\|");
    var newList = re.Matches(text)
                    .OfType<Match>()
                    .Select(m => m.Value)
                    .ToList();
    // First replace with index
    string result = re.Replace(text, x => LookupReplace(x.Value, newList));
    Console.WriteLine(result);

    // Second replace with counter
    result = re.Replace(text, x => NumberedReplace());
    Console.WriteLine(result);
}

ideone demo

每次替换的输出:

iif(instr(~0~,~1~)>0,~0~,~0~ & ~4~ & ~5~) & ~6~ & iif(instr(~7~,~1~)>0,~7~,~7~ & ~4~ & ~12~) & iif(~7~=~14~,~14~,~16~ & ~7~)
iif(instr(~0~,~1~)>0,~2~,~3~ & ~4~ & ~5~) & ~6~ & iif(instr(~7~,~8~)>0,~9~,~10~ & ~11~ & ~12~) & iif(~13~=~14~,~15~,~16~ & ~17~)