Reemplace solo algunos grupos con Regex

191

Supongamos que tengo la siguiente expresión regular:

-(\d+)-

y quiero reemplazar, usando C #, el Grupo 1 (\d+)con AA, para obtener:

-AA-

Ahora lo estoy reemplazando usando:

var text = "example-123-example";
var pattern = @"-(\d+)-";
var replaced = Regex.Replace(text, pattern, "-AA-"); 

Pero realmente no me gusta esto, porque si cambio el patrón para que coincida _(\d+)_, también tendré que cambiar la cadena de reemplazo _AA_, y esto va en contra del principio DRY.

Estoy buscando algo como:

Mantenga el texto coincidente exactamente como está, pero cambie el Grupo 1 por this texty el Grupo 2 por another text...

Editar:
Eso fue solo un ejemplo. Solo estoy buscando una forma genérica de hacer lo que dije anteriormente.

Debería funcionar para:

anything(\d+)more_text y cualquier patrón que puedas imaginar.

Todo lo que quiero hacer es reemplazar solo grupos y mantener el resto del partido.

Oscar Mederos
fuente

Respuestas:

307

Una buena idea podría ser encapsular todo dentro de los grupos, sin importar si es necesario identificarlos o no. De esa manera, puede usarlos en su cadena de reemplazo. Por ejemplo:

var pattern = @"(-)(\d+)(-)";
var replaced = Regex.Replace(text, pattern, "$1AA$3"); 

o usando un MatchEvaluator:

var replaced = Regex.Replace(text, pattern, m => m.Groups[1].Value + "AA" + m.Groups[3].Value);

Otra forma, un poco desordenada, podría ser usar un lookbehind / lookahead:

(?<=-)(\d+)(?=-)

bluepnume
fuente
17
Edité tu respuesta para proporcionar más información, pero lo que dijiste es totalmente correcto. No sé cómo extrañé que podría poner todo dentro de los grupos, sin importar si los usaría o no :) . En mi opinión, esa solución es mucho mejor y más limpia que usar lookahead y lookbehinds.
Oscar Mederos
pequeño error tipográfico, su patrón de reemplazo debe ser $ 1AA $ 3
Myster
1
Para que esto funcione, tuve que agregar .Valuea m.Groups[1]etc.
jbeldock
11
También vale la pena señalar: si su texto de reemplazo comienza con un número, ¡la primera solución ("$ 1AA $ 3") no funcionará según lo previsto!
Bertie
2
@OscarMederos también puede usar grupos que no capturan, bueno para grupos que no usa. En (?:foo)(bar), $1reemplazará bar. más detalles
Patrick
34

Puedes hacer esto usando lookahead y lookbehind :

var pattern = @"(?<=-)\d+(?=-)";
var replaced = Regex.Replace(text, pattern, "AA"); 
LukeH
fuente
19

También tenía necesidad de esto y creé el siguiente método de extensión para ello:

public static class RegexExtensions
{
    public static string ReplaceGroup(
        this Regex regex, string input, string groupName, string replacement)
    {
        return regex.Replace(
            input,
            m =>
            {
                var group = m.Groups[groupName];
                var sb = new StringBuilder();
                var previousCaptureEnd = 0;
                foreach (var capture in group.Captures.Cast<Capture>())
                {
                    var currentCaptureEnd =
                        capture.Index + capture.Length - m.Index;
                    var currentCaptureLength =
                        capture.Index - m.Index - previousCaptureEnd;
                    sb.Append(
                        m.Value.Substring(
                            previousCaptureEnd, currentCaptureLength));
                    sb.Append(replacement);
                    previousCaptureEnd = currentCaptureEnd;
                }
                sb.Append(m.Value.Substring(previousCaptureEnd));

                return sb.ToString();
            });
    }
}

Uso:

var input = @"[assembly: AssemblyFileVersion(""2.0.3.0"")][assembly: AssemblyFileVersion(""2.0.3.0"")]";
var regex = new Regex(@"AssemblyFileVersion\(""(?<version>(\d+\.?){4})""\)");


var result = regex.ReplaceGroup(input , "version", "1.2.3");

Resultado:

[assembly: AssemblyFileVersion("1.2.3")][assembly: AssemblyFileVersion("1.2.3")]
Daniel Hilgarth
fuente
Me gusta esta implementación pero no reemplaza múltiples coincidencias. Publiqué una versión que sí
Vladimir
13

Si no desea cambiar su patrón, puede usar las propiedades Índice de grupo y Longitud de un grupo coincidente.

var text = "example-123-example";
var pattern = @"-(\d+)-";
var regex = new RegEx(pattern);
var match = regex.Match(text);

var firstPart = text.Substring(0,match.Groups[1].Index);    
var secondPart = text.Substring(match.Groups[1].Index + match.Groups[1].Length);
var fullReplace = firstPart + "AA" + secondPart;
Dick Verweij
fuente
Tenga en cuenta que esto supone y solo funcionará durante la primera aparición del partido.
Bartosz
5

Aquí hay otra buena opción de limpieza que no requiere cambiar su patrón.

        var text = "example-123-example";
        var pattern = @"-(\d+)-";

        var replaced = Regex.Replace(text, pattern, (_match) =>
        {
            Group group = _match.Groups[1];
            string replace = "AA";
            return String.Format("{0}{1}{2}", _match.Value.Substring(0, group.Index - _match.Index), replace, _match.Value.Substring(group.Index - _match.Index + group.Length));
        });
genio de pelo rizado
fuente
0

revise la codificación a continuación para obtener el reemplazo de grupo separado.

new_bib = Regex.Replace(new_bib, @"(?s)(\\bibitem\[[^\]]+\]\{" + pat4 + @"\})[\s\n\v]*([\\\{\}a-zA-Z\.\s\,\;\\\#\\\$\\\%\\\&\*\@\\\!\\\^+\-\\\=\\\~\\\:\\\" + dblqt + @"\\\;\\\`\\\']{20,70})", delegate(Match mts)
                    {
                           var fg = mts.Groups[0].Value.ToString(); 
                           var fs = mts.Groups[1].Value.ToString();
                           var fss = mts.Groups[2].Value.ToString();
                               fss = Regex.Replace(fss, @"[\\\{\}\\\#\\\$\\\%\\\&\*\@\\\!\\\^+\-\\\=\\\~\\\:\\\" + dblqt + @"\\\;\\\`\\\']+", "");
                           return "<augroup>" + fss + "</augroup>" + fs;
                    }, RegexOptions.IgnoreCase);
BalaS
fuente
0

Aquí hay una versión similar a la de Daniel pero que reemplaza varios partidos:

public static string ReplaceGroup(string input, string pattern, RegexOptions options, string groupName, string replacement)
{
    Match match;
    while ((match = Regex.Match(input, pattern, options)).Success)
    {
        var group = match.Groups[groupName];

        var sb = new StringBuilder();

        // Anything before the match
        if (match.Index > 0)
            sb.Append(input.Substring(0, match.Index));

        // The match itself
        var startIndex = group.Index - match.Index;
        var length = group.Length;
        var original = match.Value;
        var prior = original.Substring(0, startIndex);
        var trailing = original.Substring(startIndex + length);
        sb.Append(prior);
        sb.Append(replacement);
        sb.Append(trailing);

        // Anything after the match
        if (match.Index + match.Length < input.Length)
            sb.Append(input.Substring(match.Index + match.Length));

        input = sb.ToString();
    }

    return input;
Vladimir
fuente