Какой код я бы использовал для преобразования выражения типа SQL в регулярное выражение на лету?
Вопрос
Я хочу преобразовать оператор типа SQL на лету в эквивалентное регулярное выражение, т.е.
LIKE '%this%'
LIKE 'Sm_th'
LIKE '[C-P]arsen'
Каков наилучший подход к этому?
P.S.Я хочу сделать это в .Net Framework (С#).
Решение
Следующее регулярное выражение преобразует шаблон, подобный SQL, в шаблон регулярного выражения с помощью MatchEvaluator
делегат.Он правильно обрабатывает блоки квадратных скобок и экранирует специальные символы Regex.
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);
});
Другие советы
В дополнение к решению @Nathan-Baulch вы можете использовать приведенный ниже код, чтобы также обработать случай, когда пользовательский escape-символ был определен с помощью LIKE '!%' ESCAPE '!'
синтаксис.
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);
}
В приведенном выше примере я бы атаковал это так (говорю в общих чертах, потому что не знаю C#):
Разбить его на части НРАВИТЬСЯ '...', поставь ... части в массив.Заменить неэкранированный % подписывает .*, подчеркивается ., и в этом случае [C-P]арсен переводится непосредственно в регулярное выражение.
Соедините части массива обратно вместе с помощью канала и заключите результат в круглые скобки и стандартные биты регулярного выражения.
Результатом будет:
/^(.*this.*|Sm.th|[C-P]arsen)$/
Самое важное здесь — с осторожностью относиться ко всем способам экранирования данных и к тому, какие подстановочные знаки преобразуются в какие регулярные выражения.
% becomes .*
_ becomes .
Я нашел модуль Perl под названием Regexp::Подстановочные знаки.Вы можете попробовать его портировать или попробовать Perl.NET.Мне кажется, ты тоже можешь что-нибудь написать.