Какой код я бы использовал для преобразования выражения, похожего на SQL, в регулярное выражение на лету? - PullRequest
7 голосов
/ 06 сентября 2008

Я хочу преобразовать SQL-оператор на лету в эквивалентное регулярное выражение, т.е.

LIKE '%this%'
LIKE 'Sm_th'
LIKE '[C-P]arsen'

Какой лучший подход к этому?

P.S. Я ищу это на .Net Framework (C #).

Ответы [ 4 ]

8 голосов
/ 01 ноября 2012

Следующий Regex преобразует шаблон, подобный SQL, в шаблон Regex с помощью делегата 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);
    });
3 голосов
/ 24 апреля 2015

В дополнение к решению @ 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);
    }
2 голосов
/ 06 сентября 2008

Из приведенного выше примера я бы атаковал его так (я говорю в общих чертах, потому что я не знаю C #):

Разбейте его на LIKE '...' , поместите кусочки ... в массив. Замените неэкранированные % знаки на . *, подчеркивая на., И в этом случае [C-P] arsen переводится непосредственно в регулярное выражение.

Соедините части массива обратно вместе с конвейером и оберните результат в круглые скобки и стандартные биты регулярных выражений.

Результат будет:

/^(.*this.*|Sm.th|[C-P]arsen)$/

Самое важное здесь - это остерегаться всех способов, которыми вы можете экранировать данные, и какие символы подстановки переводят в какие регулярные выражения.

% becomes .*
_ becomes .
0 голосов
/ 06 сентября 2008

Я нашел модуль Perl Regexp :: Wildcards . Вы можете попробовать его портировать или попробовать Perl.NET. У меня такое чувство, что ты тоже можешь что-нибудь написать.

...