matches - regex.match c#
usando Regex.IsMatch estático vs creando una instancia de Regex (7)
En una extraña salida de mi egotismo típico, estoy como revirtiéndome con esta respuesta.
Mi respuesta original, conservada a continuación, se basó en un examen de la versión 1.1 del .NET framework. Esto es bastante vergonzoso, ya que .NET 2.0 había estado fuera durante más de tres años en el momento de mi respuesta, y contenía cambios en la clase Regex
que afectan significativamente la diferencia entre los métodos estáticos y de instancia.
En .NET 2.0 (y 4.0), la función IsMatch
estática se define de la siguiente manera:
public static bool IsMatch(string input, string pattern){
return new Regex(pattern, RegexOptions.None, true).IsMatch(input);
}
La diferencia significativa aquí es tan poco true
como el tercer argumento. Eso corresponde a un parámetro llamado "useCache". Cuando eso es cierto, el árbol analizado se recupera de la memoria caché en el segundo y el uso posterior.
Este almacenamiento en caché consume la mayoría, pero no la totalidad, de la diferencia de rendimiento entre los métodos estático y de instancia. En mis pruebas, el método IsMatch
estático fue aproximadamente un 20% más lento que el método de instancia, pero eso solo ascendió a un aumento de medio segundo cuando se ejecutó 100 veces sobre un conjunto de 10.000 cadenas de entrada (para un total de 1 millón de operaciones).
Esta ralentización del 20% aún puede ser significativa en algunos escenarios. Si se encuentra regejando cientos de millones de cadenas, probablemente quiera dar todos los pasos que pueda para hacerlo más eficiente. Pero apostaría a que el 99% del tiempo, estás usando un Regex particular no más de un puñado de veces, y el milisegundo adicional que pierdes con el método estático no será ni siquiera cercano a notar.
Apoyos para devgeezer , quien señaló esto hace casi un año, aunque nadie pareció darse cuenta.
Mi vieja respuesta sigue:
La función IsMatch
estática se define de la siguiente manera:
public static bool IsMatch(string input, string pattern){
return new Regex(pattern).IsMatch(input);
}
Y, sí, la inicialización de un objeto Regex
no es trivial. Debería usar el IsMatch
estático (o cualquiera de las otras funciones Regex
estáticas) como un acceso directo rápido solo para patrones que usará solo una vez. Si va a reutilizar el patrón, también vale la pena reutilizar un objeto Regex
.
En cuanto a si debe o no especificar RegexOptions.Compiled
, como lo sugirió Jon Skeet, esa es otra historia. La respuesta es: depende. Para patrones simples o para patrones utilizados solo un puñado de veces, puede ser más rápido usar una instancia no compilada. Definitivamente debes perfilar antes de decidir. El costo de compilar un objeto de expresión regular es bastante grande, y puede no valer la pena.
Tome, como ejemplo, lo siguiente:
const int count = 10000;
string pattern = "^[a-z]+[0-9]+$";
string input = "abc123";
Stopwatch sw = Stopwatch.StartNew();
for(int i = 0; i < count; i++)
Regex.IsMatch(input, pattern);
Console.WriteLine("static took {0} seconds.", sw.Elapsed.TotalSeconds);
sw.Reset();
sw.Start();
Regex rx = new Regex(pattern);
for(int i = 0; i < count; i++)
rx.IsMatch(input);
Console.WriteLine("instance took {0} seconds.", sw.Elapsed.TotalSeconds);
sw.Reset();
sw.Start();
rx = new Regex(pattern, RegexOptions.Compiled);
for(int i = 0; i < count; i++)
rx.IsMatch(input);
Console.WriteLine("compiled took {0} seconds.", sw.Elapsed.TotalSeconds);
En el count = 10000
, como se indica, el segundo resultado es el más rápido. Aumente la count
a 100000
, y la versión compilada gana.
En C # debería tener código como:
public static string importantRegex = "magic!";
public void F1(){
//code
if(Regex.IsMatch(importantRegex)){
//codez in here.
}
//more code
}
public void main(){
F1();
/*
some stuff happens......
*/
F1();
}
¿o debería persistir una instancia de Regex que contenga el patrón importante? ¿Cuál es el costo de usar Regex.IsMatch? Imagino que hay un NFA creado en cada intage de Regex. Por lo que entiendo, esta creación de NFA no es trivial.
Esta respuesta ya no es correcta con respecto a las versiones de .NET que tengo en mi máquina. 4.0.30319 y 2.0.50727 tienen los siguientes para IsMatch:
public static bool IsMatch(string input, string pattern)
{
return new Regex(pattern, RegexOptions.None, true).IsMatch(input);
}
El valor ''verdadero'' es para un parámetro constructor llamado "useCache". Todos los constructores de Regex finalmente se conectan a través de este, los estáticos lo llaman directamente, pasando ''verdadero''.
Usted lee más en la publicación del blog de BCL sobre la optimización del rendimiento de Regex destacando el uso de la memoria caché de los métodos estáticos here . Este blog también cita mediciones de rendimiento. Leer series de entradas de blog sobre la optimización del rendimiento de Regex es un gran lugar para comenzar.
Estoy de acuerdo con Jon y solo para aclarar que se vería algo como esto:
static Regex regex = new Regex("regex", RegexOptions.Compiled);
También vale la pena mirar la enumeración de RegexOptions
para ver otras banderas que a veces pueden ser útiles.
Hay muchas cosas que afectarán el rendimiento de usar una expresión regular. En última instancia, la única forma de averiguar cuál es el más eficiente en su situación es midiendo, usando una situación lo más realista posible.
La página sobre compilación y reutilización de objetos de expresiones regulares en MSDN cubre esto. En resumen, dice
Las expresiones regulares compiladas tardan en compilarse y, una vez compiladas, solo se liberará su memoria en
AppDomain
. Si debe usar compilación o no, dependerá de la cantidad de patrones que esté utilizando y de la frecuencia con que se utilicen.Los métodos de
Regex
estáticosRegex
caché la representación de expresiones regulares analizadas para los últimos 15 (por defecto) patrones. Por lo tanto, si no está utilizando muchos patrones diferentes en su aplicación, o si su uso está lo suficientemente agrupado, no habrá mucha diferencia entre el almacenamiento en caché de la instancia o el almacenamiento en caché de la infraestructura.
Para una aplicación WinForm en la que estaba trabajando podríamos definir una expresión regular en caracteres válidos que se ejecutaría con cada pulsación de tecla y una validación para el texto de cualquier cuadro de texto (aplicación de entrada de datos), así que usé un caché o expresiones regulares compiladas como
private static Dictionary<string, Regex> regexCache = new Dictionary<string, Regex>(20);
Donde la expresión de expresiones regulares era la clave.
Luego tuve una función estática a la que podía llamar al validar datos:
public static bool RegExValidate(string text, string regex)
{
if (!regexCache.ContainsKey(regex))
{
Regex compiledRegex = new Regex(regex,RegexOptions.Compiled);
regexCache.Add(regex, compiledRegex);
}
return regexCache[regex].IsMatch(text);
}
Si va a reutilizar la expresión regular varias veces, la crearía con RegexOptions.Compiled
y la almacenaría en caché. No tiene sentido hacer que el marco analice el patrón de expresiones regulares cada vez que lo desee.
Sugiero que leas la publicación de Jeff sobre la compilación de Regex.
En cuanto a la pregunta, si hace esta pregunta, significa que la va a usar solo una vez. Entonces, realmente no importa ya que el desmontaje del Reflector de Regex.IsMatch es:
public static bool IsMatch(string input, string pattern, RegexOptions options)
{
return new Regex(pattern, options, true).IsMatch(input);
}