2011-02-14 19 views
15

¿Hay alguna manera de contar la cantidad de reemplazos que hace una llamada Regex.Replace?Count regex reemplaza (C#)

E.g. para Regex.Replace("aaa", "a", "b"); Quiero sacar el número 3 (el resultado es "bbb"); para Regex.Replace("aaa", "(?<test>aa?)", "${test}b"); Quiero sacar el número 2 (el resultado es "aabab").

maneras en que puedo pensar para hacer esto:

  1. Utilice un MatchEvaluator que se incrementa una variable capturado, haciendo la sustitución manual
  2. Obtener una MatchCollection e iterar él, haciendo la sustitución manualmente y mantener un recuento
  3. Buscar primero y obtener un MatchCollection, obtener el recuento de eso, a continuación, hacer sustituir una por separado

Métodos 1 y 2 requieren análisis manual de los $ reemplazar El método 3 requiere que la expresión regular coincida dos veces con la cadena. ¿Hay una mejor manera?

+0

Esto es para una utilidad de línea de comandos simple que podría llamarse con cualquier búsqueda de expresiones regulares y reemplazar patrones como argumentos de línea de comandos. Entonces, idealmente, querría una solución genérica que no asuma el conocimiento del patrón de antemano. Aunque esto es realmente interesante, ¿cuál es la mejor manera de hacerlo en .Net? Parece que el enfoque MatchEvaluator con el análisis manual de $ reemplazos es el camino a seguir, pero es un poco complicado :( –

+0

Simon, mira mi edición. – Chev

Respuesta

13

Gracias tanto a Chevex como a Guffa. Empecé a buscar una forma mejor de obtener los resultados y descubrí que hay un método de resultado en la clase Match que realiza la sustitución. Esa es la pieza faltante del rompecabezas. Código de ejemplo a continuación:

using System.Text.RegularExpressions; 

namespace regexrep 
{ 
    class Program 
    { 
     static int Main(string[] args) 
     { 
      string fileText = System.IO.File.ReadAllText(args[0]); 
      int matchCount = 0; 
      string newText = Regex.Replace(fileText, args[1], 
       (match) => 
       { 
        matchCount++; 
        return match.Result(args[2]); 
       }); 
      System.IO.File.WriteAllText(args[0], newText); 
      return matchCount; 
     } 
    } 
} 

Con un aaa archivo test.txt que contiene, la línea de comandos regexrep test.txt "(?<test>aa?)" ${test}b fijará% errorlevel% a 2 y cambiar el texto a aabab.

+1

(Coincidencia de concordancia) se puede simplificar a solo el nombre de la variable "coincidencia" ya que el tipo está implícito. – Chev

+0

Gracias Chevex, editado. –

+0

No estaba al tanto del método Result() en el objeto de coincidencia. Esa es una buena forma rápida y fácil de ejecutar algunas funcionalidades en cada coincidencia sin dejar que Reemplazar() haga su trabajo. Buen trabajo Simon. – Chev

3

Esto debería hacerlo.

 int count = 0; 
    string text = Regex.Replace(text, 
      @"(((http|ftp|https):\/\/|www\.)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)", //Example expression. This one captures URLs. 
      match => 
      { 
       string replacementValue = String.Format("<a href='{0}'>{0}</a>", match.Value); 
       count++; 
       return replacementValue; 
      }); 

no estoy en mi equipo dev así que no puedo hacerlo ahora, pero voy a experimentar más tarde y ver si hay una manera de hacer esto con expresiones lambda en lugar de declarar el método IncrementCount() solo para incrementar un int.

EDIT modificado para usar una expresión lambda en lugar de declarar otro método.

EDIT2 Si no conoce el patrón de antemano, puede obtener todas las agrupaciones (los $ grupos a los que se refiere) dentro del objeto de coincidencia, ya que se incluyen como GroupCollection. De este modo:

 int count = 0; 
    string text = Regex.Replace(text, 
      @"(((http|ftp|https):\/\/|www\.)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)", //Example expression. This one captures URLs. 
      match => 
      { 
       string replacementValue = String.Format("<a href='{0}'>{0}</a>", match.Value); 
       count++; 
       foreach (Group g in match.Groups) 
       { 
        g.Value; //Do stuff with g.Value 
       } 
       return replacementValue; 
      }); 
+0

Esto funcionará (¡gracias!), Pero es básicamente mi método 1.Para que funcione con entradas y salidas genéricas, necesitaría analizar el $ {test} en el reemplazo, por lo que necesitamos algo más complicado (el "análisis manual de $ reemplazos" al que me refiero). –

+0

Los objetos coincidentes incluyen los $ groups como GroupCollection adjuntos. Si no conoce la expresión de antemano y no sabe cuántos grupos se incluirán, recorra la colección de grupos como lo hace mi EDIT2. No hago nada con el valor del grupo en la respuesta, pero debería ser fácil para ti ver cómo pudiste. – Chev

7

Puede utilizar un MatchEvaluator que se ejecuta para cada sustitución, de esa manera se puede contar el número de veces que se produce:

int cnt = 0; 
string result = Regex.Replace("aaa", "a", m => { 
    cnt++; 
    return "b"; 
}); 

El segundo caso es más complicado ya que tiene que producir el mismo resultado que el patrón de reemplazo sería:

int cnt = 0; 
string result = Regex.Replace("aaa", "(?<test>aa?)", m => { 
    cnt++; 
    return m.Groups["test"] + "b"; 
}); 
+1

Y aquí está la lambda equivalente a mi respuesta: 3 – Chev

+1

@Chevex: Me tomó un poco más de tiempo para responder, ya que probé el código primero. ;) – Guffa

+0

Vea mi comentario sobre la respuesta de Chevex: esto solo funcionará si conoce los patrones de antemano. De lo contrario, debe analizar la cadena de reemplazo de expresiones regulares. –