30分钟正则:30分钟正则表达式指导



你是否曾经想过正则表达式是什么怎样能够快速得到对它个基本认识?我就是在30分钟内带你入门并且对正则表达式有个基本理解事实是正则表达式并没有它看起来那么复杂学习它最好办法就是开始写正则表达式并且不断实战在最初30分钟的后你就应该知道些基本结构并且有能力在你或者web页面中设计和使用正则表达式了对那些想要深入研究现在已经有很多非常好可用资源来让你更深入学习


到底什么是正则表达式?


我相信你对模式匹配“计算机通配符”应该比较熟悉了例如如果你想要在个Windows文件夹中找到所有Mircosoft Word文件你要搜索“*.doc”你知道星号会被解释为个通配符它匹配所有序列正则表达式就是这种功能个更加细节扩展

在写处理文本或者web页面时定位匹配复杂模式串是很常见正则表达式就是用来描述这类模式这样个正则表达式就是个模式缩减代码例如模式“\\w+”是表达“匹配任何包含字母数字非空串”精确思路方法.NET框架提供了个功能强大类库它使得在你应用中包含正则表达式更加容易使用这个库你可以轻易地搜索和替换文本解码复杂标题解析语言或者验证文本

学习正则表达式神秘语法个好办法是用例子作为开始学习对象然后实战创建自己正则表达式

让我们开始吧!


些简单例子


搜索Elvis

假设你要花费你所有空余时间来扫描文档来寻找Elvis仍然活着证据你可以使用下面正则表达式来搜索:

1. elvis -- Find elvis
这是搜索精确序列个完全合法正则表达式在.NET中你可以轻松设置选项来忽略各种情况所以这个表达式将会匹配“Elivs”“ELVIS”或者“eLvIs”不幸它也将匹配单词“pelvis”后 5个字母我们可以改进这个表达式如下:

2. \\belvis\\b -- Find elvis as a whole word
现在事情变得更加有趣了“\\b”是个特殊代码它表示“匹配任何单词开头或结尾位置”这个表达式将只匹配完整拼写为“elvis”单词无论是小写还是大写情况

假设你想要找到所有这样在其中单词“elvis”后面都跟着单词“alive”句点或者点“.”是个特殊代码匹配除了换行符的外任何星号“*”表示重复前面部分有必要次数以保证能够有个匹配这样“.*”表示“匹配除了换行符的外任意数目现在建立个表示“搜索在同行内后面跟着单词‘alive’单词‘elvis’”表达式就是件简单事了

3. \\belvis\\b.*\\balive\\b -- Find text with \"elvis\" followed by \"alive\"
仅仅使用几个特殊我们就开始创建功能强大正则表达式了而且它们已经开始变得难以被我们人类理解了

让我们看看另个例子

确定电话号码合法性

假设你web页面收集顾客7位电话号码而且你希望验证输入电话号码是正确格式“xxx-xxxx”这里每个“x”是个数字下面表达式将搜索整个文本寻找这样串:

4. \\b\\d\\d\\d-\\d\\d\\d\\d -- Find seven-digit phone number
每个“\\d”表示“匹配任何单个数字”“-”没有特殊意义并且按照字面解释匹配个连要避免繁琐重复我们可以使用个含有相同含义速记符:

5. \\b\\d{3}-\\d{4} -- Find seven-digit phone number a better way
“\\d”后面“{3}”表示“重复前面 3次”




.NET正则表达式基础


让我们探索下.NET中正则表达式基础

特殊

你应该知道几个有特殊意义你已经见过了“\\b”“.”“*”和“\\d”要匹配任何空白像空格制表符和换行符使用“\\s”相似地“\\w”匹配任何字母数字

让我们尝试更多例子:

6. \\ba\\w*\\b -- Find words that start with the letter a
这个搜索个单词开头(\\b)然后是个字母“a”接着是任意次数重复字母数字(\\w*)最后是个单词结尾(\\b)

7. \\d+ -- Find repeated s of digits
这里“+”和“*”是相似除了它需要至少次重复

8. \\b\\w{6}\\b -- Find six letter words
在Expresso中测试这几个表达式然后实战创建你自己表达式下面是个介绍说明有特殊含义表格:
. 匹配除换行符外任何
\\w 匹配任何字母数字
\\s 匹配任何空白
\\d 匹配任何数字
\\b 匹配个单词开始或结尾
^ 匹配开始
$ 匹配字结尾

表1 正则表达式常用特殊

开始阶段

特殊“^”和“$”被用来搜索那些必须以些文本开头和(或)以些文本结尾文本特别是在验证输入时特别有用在这些验证中输入整个文本必须要匹配个模式例如要验证个7位电话号码你可能要用:

9. ^\\d{3}-\\d{4}$ -- Validate a seven-digit phone number
这是和第5个例子但是强迫它符合整个文本匹配文本头尾的外没有其他通过在.NET中设置“Multiline”选项“^”和“$”改变他们意义为匹配行文本起点和结束而不是整个正文Expresso例子使用这个选项

换码

当你想要匹配这些特殊个时会产生像“^”或者“$”使用反斜线符号来去掉它们特殊意义这样“\\^”“\\.”和“\\\\”分别匹配文本“^”“.”和“\\”



重复

你已经见过了“{3}”和“*”可以指定个单独重复次数稍后你会看到相同语法怎样用来重复整个子表达式此外还有其他几种思路方法来指定个重复如下表所示:
* 重复任意次数
+ 重复次或多次
? 重复次或多次
{n} 重复n次
{n,m} 重复最少n次最多m次
{n,} 重复最少n次

表2 常用量词

让我们试试几个例子:

10. \\b\\w{5,6}\\b -- Find all five and six letter words

11. \\b\\d{3}\\s\\d{3}-\\d{4} -- Find ten digit phone numbers

12. \\d{3}-\\d{2}-\\d{4} -- Social security number

13. ^\\w* -- The first word in the line or in the text

在设置和不设置“Multiline”选项时试试最后个例子它改变了“^”含义

集合

搜索字母数字数字和空白是容易但如果你需要搜索集合中任意时如何办?这可以通过在方括号中列出想要来轻松解决这样“[aeiou]”就能匹配任意韵母而“[.?!]”就匹配句子末尾标点在这个例子中注意“.”和“?”在方括号中都失去了他们特殊意义而被解释为文本含义我们也可以指定个范围所以“[a-z0-9]”表示“匹配任何小写字母或者任何数字”

让我们试试个搜索电话号码更加复杂表达式:

14. \\(?\\d{3}[) ]\\s?\\d{3}[- ]\\d{4} A ten digit phone number

这个表达式将会搜索几种格式电话号码像“(800)325-3535”或者“650 555 1212”“\\(?”搜索0个或1个左圆括号“[)]”搜索个右圆括号或者个空格“\\s?”搜索0个或个空白不幸它也会找到像“650)555-1212”这样括号没有去掉情况在下面你会看到怎样用可选项解决这个问题

否定

有些时候我们需要搜索它不是个很容易定义集合成员下面表格介绍说明了这种怎样指定:

\\W 匹配任何非字母数字
\\S 匹配任何非空白
\\D 匹配任何非数字
\\B 匹配非单词开始或结束位置
[^x] 匹配任何非x
[^aeiou] 匹配任何不在aeiou中

表3 怎样指定你不想要东西

15. \\S+ -- All s that do not contain whitespace characters

后面我们会看到怎样使用“lookahead”和“lookbehind”来搜索缺少更加复杂模式情况

可选项

要从几个可选项中选择允许符合任何匹配使用竖杠“|”来分隔可选项例如邮政编码有两种个是5位个是9位个连我们可以使用下面表达式找到任何种:

16. \\b\\d{5}-\\d{4}\\b|\\b\\d{5}\\b -- Five and nine digit Zip Codes

当使用可选项时顺序是很重要匹配算法将试图先匹配最左面选择如果这个例子中顺序颠倒过来表达式将只能找到5位邮政编码而不会找到9位我们可以使用可选项来改进十位电话号码表达式允许包含区码无论是通过空白还是连划分:

17. (\\(\\d{3}\\)|\\d{3})\\s?\\d{3}[- ]\\d{4} -- Ten digit phone numbers, a better way




分组


圆括号可以用来划分个子表达式来允许重复或者其他特殊处理例如:

18. (\\d{1,3}\\.){3}\\d{1,3} -- A simple IP address finder

表达式部分搜索后面跟着个“\\.”位到 3位数字这被放在圆括号中并且通过使用修饰符“{3}”被重复 3次后面跟着和的前表达式而不带后缀部分

不幸这个例子允许IP地址中被分隔部分是任意两位或 3位数字尽管个合法IP地址不能有大于255数字要是能够算术比较个获取数字N使N<256就好了但是只用正则表达式是不能够办到个例子使用模式匹配测试了基于第位数字多种可选项来保证限制数字取值范围这表明个表达式会变得很笨重尽管搜索模式描述是简单

19. ((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?) -- IP finder

个“回引”用来搜索前面被个分组捕获已匹配文本再现例如“\\1”表示“匹配分组1中已捕获到文本”下面是个例子:

20. \\b(\\w+)\\b\\s*\\1\\b -- Find repeated words

运行过程是先捕获个分组1中“(\\w+)”表示至少包含个字母数字但仅当它是个单词开始或结束时才行然后它搜索任意数量空白“\\s*”后跟以被捕获文本“\\1”结尾单词

在上面例子中想要替换分组“(\\w+)”这种写法我们可以把它写成“(?<Word>\\w+)”来给这个分组命名为“Word”个对这个分组回引可以写成“\\k<Word>”试试下面例子:



21. \\b(?<Word>\\w+)\\b\\s*\\k<Word>\\b -- Capture repeated word in a named group

通过使用圆括号有很多可用特殊用途语法元素些最常用归纳如下面这张表格:

捕获
(exp) 匹配exp并且在个自动计数分组中捕获它
(?<name>exp) 匹配exp并且在个命名分组中捕获它

(?:exp) 匹配exp并且不捕获它
察看
(?=exp) 匹配任何后缀exp的前位置
(?<=exp) 匹配任何前缀exp的后位置
(?!exp) 匹配任何未找到后缀exp的后位置
(?<!exp) 匹配任何未找到前缀exp的前位置
评论
(?#comment) 评论



表4 常用分组结构

前两个我们已经说过了第 3个“(?:exp)”不会改变匹配行为它只是不像前两个那样捕获已命名或者计数分组

确定察看(Positive Lookaround)

下面 4个是所谓前向或后向断言它们从当前匹配向前或向后寻找需要东西而不在匹配中包含它们这些表达式匹配个类似于“^”或“\\b”位置而不匹配任何文本理解这个是很重要由于这个原因他们也被称为“零宽度断言”最好用例子来解释它们:

“(?=exp)”是“零宽度确定前向断言”它匹配个文本中在给定后缀的前位置但不在匹配中包含这个后缀:

22. \\b\\w+(?=ing\\b) -- The beginning of words ending with \"ing\"

“(?<=exp)”是“零宽度确定后向断言”它匹配在给定前缀后面位置但不在匹配中包含这个前缀:

23. (?<=\\bre)\\w+\\b -- The end of words starting with \"re\"

下面这个例子可以用来重复向 3位数为数字中插入逗号例子:

24. (?<=\\d)\\d{3}\\b -- Three digits at the end of a word, preceded by a digit

下面是个同时搜索前缀和后缀例子:

25. (?<=\\s)\\w+(?=\\s) -- Alphanumeric s bounded by whitespace

否定察看(Negative Lookaround)

的前我介绍说明了怎样搜索个不是特定集合成员那么如果我们想要简单验证没有出现但是不想匹配任何东西如何办?例如如果我们想要搜索其中“q”不是后跟着“u”单词如何办?我们可以尝试:

26. \\b\\w*q[^u]\\w*\\b -- Words with \"q\" followed by NOT \"u\"

运行例子你就会看到如果“q”是个单词最后个字母就不会匹配比如“Iraq”这是“[^q]”总是匹配如果“q”是单词最后它会匹配后面跟着空白所以这个例子中表达式结束时匹配两个完整单词否定察看可以解决这个问题它匹配个位置而不消耗任何文本和确定察看它也可以用来匹配个任意复杂子表达式位置而不仅仅是我们现在可以做得更好:

27. \\b\\w*q(?!u)\\w*\\b -- Search for words with \"q\" not followed by \"u\"

我们使用“零宽度否定前向断言”“(?!exp)”只有当后缀“exp”没有出现时它才成功下面是另个例子:

28. \\d{3}(?!\\d) -- Three digits not followed by another digit

相似地我们可以使用“(?<!exp)”“零宽度否定后向断言”来搜索文本中个位置这里前缀“exp”没有出现:

29. (?<![a-z ])\\w{7} -- Strings of 7 alphanumerics not preceded by a letter or space

这里是另个使用后向例子:

30. (?<=<(\\w+)>).*(?=<\\/\\1>) -- Text between HTML tags

这个使用后向搜索个HTML标记而使用前向搜索对应结束标记这样就能获得中间文本而不包括两个标记

评论

标点个使用方法是使用“(?#comment)”语法包含评论个更好办法是设置“Ignore Pattern Whitespace”选项它允许空白插入表达式然后当使用表达式时忽略它设置了这个选项的后任何文本每行末尾在数号“#”后面东西都被忽略例如我们可以格式化先前例子如下:

31. Text between HTML tags, with comments

(?<= # Search for a prefix, but exclude it
<(\\w+)> # Match a tag of alphanumerics within angle brackets
) # End the prefix

.* # Match any text

(?= # Search for a suffix, but exclude it


<\\/\\1> # Match the previously captured tag preceded by \"/\"
) # End the suffix






贪婪和懒惰


个正则表达式有个可以接受个重复次数范围量词(像“.*”)正常行为是匹配尽可能多考虑下面正则表达式:

32. a.*b -- The longest starting with a and ending with b

如果这被用来搜索串“aabab”它会匹配整个串“aabab”这被称为“贪婪”匹配有些时候我们更喜欢“懒惰”匹配其中个匹配使用发现最小数目重复表2中所有量词可以增加个问号“?”来转换到“懒惰”量词这样“*?”意思就是“匹配任何数目匹配但是使用达到个成功匹配最小数目重复”现在让我们试试懒惰版本例子(32):

33. a.*?b -- The est starting with a and ending with b

如果我们把这个应用到相同串“aabab”它会先匹配“aab”然后匹配“ab”


*? 重复任意次数但尽可能少
+? 匹配次或多次但尽可能少
?? 重复零次或多次但尽可能少
{n,m}? 重复最少n次但不多于m次但尽可能少
{n,}? 重复最少n次但尽可能少



表5 懒惰量词
我们遗漏了什么?

我已经描述了很多元素使用它们来开始创建正则表达式;但是我还遗漏了些东西它们在下面表中归纳出来这些中很多都在项目文件中使用额外例子介绍说明了例子编号在这个表左列中列出

\\a 报警
\\b 通常是单词边界但是在集合中它表示退格键
\\t 制表符
34 \\r 回车
\\v 垂直制表符
\\f 分页符
35 \\n 换行符
\\e ESC
36 \\nnn ASCII码 8进制数为nnn
37 \\xnn 十 6进制数为nn
38 \\unnnn Unicode码为nnnn
39 \\cN Control N例如回车(Ctrl-M)就是\\cM
40 \\A 开始(像^但是不依赖于多行选项)
41 \\Z 结尾或者\\n的前串结尾(忽略多行)
\\z 串结尾(忽略多行)
42 \\G 当前搜索开始阶段
43 \\p{name} 命名为nameUnicode类中任何例如\\p{IsGreek}
(?>exp) 贪婪子表达式也被称为非回溯子表达式它只匹配次然后就不再参和回溯
44 (?<x>-<y>exp)or (?-<y>exp) Balancing group. This is complicated but powerful. It allows named capture groups to be manipulated _disibledevent=>(?im-nsx:exp) 正则表达式选项为子表达式exp
46 (?im-nsx) Change the regular expression options for the rest of the enclosing group


(?(exp)yes|no) The subexpression exp is treated as a zero-width positive lookahead. If it matches at this po, the subexpression yes becomes the next match, otherwise no is used.
(?(exp)yes) Same as above but with an empty no expression
(?(name)yes|no) This is the same syntax as the preceding . If name is a valid group name, the yes expression is matched the named group had a successful match, otherwise the no expression is matched.
47 (?(name)yes) Same as above but with an empty no expression



表6我们遗漏东西左端列显示了项目文件中介绍说明这个结构例子序号

结论

我们已经给出了很多例子来介绍说明.NET正则表达式关键特性强调使用工具(如Expresso)来测试实战然后是用例子来学习如果你想要深入研究网上也有很多在线资源会帮助你更深入学习你可以从访问Ultrapico网站WebSite开始如果你想读本相关书籍我建议Jeffrey Friedl写最新版Mastering Regular Expressions

Code Project中还有很多不错文章其中包含下面教程:

·An Introduction to Regular Expressions by Uwe Keim
·Microsoft Visual C# .NET Developer\'s Cookbook: Chapter on Strings and Regular Expressions

Tags:  什么是正则表达式 js正则表达式 正则表达式 30分钟正则

延伸阅读

最新评论

发表评论