磨练构建正则表达式模式
引言UNIX? 管理员每天都需要构建和使用正则表达式 (regexp) 进行文本模式匹配。大多数语言都支持正则表达式的某种实现。有的应用程序(如 EMACS)具有正则表达式搜索功能,并且您可以通过各种命令行工具使用正则表达式。无论什么应用程序,构建正确的正则表达式的关键之处在于,识别仅满足需要匹配的数据的模式,以便在输入中排除其他不必要的内容。 出于这个目的,本文将逐步介绍几种正则表达式模式构建技巧,并介绍它们如何帮助您完成各种常规任务。 使用正则表达式 (regexp) 除非特别说明,否则本文中使用的示例都是扩展可移植操作系统接口(扩展 POSIX)的正则表达式。如果通过命令行(如使用 匹配整行内容 在用户字典文件 (/usr/dict/words) 中搜索是一个很好的基本模式。(有些版本的 UNIX 将用户字典放在 /usr/share/dict/words 中。) 例如,假设您忘记了如何拼写单词fuchsia。其中是否包含sh或cs呢?您所知道的只是,它以fu开头并以ia结尾。 尝试使用这个模式进行搜索: $ egrep -i '^fu.*ia$' /usr/dict/words
回页首 根据长度匹配行 使用大括号元字符 ( 表 1. 大括号元字符的含义
并不是所有扩展正则表达式的实现都支持大括号。此外,根据具体的实现,您可能需要先使用反斜杠对其进行转义。 您可以使用这个正则表达式得到字典中以单词长度为顺序的报告。所获得结果的数目取决于本地系统的字典文件中单词的数目,然而,它应该与清单 1所示类似。在这个示例中,最常见的单词长度是 9 个字母,该字典中有 32,380 个匹配单词。该字典中不包括 25 个字母或更长的单词,并且最长的单词并不是您认为的 21 个字母长的disestablishmentarian(有 81 个同样长度的单词,包括superincomprehensible和phoneticohieroglyphic),这个 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&;
这个示例中的正则表达式不会匹配单词ecosystem、systemic或system/70,也不会匹配模式 围绕字符与圆括号中的分组结合在一起,可以用来匹配部分单词。 要匹配包含以pre开头的单词的那些行,可以使用: &;(pre).*&;
前面的示例将匹配包含单词preface和preposterous的行,但不会匹配spread或Dupre。 回页首
匹配重复单词 这里介绍一种使用单词围绕字符匹配重复单词的快速方法,重复单词表示一个单词在空格之后再次出现。您还可以使用逆向引用,这是大多数流行的正则表达式实现中的一种递归特性,它可以匹配模式本身的某一部分。(将模式中需要引用的部分使用圆括号括起来,然后使用反斜杠加上需要进行引用的围绕字符编号来调用逆向引用: 要查找重复的单词,搜索在任意数目的空格之后再次出现该单词的情况,可以通过对第一个使用圆括号的部分进行逆向引用来实现: (&;.*&;)( )+1
这个示例匹配缩写形式和任何类型的单词,但是它不会匹配由标点符号分隔的重复单词,如It's been a long,long time。 要匹配所有的重复单词,包括由空格和任意标点符号分隔的重复单词,可以使用下面的表达式: (&;.*&;).?( )+1
如果需要对这些正则表达式使用 grep,则务必使用 回页首
匹配小时让我们再来看另外一类常见的问题:时间和日期。这里介绍了一些设计匹配正确模式的正则表达式所需要考虑的事项。 您无法搜索任何两位的数字来匹配分钟和秒,因为它们仅仅是从 0 到 59,要匹配它们,您需要使用方括号将表示十位和个位的范围括起来:
如果在上一个示例中没有开始的否定语句,它将匹配不带冒号的时间,这将取决于输入数据,可能会匹配中波广播电台(在美国称为调幅 AM 电台),如1450 AM。 回页首
匹配月份 匹配 12 个月中的任何月份需要一个使用
请注意,在上面的这两个示例中,May 是一个特殊的例外。在所有的月份中,它是唯一的完整拼写与三字母缩写相同的月份,所以成功的匹配必须包含这两种变形中的任何一种作为其缩写,因此像“Mayflower”这样的单词不会导致误报。 当匹配模式前面的字符不是空格或行首时,这些示例还是会失败(返回误报的结果)。这不太可能会出现在英语散文中,但是可能出现在程序源代码中,因为其中可能使用了像 要修复这些问题,可以执行下面的操作:
但是仍然存在潜在的问题,对于搜索整篇英文散文,这些示例并不可靠,因为它们可能返回错误的匹配结果,如“Janelle”或“Augury”这样的单词。要修复这个问题,您必须使用单词围绕字符将每个月份括起来。 本文开头提到,正确的正则表达式应该仅返回需要匹配的数据,以便在输入中排除其他不必要的内容。这种措词是经过仔细考虑的,因为对于构建正则表达式来说,这与上下文有关。对于有些情况,前面的示例非常适合,无需添加额外的单词围绕字符。在其他的情况下,可以对其进行相当程度的简化,例如,如果您正在搜索仅包含大写的日期数值数据的日志文件,那么只需要使用像 回页首
匹配日期您可以结合一些表 1所示的数量匹配来匹配日期。 要匹配“month,day,years”,可以使用下面的正则表达式(因为撇号字符是该正则表达式的一部分,所以必须使用双引号将它括起来,如下所示): "[A-Za-z]{3,10}.? [0-9]{1,2},([0-9]{4}|'?[0-9]{2})"
这个正则表达式匹配 9 种不同的日期格式:
这个正则表达式的误报包括“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,}
回页首
更多实际的匹配范围加上使用括号括起来的元字符,在查找符合任何特定格式的数值时非常有用。将前面介绍的一些技术结合起来,可以构建匹配各种数据的正则表达式:
回页首
结束语本文涉及到一些用于编写正则表达式的模式构建技术,以及如何使用它们来完成管理员时常碰到的特定类型数据匹配的工作。在此过程中,向您介绍了大量有价值的实际正则表达式,您可以将它们添加到自己的管理工具库中。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |