如何分别替换组的每个捕获?

我有一个正则表达式,它在捕获中使用GroupCollection来捕获一组项目标识(可以用逗号分隔,也可以用最后一个词来表示“and”):

(bItem #(?<ITEMID>d+))|(,s?(?<ITEMID>d+))|(,?sands(?<ITEMID>d+))

有没有一种简单的方法使用C#的正则Regex类来替换ITEMID号码的URL? 现在,我有以下几点:

foreach (Match match in matches)
{
    var group = match.Groups["ITEMID"];
    var address = String.Format(UnformattedAddress, group.Value);

    CustomReplace(ref myString, group.Value, address,
        group.Index, (group.Index + group.Length));
}

public static int CustomReplace(ref string source, string org, string replace,
    int start, int max)
{
    if (start < 0) throw new System.ArgumentOutOfRangeException("start");
    if (max <= 0) return 0;

    start = source.IndexOf(org, start);

    if (start < 0) return 0;

    var sb = new StringBuilder(source, 0, start, source.Length);

    var found = 0;
    while (max-- > 0)
    {
        var index = source.IndexOf(org, start);

        if (index < 0) break;

        sb.Append(source, start, index - start).Append(replace);
        start = index + org.Length;
        found++;
    }

    sb.Append(source, start, source.Length - start);
    source = sb.ToString();

    return found;
}

我在网上找到的CustomReplace方法是一种简单的方法,用一个字符串源中的另一个字符串替换一个字符串。 问题是我确信可能有更简单的方法,可能使用Regex类来根据需要替换GroupCollection 。 我无法弄清楚那是什么。 谢谢!

示例文本:

Hello the items you are looking for are Item #25, 38, and 45. They total 100 dollars.

253845应该与我创建的URL字符串(这是一个HTML字符串)所取代。


你的模式适用于你的输入,但它确实有一个错误。 具体来说,它会匹配输入中出现在逗号或单词“和”后面的任何数字。

我继续并重写了你的模式以避免这个问题。 为了实现这一点,我实际上使用了两种正则表达式模式。 可以使用一种模式来解决这个问题,但它比我选择分享的方法相当复杂,可读性差。

主要模式是: bItem #d+(?:,? d+)*(?:,? and d+)? 这里没有使用捕获组,因为我只对匹配项目感兴趣。 (?: ... )位是一个非捕获组。 (?:,? d+)*的用法是在字符串的中间部分匹配多个逗号分隔值。

一旦项目匹配,我使用Regex.Replace格式化项目,然后重新构建字符串以用格式化项目替换原始项目。

以下是一些有关不同输入的示例:

string[] inputs =
{
    "Hello the items you are looking for are Item #25, 38, 22, and 45. They total 100 dollars.",
    "... Item #25, 38 and 45. Other numbers 100, 20, and 30 untouched.",
    "Item #25, and 45",
    "Item #25 and 45",
    "Item #25"
};

string pattern = @"bItem #d+(?:,? d+)*(?:,? and d+)?";
string digitPattern = @"(d+)";
// $1 refers to the first (and only) group in digitPattern
string replacement = @"<a href=""http://url/$1.html"">$1</a>";

foreach (var input in inputs)
{
    Match m = Regex.Match(input, pattern);
    string formatted = Regex.Replace(m.Value, digitPattern, replacement);
    var builder = new StringBuilder(input)
                        .Remove(m.Index, m.Length)
                        .Insert(m.Index, formatted);
    Console.WriteLine(builder.ToString());
}

如果您需要使用现有方法来格式化URL,而不是使用正则表达式替换模式,则可以使用接受MatchEvaluatorRegex.Replace重载。 这可以使用lambda来实现,并且比MSDN文档中显示的繁琐方法更好。

例如,假设您有一个FormatItem方法接受一个字符串并返回一个格式化的字符串:

public string FormatItem(string item)
{
    return String.Format("-- {0} --", item);
}

要使用FormatItem ,可以使用以下代码更改早期代码示例中使用的Regex.Replace方法:

string formatted = Regex.Replace(m.Value, digitPattern,
                       d => FormatItem(d.Value));

这里是所需语法的一个例子,同时也表明您可以通过回调将其放回到C#中。

MatchEvaluator在Regex.Replace中如何工作?


你似乎一次从两个方向来到这里。 一方面,你有一个带有三个捕获组的正则表达式,所以你期望解决方案涉及一个GroupCollection。 另一方面,所有三个组都有相同的名称,所以也许你必须将它们作为同一组的单独捕获 - 即CaptureCollection。 实际上,你可能不需要他们中的任何一个。 这是你的正则表达式(经过一些美学调整):

string source = @"Total cost for Item #25, 38, and 45 is 100 dollars.";

Regex regex1 = new Regex(
    @"bItem #(?<ITEMID>d+)|,s*(?<ITEMID>d+)|,?s+ands+(?<ITEMID>d+)",
    RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);

foreach (Match m in regex1.Matches(source)) {
  Console.WriteLine(m.Groups["ITEMID"].Value);
}

它输出253845按预期方式。 每个选项都有自己的捕获组副本,但其中只有一个参与每场比赛。 这是.NET正则表达式的一个显着特征; 其他一些提供特殊的设置或组构造,允许您重用组名,但没有一个使它像.NET一样简单。 但是,在这种情况下,你并不需要它。 你可以像这样合并替代方案:

@"(bItem #|,s*|,?s+ands+)(?<ITEMID>d+)"

然而,你的正则表达式有一个问题,如果将源字符串更改为:

@"Total cost for Item #25, 38, and 45 is 1,500 dollars and 42 cents."

现在的输出是25384550042 。 为了防止这些误报,您需要确保每个不以Item #开头的比赛,从最后一场比赛结束时开始。 为此,您可以使用G

@"(bItem #|G,?s+ands+|G,s*)(?<ITEMID>d+)"

(为了效率的缘故,我也换了最后两个选项的顺序。)把所有这些放在一起,我们只有另一个正则表达式替换。

string source =
   @"Total cost for Item #25, 38, and 45 is 1,500 dollars and 42 cents.";
Regex regex2 = new Regex(
    @"(?<TEXT>bItem #|G,?s+ands+|G,s*)(?<ITEMID>d+)",
    RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
string result = regex2.Replace(source, 
    @"${TEXT}<a href='URL_${ITEMID}'>${ITEMID}</a>");
Console.WriteLine(result);

不需要明确使用GroupCollections或CaptureCollections,除非您的替换比这更复杂,可能也不需要MatchEvaluator。

链接地址: http://www.djcxy.com/p/74811.html

上一篇: How to replace each Capture of a Group individually?

下一篇: Referencing nested groups in JavaScript using string replace using regex