Search This Blog

2013-05-01

说正则表达式

目录  [+]

1 简介

全称:Regular Expression、正则表达式,简称:regexp、regex、正则。
定义:A pattern that selects specific strings from a set of character strings.
正则可用于验证字符串的格式,查找、替换复杂字符串。操作文本的工作尽量交给正则,可以提高效率。
正则就是一段文本,执行这段文本的是正则引擎。
在一些地方,正则被当成编程语言,比如,http://www.dmoz.org/Computers/Programming/Languages/Regular_Expressions/、《Unix编程艺术》。
使用方式:编程、文本编辑器、Unix/Linux/OSX命令、等。
在Windows上,下载一个UnxUtils即可方便地使用Unix命令,把相应的bin目录放在Path环境变量的最前面,因为Windows有一些同名的命令。安装Cygwin要麻烦一些,如果只是用一些常用的Unix命令,不推荐使用。

2 正则流派

很早以前就有Unix命令支持正则,这些命令支持的正则并不完全相同,正则有了变体,这些正则后来被规范化为POSIX正则。
Perl语言支持正则,并且增加了很多新特性。到目前为止,Perl正则是最强大的,一部分其他实现也向Perl正则靠拢。
详细对比可以参考Regular Expression Flavor Comparison - Author: Jan Goyvaerts - www.regular-expressions.info

3 POSIX正则

很简单,不重复了,看参考文章即可。
[:alnum:]之类的方括号表达式不需要记,都有等价写法,大部分都很好写。只有少部分写起来麻烦一些,比如[:punct:]代表标点符号字符,这个也不常用。

3.1 教程列表

)中文的

)英文的

4 Perl正则

POSIX正则相对简单,有些情况无法处理。Perl正则有一些额外的特性,有时候需要用,有必要学一下。
Perl正则的特性不需要特别去记,大概了解一下特性分类,用到的时候可以稍微看看,自己多写正则解决问题,自然就会了。

4.1 教程列表

)英文的
链接1: Perl Regular Expression Syntax - www.boost.org
链接2: Perl Regular Expression - perldoc.perl.org

)中文的
这两篇文章我以前都看过,不推荐看文章当中复杂的例子,不求甚解即可,自己写正则解决问题,效果会好得多。

4.2 独特特性举例

)环视断言、条件表达式
这两个特性具有明显的编程风格,有些特殊情况就不用写代码来解决了。
Lookahead , Lookbehind , Conditional Expressions 在链接1; Look-Around Assertions , Conditional expression 在链接2。
Lookahead : (?=pattern), (?!pattern)
Lookbehind : (?<=pattern), (?<!pattern)

)转换字符的大小写
来自 链接2 Escape sequences
\l lowercase next char (think vi)
\u uppercase next char (think vi)
\L lowercase till \E (think vi)
\U uppercase till \E (think vi)
\E end either case modification or quoted section, think vi

5 高级语言对正则的支持

大部分高级语言以标准API的形式提供正则支持,需要参考相关文档,这是最重要的。

6 库/文本编辑器对正则的支持

6.1 POSIX正则支持

大部分都支持。

6.2 支持Perl正则的库

比如Boost.Regex、PCRE、JRegex。

6.3 支持Perl正则的文本编辑器

不是所有的都支持Perl正则,比如Eclipse就不支持(可能有这样的插件)。下面只列举我用过的。

)跨平台
Geany
    --来自Re: What can I use as a Notepad++ alternative in Linux (Ubuntu)? - Author: Jarek - stackoverflow.com的推荐
Bluefish

)Linux平台
KWrite
Kate

)Windows平台
Notepad++

7 一些应用例子

随时都可以用于实际工作,仅举几例。
下面的正则都是写这篇文章的时候写的,有一些跟工作当中使用的没什么区别,有些更完善。有些例子没有给出正则。
我是Java程序员,所以很多例子都跟Java有关,其他语言也可能有类似概念,应该能通用。

7.1 检查代码

)检查C代码if条件里有没有0赋值
在文本编辑器中,递归查找指定文件夹中符合条件的文件。
搜索\s*if\s*\(\s*[A-Za-z_][A-Za-z0-9_]*\s*=\s*0\s*\)
代码检查工具应该有这个功能了,这里只是个例子。

7.2 清理代码

)偶尔会写点DummyXyzImpl或者XyzWrapper之类的Java class,把Eclipse自动生成的TODO以及空行一次性清除。

7.3 生成代码

)有很多种状态,每一种状态有相应的处理类,可以根据这些状态文本生成类的骨架,拷贝过去格式化一下代码就行了。
  1. 把所有状态拷贝到文本编辑器,一行一个,使用下面的正则
  2. 搜索^(.+)$
  3. 替换为private static class \1Handler extends AbstractHandler \{\npublic void handle\(\1Param p\)\{\n//TODO \1 handler\n\}\n\}

也可能是用switch case处理,生成这个也很方便。

7.4 重构代码

在文本编辑器中,递归查找并替换指定文件夹中符合条件的文件。

)logging系统升级,从不使用logging系统到使用某种logging系统,或者从一种logging系统迁移到另一种。

)给LOGGER.debug方法添加if(LOGGER.isDebugEnabled())检查:
  1. 用之前先格式化代码。"LOGGER"可以自己换,level可以自己换,比如trace,info之类。完成之后刷新项目,再格式化一下代码。
  2. 搜索(?:if \(LOGGER\.isDebugEnabled\(\)\)\s*)*(LOGGER\.debug\()
  3. 替换为if \(LOGGER\.isDebugEnabled\(\)\) \1

)我写了两个Java bean,有很多字段,这两个类只在package之内使用,我自作聪明地没有使用get/set方法,而是直接引用字段。其中有一个字段有特殊格式,我着急写代码,在某种情况下生成的格式错了,这是个严重问题,我准备在设置的时候验证格式并做断言,放在set方法比较合适,悲剧的是没有set方法。为了避免不一致的代码,所有的字段都要改掉。找到正则如何转换字母大小写,写了两个正则,把bean.fieldX跟bean.fieldX=valueX替换为bean.getFieldX()跟bean.setFieldX(valueX)。

7.5 把普通文本转化为SQL语句

如果是数据复杂的CSV文件,那最好找专门的软件来处理,因为有时候有些字符是转义过的,正则不好处理,万一没处理到位,填充到SQL里的数据是错误的。

7.6 提取文本

7.6.1 从Android国际化资源文件strings.xml提取文本

strings.xml的格式在String Resources - developer.android.com

)从strings.xml提取数据,生成类似CSV的格式,可以方便地拷贝到Excel,用于翻译。
  1. 使用之前,先格式化strings.xml,拷贝<string name="string_name">text_string</string>之类的内容到文本编辑器,使用下面的正则(text_string不允许包含\t)
  2. 搜索^[^<>\r\n]*<string\s+name\s*=\s*\"([^"]+)\"\s*>([^<\t]+)</string>[^<>\r\n]*$
  3. 替换为\1\t\2

)翻译好之后,从Excel拷贝数据到文本编辑器,数据格式是string_name\ttext_string(这是从Excel拷贝出来的默认格式),使用正则生成strings.xml能用的格式
  1. 搜索^([^\t\r\n]+)\t([^\t\r\n]+)$
  2. 替换为<string name="\1">\2</string>

7.6.2 grep

用grep从文件中提取文本,重定向输出到一个文件供下一步处理。grep还是比较简单常用的,awk,sed之类的我还不会。

一点文档:
grep, egrep, fgrep - print lines matching a pattern
By default, grep prints the matching lines.
-x, --line-regexp. Select only those matches that exactly match the whole line.
--引用自UNIX man pages : egrep () - Author: Panagiotis Christias - unixhelp.ed.ac.uk
-x选项最好不要使用,写起来麻烦,如果不能匹配整行,那什么都不会输出。

例子:
)在Squid日志查询某段时间访问index.jspa的请求
egrep 1242713.+index.jspa.+ access.log-20090520 > 1242713xxx_index.jspa_squid.log

8 一些校验例子

最好自己懂正则,这样对正确性更有保障。如果用别人写的,那也得看看格式定义,然后验证一下是否正确。有些地方提供的正则过于简陋,直接用的话是不够好的。

8.1 email正则

关于email用户名的大小写,有些email服务器区分,有些不区分。在验证或发送邮件的时候都需要区分email的大小写,以免发生安全问题。
能满足大部分email格式的正则表达式:
^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$
参考文章:

8.2 http url正则

IPv6已经越来越实用,一些大型网站已经支持。相应的http url正则也该更新了。
参考文章:

9 正则辅助生成工具

很早以前,我按照RFC文档写过一个匹配http-url的正则,写得很长,之后发现一点问题而去修改,看着很晕。
现在想起来,这种复杂的正则应该借助正则辅助生成工具来生成,而不是直接写。在这个工具里面,只需要描述各个组成部分以及每个部分允许的字符,就像文档里的描述形式。简单看了下,暂时没发现这样的工具,有空写写。

10 更多参考资料


=文章版本=

201303
20130425 修改标题格式;增加一些支持Perl正则的文本编辑器;增删一部分内容;
20130430 增加一个重构代码的例子
20130501 增加一些文章的作者信息
20130506 改为html格式
20130510 改为html格式

No comments:

Post a Comment