`
hgfghwq11
  • 浏览: 47473 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
社区版块
存档分类
最新评论

复杂的正则表达式应该如何构造

 
阅读更多

  昨天Snopo问我如何写一段正则表达式,来提取sql的条件语句。解答之余,想写一篇文章介绍一下经验。文题本来是《如何构造复杂的正则表达式》,但是觉得有些歧义,就感觉正则式本来很简单,我在教人如何将它小事化大一样。正好相反,我的本意是说,即使复杂的正则式也不怕,找出合适的方法,将其构造出来。 Snopo给出的文本是这样的:or and name='zhangsan' and id=001 or age>20 or area='%renmin%' and like,问,如何提取其中正确的SQL查询语句。 简要分析可知,中间部分是合乎要求的,只是两端的有若干个like, or, and。构造能够解析合乎SQL语法的查询语句的正则表达式,应该是比较复杂的。可是,对于具体的问题,也可以更简单。上述的不良构的SQL语句,应该是使用程序自动生成的,它的两端会有一些不符合题意的文本。只要将这些文本去除就可以了。 于是,我写出了正则表达式:s/^(?:(?:or|and|like)\s*)+|\s*(?:(?:or|and|like)\s*)+$//mi;,这样就把多行字串首尾的like, or, and以及可能的空白字符全部去掉了,剩下的内容即为所求。 答案发过去之后,Snopo显然不是很满意这种"偷懒"的办法。他继续问道,能否写出正则式,用来匹配合符SQL语法要求的条件查询语句?(只考虑where部分即可,不必写完整的select。)
  的确,从快速解决问题的角度来说,只要能够行之有效地解决,用什么办法都可以;不过从学习知识的角度来说,不避重就轻,而是刨根问底,才是正途。既如此,就看一下如何使用正则,将该SQL查询语句解决掉。 最简单的查询语句,应该是真假判断,即 where 1; where True; where false,等等。 这样的语句使用正则式,直接/(?:-?\d+|True|False)/i。 稍复杂些的单条语句,可以是左右比较,即 。将其简单化,结构就变为A OP B。其中A代表变量,OP代表比较操作符,B代表值。 A: 最简单的A,应该是\w+。考虑到实际情况,变量包含点号或脱字符,例如`table.salary`,可以记为/[\w.`]+/。这是比较笼统的细化。如果要求比较苛刻,还可以做到让脱字符同时在左右两边出现(条件判断)。
  OP: Where 常用的几种关系比较为:=, , >, =, 正则描述之,成为:/(?:[=]{1,2}|Between|Like|In)/i。
  B: B 的情况又可分为3种:变量,数字,字符串,列表。为简单起见,这里就不考虑算术表达式了。
  变量的话,直接延用A的定义即可。不赘述。
  数字:使用/\d+/来定义。不考虑小数和负数了。
  字符串:包括单引号字串和双引号字串。中间可以包括被转义的引号。我写了一个符合这一要求的引号字串正则表达式,形如:/(['"])(?:\\['"]|[^\\1])*?\1/。不过,由于它只是庞大机器的一个零件,这样写的风险是极其大的。首先,它使用了反向引用;其次,该反向引用使用了全局的反向引用编号。我写了自动生成全局编号的函数,来解决这一问题。不过,这里谈细节是不是太深入了。应该先谈框架,再说细节才对。不应该一入手就陷进细节的汪洋大海。
  列表:列表是形如(1, 3 , 4) 或 ("it","hr", "r&d")之类的东东,它由简单变量以逗号相连,两边加上括号组成。列表的单项以I表示,它代表 数字|字符串。此时,列表就变为:/\(I(?:,I)*?\)/。它表示,左括号,一个I,一系列由逗号、I组成的其它列表项(0个或多个),右括号。简单起见没有考虑空白字符。
  至此,可以总结出单条语句的正则框架:S =~ /A OP B/i。S在此代表单条语句。
  更为复杂的是多条语句,可以由单条语句组成,中间使用 and 或 or 连接。合理地构造单条语句,将其稳定地编制为多条语句,任务就完成了。 沿用上面的示例,以S代表单条语句,那么复合语句C就是 C =~S(?:(?:or|and) S)*?/。至此,一个初具规模的条件语句解析器就诞生了。下面以python为例,一步一步实现出来。 重申一句:虽然给出了实现,但是仍请注重思路,忽略代码。 1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  #!/usr/bin/python
  # -*- coding: utf-8 -*-
  #
  #author:         rex
  #blog:           http://iregex.org
  #filename        test.py
  #created:        2010-08-06 17:12
  #generage quoted string;
  #including ' and " string
  #allow \' and \" inside
  index=0
  def gen_quote_str(): 
  global index
  index+=1
  char=chr(96+index)
  return r"""(?P['"])(?:\\['"]|[^'"])*?(?P=quote_%s)"""% (char, char)
  #simple variable 
  def a():
  return r'[\w.`]+'
  #operators
  def op():
  return r'(?:[=]{1,2}|Between|Like|In)'
  #list item within (,)
  #eg: 'a', 23, a.b, "asdfasdf\"aasdf"
  def item():
  return r"(?:%s|%s)" % (a(), gen_quote_str())
  #a complite list, like
  #eg: (23, 24, 44), ("regex", "is", "good")
  def items():
  return r"""\( \s* 
  %s 
  (?:,\s* %s)* \s* 
  \)""" % (item(), item())
  #simple comparison
  #eg: a=15 , b>23
  def s():
  return r"""%s \s* %s \s* (?:\w+| %s | %s )""" % (a(), op(), gen_quote_str(), items())
  #complex comparison
  # name like 'zhang%' and age>23 and work in ("hr", "it", 'r&d')
  def c():
  return r"""
  (?ix) %s 
  (?:\s*
  (?:and|or)\s*
  %s  \s*
  )*
  """ % (s(), s())
  print "A:\t", a()
  print "OP:\t", op()
  print "ITEM:\t", item()
  print "ITEMS:\t", items()
  print "S:\t", s()
  print "C:\t", c()
  该代码在我的机器上(Ubuntu 10.04, Python 2.6.5)运行的结果是: 1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  A:  [\w.`]+
  OP: (?:[=]{1,2}|Between|Like|In)
  ITEM:   (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_a))
  ITEMS:  \( \s* 
  (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_b)) 
  (?:,\s* (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_c)))* \s* 
  \)
  S:  [\w.`]+ \s* (?:[=]{1,2}|Between|Like|In) \s* (?:\w+| (?P['"])(?:\\['"]|[^'"])*?(?P=quote_d) | \( \s* 
  (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_e)) 
  (?:,\s* (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_f)))* \s* 
  \) )
  C:  
  (?ix) [\w.`]+ \s* (?:[=]{1,2}|Between|Like|In) \s* (?:\w+| (?P['"])(?:\\['"]|[^'"])*?(?P=quote_g) | \( \s* 
  (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_h)) 
  (?:,\s* (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_i)))* \s* 
  \) ) 
  (?:\s*
  (?:and|or)\s*
  [\w.`]+ \s* (?:[=]{1,2}|Between|Like|In) \s* (?:\w+| (?P['"])(?:\\['"]|[^'"])*?(?P=quote_j) | \( \s* 
  (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_k)) 
  (?:,\s* (?:[\w.`]+|(?P['"])(?:\\['"]|[^'"])*?(?P=quote_l)))* \s* 
  \) )  \s*
  )*
  请看匹配效果图:
  
  我记得刚才好像提到"为简单起见,这里就不考虑算术表达式了"。不过,解析算术表达式是个非常有趣的话题,只要是算法书,都会提及(中缀表达式转前缀表达式,诸如此类)。当然它也可以使用正则表达式来描述。
  其主要思路是: 以及代码: 1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  #!/usr/bin/python
  # -*- coding: utf-8 -*-
  #
  #author:         rex
  #blog:           http://iregex.org
  #filename        math.py
  #created:        2010-08-07 00:44
  integer=r"\d+"
  factor=r"%s (?:\. %s)?" % (integer, integer)
  term= "%s(?: \s* [*/] \s* %s)* " % (factor, factor)
  expr= "(?x) %s(?: \s* [+-] \s* %s)* " % (term, term)
  print expr
  看一下它的输出和匹配效果图:
  
  如果不用复杂的正则式就能解决问题,一定不要用。
  如果必须写比较复杂的正则式,请参考以下原则。
  从大处着眼,先理解待解析的文本的整体结构是什么样子,划分为小部件;
  从细处着手,试图实现每一个小部件,力求每一部分都是完整、坚固的,且放在全局也不会冲突。
  合理组装这些部件。
  分而治之的好处:只有某个模块出错,其它部分没错时,可以迅速定位错误,消除BUG。
  谨慎使用捕获括号,除非你知道自己在做什么,知道它会有什么副作用,以及是否有可行的解决措施。对于短小的正则式来说,一两个多余的括号是无伤大雅的;但是对于复杂的正则式来说,一对多余的括号可能就是致命的错误。
  尽量使用free-space模式。此时你可以自由地添加注释和空白字符,以便提高正则表达式的可读性。
分享到:
评论

相关推荐

    正则表达式在语料库研究中的应用

    本文的研究目的在于厘清正则表达式的起源、概念和构造,并以任务驱动的方法,以几款常用的语料库工具为例,探讨正则表达式在文本清理、语料标注和检索等技术环节中的具体应用,从而进一步推动正则表达式在语料库研究中的...

    精通正则表达式~~~

    错综复杂的正则表达式... 43 暂停片刻... 49 使用正则表达式修改文本... 50 例子:公函生成程序... 50 举例:修整股票价格... 51 自动的编辑操作... 53 处理邮件的小工具... 53 用环视功能为数值添加逗号......

    应该如何构造复杂的正则表达式

    文题本来是《如何构造复杂的正则表达式》,但是觉得有些歧义,就感觉正则式本来很简单,我在教人如何将它...构造能够解析合乎SQL语法的查询语句的正则表达式,应该是比较复杂的。可是,对于具体的问题,也可以更简单

    正则表达式

    这些复杂的模式使用的正则表达式语法指定了该表达式中每个元素要重复出现的次数. 指定复制的字符总是出现在它们所作用的模式后面.由于某种复制类型相当常用.所以有一些特殊的字符专门用于表示它们.例如: +号匹配的...

    详解JavaScript正则表达式之RegExp对象

     RegExp对象表示正则表达式,RegExp是正则表达式的缩写,它是对字符串执行模式匹配的强大工具。RegExp对象用于规定在文本中检索的内容。当您检索某个文本时,可以使用一种模式来描述要检索的内容。RegExp就是这种...

    正则表达式30分钟入门教程

    如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子: 0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话...

    reb4s:Scala 的正则表达式生成器

    关于 reb4s reb4s的目的是围绕 JRE 的类提供的正则表达式语法提供一个纯 Scala 包装器。... 由于reb4s对开发人员隐藏了正则表达式语法,因此不需要记住(或反复查找)哪些符号代表哪些表达式构造,也

    理解javascript正则表达式

    可以是任何简单或复杂的正则表达式,可以包含字符类,限定符,分组,向前查找,反向引用。 关于正则表达式中各种特殊字符(如 \,^,$,\w,\b 等)的含义可以参考 MDN 正则表达式-特殊字符 的整理。这里我们简单介绍一下...

    正则表达式详述第一部

    正则表达式是regular expression,看来英文比中文要好理解... 除此之外,你用RegExp构造器建立的个别正则表达式对象的属性,就已经预先定义好了正则表达式对象的静态属性,你可以随时使用它们。 核心对象: 在Javascrip

    Java及python正则表达式详解

    正则表达式有元字符及不同组合来构成,通过巧妙的构造正则表达式可以匹配任意字符串,并完成复杂的字符串处理任务

    轻松学习C#的正则表达式

    正则表达式拥有一套自己的语法规则,常见语法包括字符匹配,重复匹配,字符定位,转义匹配和其他高级语法(字符分组,字符替换和字符决策),使用正则表达式时,首先构造正则表达式,这就用到了Regex类。其构造方式有...

    正则表达式regular expression详述(一)

    正则表达式是regular expression,看来英文比中文要好... 除此之外,你用RegExp构造器建立的个别正则表达式对象的属性,就已经预先定义好了正则表达式对象的静态属性,你可以随时使用它们。 核心对象: 在JavaScri

    反射,动态代理,正则,注解

    正则表达式:专门用于操作字符串的技术,并且可以简化代码,用于对字符串的复杂操作。 正则表达式弊端:代码可读性比较差。 反射要依赖于Class类。 由于Class表示类文件的字节码文件对象,类字节码文件就是在描述一...

    一种构造正则表达式更小ε-NFA的方法 (2013年)

    基于有限自动机的正则表达式匹配技术在网络信息领域得到了广泛应用,提出了一种构造... GREC方法在正则表达式层次结构复杂或包含有大量闭包运算的情况下,能够快速地构造出空间效率比传统的Thompson构造法高得多的NFA.

    Javascript中正则表达式的使用及基本语法

    通常的情况是,问题本身并不复杂,但没有正则表达式就成了大问题。javascript中的正则表达式作为相当重要的知识,本文将介绍正则表达式的基础语法 定义  正则表达式(Regular Expression)是一门简单语言的语法规范,...

    JavaScript学习小结(7)之JS RegExp

    在js中,正则表达式是由一个RegExp对象表示的,RegExp 是正则表达式的缩写。RegExp简单的模式可以是一个单独的字符。更复杂的模式包括了更多的字符,并可用于解析、格式检查、替换等等。可以使用一个RegExp()构造函数...

    01-爬虫的基本知识.pdf

    网络爬虫(又被称为网页蜘蛛,网络...首先,最通用的方法便是采用正则表达式提取,这是一个万能的方法,但是在构造正则表达式时比较复杂且容易出错。 另外,由于网页的结构有一定的规则,所以还有一些根据网页节点属性

    Java-PHP-C#

    构造检查email的正则表达式 好,让我们继续讨论怎么验证一个email地址. 在一个完整的email地址中有三个部分: POP3 用户名 (在 '@' 左边的一切), '@', 服务器名(就是剩下那部分). 用户名可以含有大小写字母阿拉伯...

Global site tag (gtag.js) - Google Analytics