¿Qué código usaría para convertir una expresión similar a SQL en una expresión regular sobre la marcha?
Pregunta
Estoy buscando convertir una declaración similar a SQL sobre la marcha a la expresión regular equivalente, es decir.
LIKE '%this%'
LIKE 'Sm_th'
LIKE '[C-P]arsen'
¿Cuál es el mejor enfoque para hacer esto?
PDEstoy buscando hacer esto en .Net Framework (C#).
Solución
La siguiente expresión regular convierte un patrón similar a SQL en un patrón de expresión regular con la ayuda de un MatchEvaluator
delegar.Maneja correctamente los bloques de corchetes y escapa de los caracteres Regex especiales.
string regexPattern = Regex.Replace(
likePattern,
@"[%_]|\[[^]]*\]|[^%_[]+",
match =>
{
if (match.Value == "%")
{
return ".*";
}
if (match.Value == "_")
{
return ".";
}
if (match.Value.StartsWith("[") && match.Value.EndsWith("]"))
{
return match.Value;
}
return Regex.Escape(match.Value);
});
Otros consejos
Además de la solución de @ Nathan-Baulch, puede usar el siguiente código para manejar también el caso en el que se ha definido un carácter de escape personalizado utilizando el LIKE '!%' ESCAPE '!'
sintaxis.
public Regex ConvertSqlLikeToDotNetRegex(string regex, char? likeEscape = null)
{
var pattern = string.Format(@"
{0}[%_]|
[%_]|
\[[^]]*\]|
[^%_[{0}]+
", likeEscape);
var regexPattern = Regex.Replace(
regex,
pattern,
ConvertWildcardsAndEscapedCharacters,
RegexOptions.IgnorePatternWhitespace);
regexPattern = "^" + regexPattern + "$";
return new Regex(regexPattern,
!m_CaseSensitive ? RegexOptions.IgnoreCase : RegexOptions.None);
}
private string ConvertWildcardsAndEscapedCharacters(Match match)
{
// Wildcards
switch (match.Value)
{
case "%":
return ".*";
case "_":
return ".";
}
// Remove SQL defined escape characters from C# regex
if (StartsWithEscapeCharacter(match.Value, likeEscape))
{
return match.Value.Remove(0, 1);
}
// Pass anything contained in []s straight through
// (These have the same behaviour in SQL LIKE Regex and C# Regex)
if (StartsAndEndsWithSquareBrackets(match.Value))
{
return match.Value;
}
return Regex.Escape(match.Value);
}
private static bool StartsAndEndsWithSquareBrackets(string text)
{
return text.StartsWith("[", StringComparison.Ordinal) &&
text.EndsWith("]", StringComparison.Ordinal);
}
private bool StartsWithEscapeCharacter(string text, char? likeEscape)
{
return (likeEscape != null) &&
text.StartsWith(likeEscape.ToString(), StringComparison.Ordinal);
}
Según el ejemplo anterior, lo atacaría así (hablo en términos generales porque no conozco C#):
Romperlo por COMO '...', Pon el ... piezas en una matriz.Reemplazar sin escape % signos por .*, subrayado por ., y en este caso el [C-P]arsen se traduce directamente en expresiones regulares.
Vuelva a unir las piezas de la matriz con una tubería y envuelva el resultado entre paréntesis y bits de expresiones regulares estándar.
El resultado sería:
/^(.*this.*|Sm.th|[C-P]arsen)$/
Lo más importante aquí es tener cuidado con todas las formas en que puede escapar de los datos y qué comodines se traducen en qué expresiones regulares.
% becomes .*
_ becomes .
Encontré un módulo Perl llamado Regexp::Comodines.Puedes intentar portarlo o probar Perl.NET.Tengo la sensación de que tú también puedes escribir algo tú mismo.