正则表达式解析有限SQL的子句

hen*_*000 2 c# regex sqlite

如何使用正则表达式解析有限的SQL where子句?

where子句的格式受到限制.它不包含子查询.它仅限于"AND","OR"和"()".

因此,如果给出where子句,我想从中提取部分.

where子句的3个例子:

  1. ProjectNumber =?
  2. ProjectNumber =?AND severity = 5
  3. (ProjectNumber =?AND severity = 5)OR DueDate <(DATETIME('NOW'))

例如1,我想要"ProjectNumber =?"

例如2,我想要"ProjectNumber =?","severity = 5"

例如3,我想要"ProjectNumber =?","severity = 5","DueDate <(DATETIME('NOW'))"

"?" 表示该值已参数化.

我理解正则表达式不足以解析完整的SQL where子句.

我发现这个"(?<= ^ |\A |(AND | OR))(?:[^'] |'(?:[^'] |'{2})+')*?(?= (AND | OR)| $ |\Z)"但它不适用于3.

编程语言是C#,数据库是SQLite.

ps我是Regex的基本经验.

谢谢.

ps下面是我目前的C#代码:

string query = @"(ProjectNumber=? AND severity=5) OR DueDate < (DATETIME('NOW'))";
string pattern = @"(?<=^|\A|(AND|OR))(?:[^']|'(?:[^']|'{2})+')*?(?=(AND|OR)|$|\Z)";
MatchCollection matches = Regex.Matches(query, pattern);
foreach (Match match in matches) 
    Console.WriteLine(match.ToString());
    // currently Console.WriteLine() gives the following:
    // (ProjectNumber=?
    // severity=5)
    //  DueDate < (DATETIME('NOW'))
Run Code Online (Sandbox Code Playgroud)

小智 7

通过对SQL WHERE谓词施加的限制,可以创建一个正则表达式,在表示类似的结构时提取比较表达式(DATETIME('NOW')).

我将展示和解释的正则表达式要求WHERE谓词在语法上是正确的.如果WHERE谓词有语法错误,则正则表达式可能不匹配或产生垃圾结果.

充满光彩的正则表达式(增加了空格以增强可读性!):

\w[\w\d]* \s*[<>=]{1,2}\s* ( \?|\w[\w\d]*|(\w[\w\d]*)*((?<PR>\()|(?<-PR>\))|[^()])+ )
Run Code Online (Sandbox Code Playgroud)

虽然它肯定不是一个很长的正则表达式,但它仍然很难阅读和理解.因此,让我们解构这个正则表达式并解释它的几个部分.为此,我们将首先看看我们实际想要从WHERE谓词中提取的内容.

我们想要从WHERE谓词中提取的每个表达式都遵循相同的基本模式:

SomeIdentifierWithoutParantheses =|<=|>=|<> SomeOtherThingWithOrWithoutParentheses
Run Code Online (Sandbox Code Playgroud)

这个(a)的高级描述模式足以理解正则表达式需要匹配什么来从WHERE谓词中提取所需的部分.

\w[\w\d]*我们的正则表达式的第一部分与SomeIdentifierWithoutParantheses匹配.这可以是以字母数字字符开头的任何标识符,后跟其他字母数字字符和/或数字.这种标识符的示例是ProjectNumberMy1Ident23.

在正则表达式的第二部分,\s*[<>=]{1,2}\s*相匹配,所述比较操作数=,<=,>=<>,包括任何的空格前后的比较操作后以下.(好吧,它也会像废话一样匹配=<,但是 - 在开头说 - 我们假设语法正确的SQL.)

正则表达式的第三部分与比较运算符后面的第二个操作数匹配,这看起来有点笨拙:( \?|\w[\w\d]*|(\w[\w\d]*)*((?<PR>\()|(?<-PR>\))|[^()])+ ).让我们进一步解构正则表达式的这一部分.您可能已经注意到,整个事情是三个备选选项的交替,将在下面解释.

\?显然匹配一个问号(如"ProjectNumber =?").\w[\w\d]*匹配标识符的方式与第一个操作数匹配的方式相同.

(\w[\w\d]*)*((?<PR>\()|(?<-PR>\))|[^()])+包含.NET中RegEx引擎的特性:平衡组.使用这样的构建体具有平衡组允许正则表达式匹配,其包含括号的(嵌套的)基团的操作数(- )DATETIME("NOW")(DATETIME("NOW")) .

对于这里的StackOverflow另一个问题,马丁布特内尔已经提供了有关平衡组很好的解释(这个问题的题目是" 什么是正则表达式平衡组? "),我想在这里谁不知道均衡组对他的回答(点击这里导航到马丁的答案).可以在CodeProject上找到另一个很好的解释.

您会注意到没有任何东西可以处理布尔运算符,例如ANDOR.这不是必需的,因为这些布尔运算符不是正则表达式匹配的模式的一部分.(还记得上面关于模式的崇高描述吗?)


如何在C#中使用这样的正则表达式从WHERE谓词中提取所需的部分?

首先,请注意我将继续在正则表达式中使用空格以提高可读性.这需要使用RegexOptions.IgnorePatternWhitespace或" (?x) "内联选项进行RegEx初始化.在下面的代码中,我使用前者.

要从WHERE谓词中提取所有部分,将使用RegEx.Matches方法,该方法返回Match对象的集合.每个Match对象代表一个提取的部分.

Regex re = new Regex(
    @"\w[\w\d]* \s*[<>=]{1,2}\s* ( \?|\w[\w\d]*|(\w[\w\d]*)*((?<PR>\()|(?<-PR>\))|[^()])+ )",
    RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled
);


string wherePredicate =
    "(ProjectNumber=? AND severity=5) OR DueDate < (DATETIME('NOW'))";
    // or use any other WHERE predicate string here...

MatchCollection mc = re.Matches(wherePredicate);

if (mc.Count == 0)
    Console.WriteLine("No matches found.");
else
    foreach (Match m in mc)
        Console.WriteLine("\"{0}\"", m.Value);    
Run Code Online (Sandbox Code Playgroud)


您可以在Regex Storm .NET Regex测试人员的帮助下在线试验正则表达式和不同的输入字符串.