ChinaUnix.net 首页 | 博客 | Linux | 论坛 | 人才 | 培训 | 知识库 | 资料 | 读书 | 手册 | 精华 | 下载 | 沙龙 | 搜索
Linux首页 | Linux论坛 | 论坛精华 | 开源新闻 | 技术文章 | 专题专栏 | 新手指南 | 迁移方案 | 产品方案 | 开源项目 | 开源图书 | 软件下载 | 人才招聘 | Linux博客
  搜索

  产品与方案
·中科红旗全面打造现代化邮政体系
·红旗助力“网上审批服务” 推动电子政务
·红旗正版化开创呼和浩特网吧建设新起点
·红旗Linux助信息产业部邮件服务器“快跑”
·中标普华Linux 为电子政务信息化保驾护航
·中标普华Linux助力基金产业
·中标普华Office率先支持UOF标准
·中标普华邮件系统助力西藏政府信息化建设
·红旗Linux助力国库集中支付系统改革
·红旗助中信卫星 掀起GIS通信应用风暴
·红旗软件助力烟草总局 全面建设“数字烟草”
·红旗助力“信访阳光工程”打造畅通信访渠道
·红帽联合FIS发布下一代实时核心银行平台
·红旗助力金盾 打造全无忧出入境信息系统
·红旗Linux全力打造中国邮政总局名址信息库
·爱尔兰证交所从Unix迁移到红帽企业Linux
·一流的意大利银行选择使用红帽企业Linux
·PLUS Finanzservice选择使用红帽企业Linux
·红帽助力TransACT Communications 公司
·法国零售业巨头Lapeyre采用Redhat Linux
·旅游预订网站选择使用红帽企业Linux
·马哈拉施特拉邦政府的红帽解决之道
·美国联邦政府案例
·红帽为慕尼黑展览会提供现代化集群系统
·Yuba郡用开源软件和红帽产品提高了效率
·红帽企业Linux助印度理工建立高性能计算中心
·采用红帽Linux 将系统维护时间缩短了65%
·从UNIX迁移到Linux使Peñoles公司获益非浅
·Hikal公司用红帽企业Linux开展任务关键的ERP项目
·KDE3.5.4新版本发布
·芝加哥商业交易所从Unix向Linux迁移
·南方基金管理有限公司成功案例 Red Hat Linux
·广东北电通讯设备有限公司成功案例
·挪威国家石油公司从UNIX迁移到红帽Linux,成本减半
·中央电视台CCTV动画部案例 Red Hat Linux

  图书

鸟哥的Linux私房菜基础学..


Linux程序设计.第3版


Linux设备驱动开发详解


  下载
·Endian Firewall
·linux kernel(Linux 内核)
·CentOS
·Fedora Core 6
·Scientific Linux
·Slackware 11.0
·Gentoo Linux
·ubuntu-6.10-i386服务器版本
·ubuntu-6.10-amd64服务器版
·ubuntu-6.10-i386桌面版
·ubuntu-6.10-amd64桌面版
·Engarde Linux
您的位置: Linux时代 > 技术文档 > 新手入门 >

磨练构建正则表达式模式的技能

日期:2006-08-22 作者:Michael Stutz 来自:IBM DW中国


通过本文的学习,您可以增加一些有用的设计实际正则表达式 (regexp) 的技能。构建正则表达式是任何管理员日常工作中的一部分。为了构造返回所需条件的成功正则表达式,需要学习以模式匹配的角度进行思考,而这种技能需要花大量的时间进行练习。

引言

UNIX® 管理员每天都需要构建和使用正则表达式 (regexp) 进行文本模式匹配。大多数语言都支持正则表达式的某种实现。有的应用程序(如 EMACS)具有正则表达式搜索功能,并且您可以通过各种命令行工具使用正则表达式。无论什么应用程序,构建正确的正则表达式的关键之处在于,识别仅满足需要匹配的数据的模式,以便在输入中排除其他不必要的内容。

出于这个目的,本文将逐步介绍几种正则表达式模式构建技巧,并介绍它们如何帮助您完成各种常规任务。

使用正则表达式 (regexp)

除非特别说明,否则本文中使用的示例都是扩展可移植操作系统接口(扩展 POSIX)的正则表达式。如果通过命令行(如使用 egrep 实用工具)使用它们,您应该根据需要引用各种正则表达式。请记住,不同的正则表达式实现之间存在一些区别,您可能不得不适应所使用的特定的工具、应用程序或语言中的具体实现。

匹配整行内容

^ 元字符匹配行首,而 $ 匹配行尾,如果将它们组合在一起(如 ^$),它们将匹配空行。(这个表达式的镜像,即 $^,是不可能匹配成功的,它将永远 都无法匹配到有效行。)这个基本的正则表达式是许多复杂正则表达式的基础,如果您还不习惯使用这个基本的正则表达式,那么您应该逐步养成使用它的习惯。使用它来构建匹配整行内容 的模式。

在用户字典文件 (/usr/dict/words) 中搜索是一个很好的基本模式。(有些版本的 UNIX 将用户字典放在 /usr/share/dict/words 中。)

例如,假设您忘记了如何拼写单词 fuchsia。其中是否包含 shcs 呢?您所知道的只是,它以 fu 开头并以 ia 结尾。

尝试使用这个模式进行搜索:

$ egrep -i '^fu.*ia$' /usr/dict/words

-i 标志表示在搜索过程中不区分大小写。在这个示例中,因为 fuchsia 拼写正确,所以在返回的单词中包括这个单词。

根据长度匹配行

使用大括号元字符 ({ }) 指定前面的正则表达式匹配多少次,如表 1 所示。当您将它们添加到刚才介绍的整行搜索中时,您可以指定行的长度。


表 1. 大括号元字符的含义
示例 描述
{X} 这个字符对前面的正则表达式匹配 X 次。
{X,} 这个字符对前面的正则表达式匹配 X 或更多 次。
{X,Y} 这个字符对前面的正则表达式匹配至少 X 而不超过 Y 次。

并不是所有扩展正则表达式的实现都支持大括号。此外,根据具体的实现,您可能需要先使用反斜杠对其进行转义。

您可以使用这个正则表达式得到字典中以单词长度为顺序的报告。所获得结果的数目取决于本地系统的字典文件中单词的数目,然而,它应该与清单 1 所示类似。在这个示例中,最常见的单词长度是 9 个字母,该字典中有 32,380 个匹配单词。该字典中不包括 25 个字母或更长的单词,并且最长的单词并不是您认为的 21 个字母长的 disestablishmentarian(有 81 个同样长度的单词,包括 superincomprehensiblephoneticohieroglyphic),这个 UNIX 字典中最长的单词有 5 个,包括 pathologicopsychological


清单 1. 计算字典中 X 个字母的单词的个数
$ for i in `seq 1 32`
>  {
>   echo "There are" `egrep '^.{'$i'}$' /usr/dict/words  \
   | wc -l` "$i-letter words in the dictionary."
>  }
There are 52 1-letter words in the dictionary.
There are 155 2-letter words in the dictionary.
There are 1351 3-letter words in the dictionary.
There are 5110 4-letter words in the dictionary.
There are 9987 5-letter words in the dictionary.
There are 17477 6-letter words in the dictionary.
There are 23734 7-letter words in the dictionary.
There are 29926 8-letter words in the dictionary.
There are 32380 9-letter words in the dictionary.
There are 30867 10-letter words in the dictionary.
There are 26011 11-letter words in the dictionary.
There are 20460 12-letter words in the dictionary.
There are 14938 13-letter words in the dictionary.
There are 9762 14-letter words in the dictionary.
There are 5924 15-letter words in the dictionary.
There are 3377 16-letter words in the dictionary.
There are 1813 17-letter words in the dictionary.
There are 842 18-letter words in the dictionary.
There are 428 19-letter words in the dictionary.
There are 198 20-letter words in the dictionary.
There are 82 21-letter words in the dictionary.
There are 41 22-letter words in the dictionary.
There are 17 23-letter words in the dictionary.
There are 5 24-letter words in the dictionary.
There are 0 25-letter words in the dictionary.
There are 0 26-letter words in the dictionary.
There are 0 27-letter words in the dictionary.
There are 0 28-letter words in the dictionary.
There are 0 29-letter words in the dictionary.
There are 0 30-letter words in the dictionary.
There are 0 31-letter words in the dictionary.
There are 0 32-letter words in the dictionary.
$ 


匹配单词

围绕字符 \<\> 是非常有用的模式构造器:它们将要匹配的整个单词 括起来,这表示,它们不会匹配带括号的模式,除非该模式本身就是一个单词。单词 定义为两侧由非单词字符描述的、任意数目组成单词的字符(数字、字母和下划线字符)。非单词字符包括下面的所有字符:

  • 行首
  • 空白字符
  • 标点符号
  • 行尾
  • 任何除字母、数字或下划线以外的字符

这些围绕字符可以节省大量的时间,但是它们常常没有被充分地利用,可能是因为并非所有的正则表达式实现都支持它们。如果您的正则表达式实现支持它们,那么您应该逐步养成使用它们的习惯。

将需要单独匹配的单词括起来,如下所示:

\<system\>

这个示例中的正则表达式不会匹配单词 ecosystemsystemicsystem/70,也不会匹配模式 system 出现在行中任意位置的那些行,它将仅仅 匹配 system 作为独立的单词出现的那些行。

围绕字符与圆括号中的分组结合在一起,可以用来匹配部分 单词。

要匹配包含以 pre 开头 的单词的那些行,可以使用:

\<\(pre\).*\>

前面的示例将匹配包含单词 prefacepreposterous 的行,但不会匹配 spreadDupre

匹配重复单词

这里介绍一种使用单词围绕字符匹配重复单词的快速方法,重复单词表示一个单词在空格之后再次出现。您还可以使用逆向引用,这是大多数流行的正则表达式实现中的一种递归特性,它可以匹配模式本身的某一部分。(将模式中需要引用的部分使用圆括号括起来,然后使用反斜杠加上需要进行引用的围绕字符编号来调用逆向引用:1 表示第一个圆括号分组,2 表示第二个圆括号分组,依此类推。)

要查找重复的单词,搜索在任意数目的空格之后再次出现该单词的情况,可以通过对第一个使用圆括号的部分进行逆向引用来实现:

(\<.*\>)( )+\1

这个示例匹配缩写形式和任何类型的单词,但是它不会匹配由标点符号分隔的重复单词,如 It's been a long, long time

要匹配所有的重复单词,包括由空格 任意标点符号分隔的重复单词,可以使用下面的表达式:

(\<.*\>).?( )+\1

如果需要对这些正则表达式使用 grep,则务必使用 -i 标志,以便在搜索中不区分大小写。

匹配小时

让我们再来看另外一类常见的问题:时间和日期。这里介绍了一些设计匹配正确模式的正则表达式所需要考虑的事项。

您无法搜索任何两位的数字来匹配分钟和秒,因为它们仅仅是从 0 到 59,要匹配它们,您需要使用方括号将表示十位和个位的范围括起来:

  • 要匹配标准的 12 或 24 小时格式的小时,可以使用下面的表达式:
    (([0-1]?[0-9])|([2][0-3])):([0-5][0-9])(:[0-5][0-9])?
    

  • 要匹配 12 小时 AM/PM 格式、带或不带秒数的时间,甚至匹配大写或小写、不带后缀 AM 或 PM 标识符的时间,可以使用下面的表达式:
    ([^0-9])([0-1]?[0-9]){1}(((:([0-5]){1}([0-9]){1}){1,2})|(( )?([AP]M)|([ap]m)))?
    

如果在上一个示例中没有开始的否定语句,它将匹配不带冒号的时间,这将取决于输入数据,可能会匹配中波广播电台(在美国称为调幅 AM 电台),如 1450 AM

匹配月份

匹配 12 个月中的任何月份需要一个使用 | 操作符进行分隔的列表,但有时会使用不同的方式对日期进行缩写:

  • 要查找完整拼写或三字母缩写的 12 个月份,可以使用下面的表达式(位于一行):
    Jan(uary)?|Feb(uary)?|Mar(ch)?|Apr(il)?|May|Jun(e)?|Jul(y)?|
    Aug(ust)?|Sep(tember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?
    

  • 您可以加以想象并搜索完整拼写或三字母缩写的变形,即仅当后面紧跟着一个空格或点号的情况,可以使用下面的表达式(位于一行):
    Jan(uary| |\.)|Feb(uary| |\.)|Mar(ch| |\.)|Apr(il| |\.)|May( |\.)|Jun(e| |\.)|Jul(y| |\
    .)|Aug(ust| |\.)|Sep(tember| |\.)|Oct(ober| |\.)|Nov(ember| |\.)|Dec(ember| |\.)
    

请注意,在上面的这两个示例中,May 是一个特殊的例外。在所有的月份中,它是唯一的完整拼写与三字母缩写相同的月份,所以成功的匹配必须包含这两种变形中的任何一种 作为其缩写,因此像“Mayflower”这样的单词不会导致误报。

当匹配模式前面的字符不是空格或行首时,这些示例还是会失败(返回误报的结果)。这不太可能会出现在英语散文中,但是可能出现在程序源代码中,因为其中可能使用了像 NumOct 这样的变量名。

要修复这些问题,可以执行下面的操作:

  • 使用圆括号将整个正则表达式括起来,并在它的前面加上另一个限定符,用于匹配行首或者空格字符,如下所示(位于一行):
    (^| )(Jan(uary| |\.)|Feb(uary| |\.)|Mar(ch| |\.)|Apr(il| |\.)|
    May( |\.)|Jun(e| |\.)|Jul(y| |\.)|Aug(ust| |\.)|Sep(tember| |
    \.)|Oct(ober| |\.)|Nov(ember| |\.)|Dec(ember| |\.))
    

  • 另一种完成这个任务的方法是,在该正则表达式的前面加上一个限定符,以匹配非文字数字的字符,如下所示(位于一行):
    ([^A-Za-z0-9])(Jan(uary| |\.)|Feb(uary| |\.)|Mar(ch| |\.)|
    Apr(il| |\.)|May( |\.)|Jun(e| |\.)|Jul(y| |\.)|Aug(ust| |\.)|
    Sep(tember| |\.)|Oct(ober| |\.)|Nov(ember| |\.)|Dec(ember| |\.))
    

但是仍然存在潜在的问题,对于搜索整篇英文散文,这些示例并不可靠,因为它们可能返回错误的匹配结果,如“Janelle”或“Augury”这样的单词。要修复这个问题,您必须使用单词围绕字符将每个月份括起来。

本文开头提到,正确的正则表达式应该仅返回需要匹配的数据,以便在输入中排除其他不必要的内容。这种措词是经过仔细考虑的,因为对于构建正则表达式来说,这与上下文有关。对于有些情况,前面的示例非常适合,无需添加额外的单词围绕字符。在其他的情况下,可以对其进行相当程度的简化,例如,如果您正在搜索仅包含大写的日期数值数据的日志文件,那么只需要使用像 [A-S] 这样的正则表达式来匹配包含月份名称的行。

匹配日期

您可以结合一些表 1 所示的数量匹配来匹配日期。

要匹配“month, day, years”,可以使用下面的正则表达式(因为撇号字符是该正则表达式的一部分,所以必须使用双引号将它括起来,如下所示):

"[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2})"

这个正则表达式匹配 9 种不同的日期格式:

  1. MONTH [D]D, YY
  2. MONTH [D]D, 'YY
  3. MONTH [D]D, YYYY
  4. MON. [D]D, YY
  5. MON. [D]D, 'YY
  6. MON. [D]D, YYYY
  7. MON [D]D, YY
  8. MON [D]D, 'YY
  9. MON [D]D, YYYY

这个正则表达式的误报包括“Order 99, 99”,要消除这些误报,可以将这个正则表达式与用于月份的正则表达式结合起来,如上所述,以便能够仅匹配实际的月份名称。另外,更改数值范围以避免错误的匹配,并且通过使逗号成为可选项,重复了 18 种可能的格式。

这将得到一个很长的正则表达式。尝试下面的表达式:

"([^A-Za-z0-9])(Jan(uary| |\.)|Feb(uary| |\.)|Mar(ch| |\.)|
Apr(il| |\.)|May( |\.)|Jun(e| |\.)|Jul(y| |\.)|Aug(ust| |\.)|
Sep(tember| |\.)|Oct(ober| |\.)|Nov(ember| |\.)|
Dec(ember| |\.)) [0-3]?[0-9]{1}(,)? ([0-9]{4}|'?[0-9]{2})"

同样,根据您的需要仔细设计正则表达式。匹配模式通常比较容易,这是因为它存在于特定输入的上下文中,而不是因为它可能独立于数据集而存在。后代人将会发现,前面那个很长的正则表达式中仍然存在 Y10K 错误,因为它能匹配的最大可能的年份为 9999。

匹配整数

正如您在前几个示例中看到的,使用方括号中的范围可以很好地匹配数值。

要匹配任意长度的整数,可以在数值范围后面加上 +;要包括负值,可以在它的前面加上可选的负号(连字号)匹配:

-?[0-9]+

前面的例子可以匹配 0,因为 0 是指定范围中可选的字符。

对于数值匹配,使用圆括号将某些部分括起来也非常有效。要匹配任意的十进制数值,可以使用包含小数点加上一个或多个数值的可选围绕字符,以此对前面的正则表达式进行扩展:

-?[0-9]+(\.[0-9]+)?

可以使用方括号指定十进制数值的小数位数。例如,要匹配小数位数为 5 或更多小数位数的正数值,可以使用下面的表达式:

[^-][0-9]+\.([0-9]){5,}


更多实际的匹配

范围加上使用括号括起来的元字符,在查找符合任何特定格式的数值时非常有用。将前面介绍的一些技术结合起来,可以构建匹配各种数据的正则表达式:

  • 要匹配美国的电话号码,可以使用:
    ((\([2-9][0-9]{2}\))?\ ?|[2-9][0-9]{2}(?:\-?|\ ?))[2-9][0-9]{2}[- ]?[0-9]{4}
    

    这个正则表达式可以匹配美国 15 种格式的电话号码:

    1. (NPA) PRE-SUFF
    2. (NPA) PRE SUFF
    3. (NPA) PRESUFF
    4. (NPA)PRE-SUFF
    5. (NPA)PRE SUFF
    6. (NPA)PRESUFF
    7. NPA PRE-SUFF
    8. NPA PRE SUFF
    9. NPA PRESUFF
    10. NPAPRE-SUFF
    11. NPAPRE SUFF
    12. NPAPRESUFF
    13. PRE-SUFF
    14. PRE SUFF
    15. PRESUFF

    它还可以匹配美国免费 WATS 号码,尽管 1-800 的“1-”前缀或其他的免费号码不是匹配的一部分,但它本身可以匹配 10 位的数值。对于以 1 或 1+ 开头的美国号码和任意数目的空格,也完全一样,长途电话拨号前缀本身无法匹配,但是只要它后面跟着实际的号码,这个正则表达式就能够将其找出来。

  • 要匹配两或三位域的电子邮件地址,可以尝试下面的表达式:
    \<[^@]+\>@[a-zA-Z_\.]+?\.[a-zA-Z]{2,3}
    

  • 要匹配如今所有流行的 URL,可以使用下面的正则表达式:
    (((http(s)?|ftp|telnet|news)://|mailto:)[^\(\)[:space:]]+)
    

    这个表达式可以正常运行,但是匹配 URL 并不像您想象的那么简单。匹配任何可能的 URL 的正则表达式,如 RFC 1738 中的定义,发表在“Regexp for URLs”(请参见参考资料部分)一文中,它非常巨大并且看起来令人生畏。现在应该将它合并为一个 [:url:] 类(如果有用于处理类似数据种类的各种新的类,如 [:email:],那就好了)。

结束语

本文涉及到一些用于编写正则表达式的模式构建技术,以及如何使用它们来完成管理员时常碰到的特定类型数据匹配的工作。在此过程中,向您介绍了大量有价值的实际正则表达式,您可以将它们添加到自己的管理工具库中。


 

原文链接:http://www-128.ibm.com/developerworks/cn/aix/library/au-expressions.html

本文被浏览



 相关新闻

YAPC::NA 北美 perl 大会实录(二)2005-06-29 16:06:36
YAPC::NA 北美 perl 大会实录2005-06-29 08:58:38
Perlchina上海聚会-热烈招募中... ...2005-05-24 18:37:13
Pugs让Perl6快速起跑2005-03-06 22:48:35
用PERL实现一个简单的NIDS2005-01-30 13:35:52
Perl 操作符和优先级2005-01-28 11:13:31
一个测试网站速度的perl程序.2005-01-28 11:09:19
perl常用函数(适合初学者)!2005-01-17 14:15:37


 相关评论
关于我们 | 联系方式 | 广告合作 | 诚聘英才 | 网站地图 | 免费注册

Copyright © 2001-2006 ChinaUnix.net All Rights Reserved

感谢所有关心和支持过ChinaUnix的朋友们

京ICP证041476号