|
楼主 |
发表于 2011-8-30 19:55
|
显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件 ★ 免费下载 ★ ★ 使用帮助★
本帖最后由 godhawk 于 2011-8-30 19:48 编辑
10、正则表达式实战训练1
光说不练也不行,在讲理论的时候得配合实战训练,效果才会更好。下面给一个例子,用来学习基本的正则编程。
题目:要求取得字符串中的所有数字。
题目本身比较简单,我们可以采用常规的做法,从字符串的第一个字符开始,循环逐个判断每一个字符是数字还是非数字......
但常规做法显得比较麻烦,我们来看如何使用正则表达式来解决这个问题。
思路有二:
1. 采用查找的方式,匹配到数字就取出来,最后连成一个结果。 所用的正则表达式为 \d+,可以匹配一个或多个连续的数字。
2. 采用替换的方式,匹配到非数字就把它替换成空字符串。所用的正则表达式为 [^\d]+,可以匹配一个或多个连续的非数字。
究竟那种方式更好,大家自己判断一下就可以了,比较容易的。
程序代码及实际效果见附件
“正则表达式(regular expression)就是用一个“字符串”来描述一个特征,然后去验证另一个“字符串”是否符合这个特征,即一段字符串的模式。比如,表达式“ab+” 描述的特征是“一个 'a' 和 任意个 'b' ”,那么 'ab', 'abb', 'abbbbbbbbbb' 都符合这个特征。”
------------
看了这段后,我决定要学一学正则表达式。
谢谢领我这个笨学徒入门。
11、问号的使用
在正则表达式中,问号使它前面的符号变成可选项。如:<<colou?r>>可以匹配"colour"和"color"。
使用圆括号使一组字符成为可选项,问号在圆括号后。如:<<Nov(ember)?>>会匹配"Nov"和"November"。
在一个正则表达式中,可以使用多个问号,使多个符号成为可选项。如:<<Feb(ruary)? 23(rd)?>>匹配"February 23rd","February 23","Feb 23rd"和"Feb 23"。
在这里,要介绍正则表达式中的一个非常重要的概念------贪婪性。我们这里介绍的第一个贪婪的元字符就是问号。问号让正则引擎有两种选择:尽力去匹配问号前的部分;不匹配问号钱的部分。而正则引擎总是试图去匹配问号前的部分,只有当这样的尝试失败后,才会忽略掉问号前的部分,所以说问号是一个贪婪的元字符。
若用正则式<<Feb 23(rd)?>>去匹配字符串"Today is Feb 23rd, 2003",得到的结果总是"Feb 23rd"而不是"Feb 23",就是因为问号的贪婪性。
12、重复量词
使用*和+进行重复
11、中已经介绍过一种重复操作符或者说量词:问号?。它告诉正则引擎匹配前导字符0次或者1次,效果就是使它成为可选的。
+告诉引擎匹配前导字符1次或多次;*告诉引擎匹配前导字符0次或多次。
正则式 <[A-Za-z][A-Za-z0-9]*> 匹配没有属性的HTML标签,,“<”以及“>”是文字符号。第一个字符集[A-Za-z]匹配一个字母,第二个字符集[A-Za-z0-9]匹配一个字母或数字,星号重复第二个字符集一次或多次。因为我们使用星号,第二个字符集有0个匹配也是正确的,所以这个正则式可以匹配一个标签"<B>"。匹配字符串"<HTML>"时,第一个字符集匹配"H",星号将会导致第二个字符集重复匹配三次,匹配到"TML"。
有限重复
许多现在的正则引擎实现,允许定义对一个字符重复多少次。语法是:{min,max}。min和max都是非负整数。如果逗号有而max被忽略了,则max没有限制。如果逗号和max都被忽略了,则重复min次。
因此{0,}和*一样,{1,}和+ 的作用一样。
你可以用\b[1-9][0-9]{3}\b匹配1000~9999之间的数字(“\b”表示单词边界)。\b[1-9][0-9]{2,4}\b匹配一个在100~99999之间的数字。
注意贪婪性
假设用一个正则式去匹配一个HTML标签。知道输入将会是一个有效的HTML文件,因此正则式不需要排除那些无效的标签。所以如果是两个尖括号之间的内容,就应该是一个HTML标签。
很多正则式的新手首先会想到用正则式 <.+> ,他们会很惊讶的发现,对于测试字符串,“This is a <EM>first</EM> test”,你可能期望会返回<EM>,然后继续进行匹配的时候,返回</EM>。但事实不会。正则式将会匹配“<EM>first</EM>”。显然这不是我们想要的结果。
原因在于“+”是贪婪的。也就是说,“+”会导致正则引擎试图尽可能地重复前导字符。只有当这种重复会引起整个正则表达式匹配失败的情况下,引擎会进行回溯。也就是说,它会放弃最后一次的“重复”,然后处理正则表达式余下的部分。
和“+”类似,“?”“*”的重复也是贪婪的。
13、正则表达式实战训练2
练习重复量词的使用
要求把一个由多个名字组成的字符串,按组成名字逆序排列。
程序代码及运行效果见附件
正则表达式中有类似"或"运算的符号:"|",可是没有类似"与"运算的符号,这是为什么呢?
另外:所有不是汉字的字符正则表达式怎么写呢?
如这样的字符串:"a b c d e f",每两个字符中间的空格可能不止一个,要把它变成"ab cd ef",空格都是一个,正则表达式又该怎么写呢?
问题1
正则表达式中没有和“与”运算对应的符号,毕竟不是实现逻辑运算。如果你想验证一个字符串是否包含字符串"a”且包含"b",可以这样做:
Dim myRegExp, FoundMatch1, foundMatch2
Set myRegExp = New RegExp
myRegExp.Pattern = "a"
FoundMatch1 = myRegExp.Test(SubjectString)
myRegExp.Pattern = "b"
FoundMatch2 = myRegExp.Test(SubjectString)
这里判断FoundMatch1和FoundMatch2同时为True即可。
就是说与的关系可以用某种编程语言的与运算符来实现,而不是用正则中的特殊字符去表示。
14、分组与后向引用(Grouping and Backreference)
使用圆括号进行分组
把部分正则表达式置于一对圆括号内"()",可以将它们形成组。这样可以对整个组使用一些正则操作,例如重复操作。这里需要注意的是,只有圆括号才可以用于分组, 方括号和大括号都有不同的用途,不要混淆。
使用圆括号创建后向引用
“()”在分组的同时,也会创建一个“后向引用”,一个“后向引用”保存的是一个分组内正则表达式匹配的字符串。
在定义了一个正则表达式组的同时,“()”也创建了一个后向引用。一个后向引用保存了由"()"内的正则式匹配到的字符串。
除非你使用“非捕获”的“()”,否则该“()”会形成一个后向引用。这里记住后向引用会减慢正则引擎的速度,因为它需要完成一些额外的操作。如果你不使用后向引用,你可以使用非捕获的“()”来提高正则引擎的工作速度,但这样做的代价是会使你的正则式的易读性降低。
正则式"Set(Value)?"匹配"Set"或"SetValue"。在第一种情况中,第一个后向引用(这里也只有一个后向引用)是空的,因为它没有匹配任何字符串。在第二种情况中,第一个后向引用将会保存"Value"。
如果你不使用后向引用,你可以把这个正则式优化成"Set(?:Value)?",正则式中"("后的问号和冒号是一种特殊的语法格式,它会告诉正则引擎"()"不创建后向引用。注意这里的"("后的"?"和我们在11、中讲到的正则表达式中的问号是不一样的,因为"("本身是一个特殊符号,不是一个有效的正则符号。
如何使用后向引用
后向引用允许你重复使用后向引用中保存的内容。可以用“\数字”的方式进行引用。"\1"引用第一个匹配的后向引用组,"\2"引用第二个组,以此类推,"\n"引用第n个组。而"\0"则引用整个被匹配的正则表达式本身。
在正则式中使用后向引用
假设你想匹配一个HTML标签的开始标签和结束标签,以及标签中间的文本。比如<B>This is a test</B>,我们要匹配<B>和</B>以及中间的文字。我们可以用如下正则表达式:“<([A-Z][A-Z0-9]*)[^>]*>.*?</\1>”
首先,“<”将会匹配“<B>”的第一个字符“<”。然后[A-Z]匹配B,[A-Z0-9]*将会匹配0到多次字母数字,后面紧接着0到多个非“>”的字符。最后正则表达式的“>”将会匹配“<B>”的“>”。接下来正则引擎将对结束标签之前的字符进行惰性匹配,直到遇到一个“</”符号。然后正则表达式中的“\1”表示对前面匹配的组“([A-Z][A-Z0-9]*)”进行引用,在本例中,被引用的是标签名“B”。所以需要被匹配的结尾标签为“</B>”
你可以对相同的后向引用组进行多次引用,"([a-c])x\1x\1"将匹配“axaxa”、“bxbxb”以及“cxcxc”。如果用数字形式引用的组没有有效的匹配,则引用到的内容简单的为空。
一个后向引用不能用于它自身。"([abc]\1)"是错误的。因此你不能将"\0"用于一个正则表达式匹配本身,它只能用于替换操作中。
后向引用不能用于字符集内部。"(a)[\1b]"中的"\1"并不表示后向引用。在字符集内部,"\1"可以被解释为八进制形式的转码。
重复操作与后向引用
当对组使用重复操作符时,缓存里后向引用内容会被不断刷新,只保留最后匹配的内容。如果"()"中的正则式找到一个新的匹配,则后向引用中的内容会被覆盖。"([abc]+) "和"([abc])+"就有着明显的差别。对于字符串"abc",两个正则式都会匹配成功。第一个正则式将会把"abc"置于后向引用中,而第二个正则式仅会把"b"置于后向引种中。因为在第二个正则式中,"+"会导致"()"重复匹配3次,第一次"a"被保存,第二次"b"被保存,第三次"c"被保存,每一次,前一次的值都被覆盖。
这也意味着对于字符串"cab=cab", 正则式"([abc]+)=\1"会匹配成功,而"([abc])+=\1"则不能匹配成功。
后向引用使用实例
当编辑文字时,很容易就会输入重复单词,例如"the the"。使用"\b(\w+)\s+\1\b"可以检测到这些重复单词。要删除第二个单词,只要简单的利用替换功能,用"\1"替换就可以了。
15、正则表达式实战训练3
问题: 字符串中1个或者连续多个空格替换成一个Tab键
分析: 由于字符串中连续的空格的个数不太确定,如果我们逐个字符去分析的话,也是比较麻烦的,但如果用正则去实现,则简单的多。这也是一个典型的适合用正则解决的问题。解决方式如下:
Sub test()
Dim RegEx As Object
Set RegEx = CreateObject("vbscript.regexp")
With RegEx
.Global = True
.Pattern = "\s+"
End With
Range("B1").Value = RegEx.Replace(Range("A1").Text, Chr(9))
Set RegEx = Nothing
End Sub
16、正则表达式实战训练4
问题:查找字符串中连续9个数字字符并显示其开始位置。
Sub test()
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Global = True
objRegEx.Pattern = "\d{9}"
strSearchString = "aaaaaaa123456789qqqqqqqqqqqq234567891aaaa12345678"
Set colMatches = objRegEx.Execute(strSearchString)
If colMatches.Count > 0 Then
strMessage = "The following user accounts were found:" & vbCrLf
For Each strMatch In colMatches
strMessage = strMessage & strMatch.Value & " (character position " & _
strMatch.FirstIndex & ")" & vbCrLf
Next
End If
MsgBox strMessage
End Sub
17、实用的正则查找封装函数
这是一个封装的查找函数,使用正则实现查找功能,函数实现如下,使用时,把下面的函数置于VBA工程的普通模块中,就可以在代码中引用或者在单元格中直接使用函数
Function RegExpFind(LookIn As String, PatternStr As String, Optional Pos, Optional MatchCase As Boolean = True)
' This function uses Regular Expressions to parse a string (LookIn), and return matches to a
' pattern (PatternStr). Use Pos to indicate which match you want:
' Pos omitted : function returns a zero-based array of all matches
' Pos = 0 : the last match
' Pos = 1 : the first match
' Pos = 2 : the second match
' Pos = <positive integer> : the Nth match
' If Pos is greater than the number of matches, is negative, or is non-numeric, the function
' returns an empty string. If no match is found, the function returns an empty string
' If MatchCase is omitted or True (default for RegExp) then the Pattern must match case (and
' thus you may have to use [a-zA-Z] instead of just [a-z] or [A-Z]).
' If you use this function in Excel, you can use range references for any of the arguments.
' If you use this in Excel and return the full array, make sure to set up the formula as an
' array formula. If you need the array formula to go down a column, use TRANSPOSE()
Dim RegX As Object
Dim TheMatches As Object
Dim Answer() As String
Dim Counter As Long
' Evaluate Pos. If it is there, it must be numeric and converted to Long
If Not IsMissing(Pos) Then
If Not IsNumeric(Pos) Then
RegExpFind = ""
Exit Function
Else
Pos = CLng(Pos)
End If
End If
' Create instance of RegExp object
Set RegX = CreateObject("VBScript.RegExp")
With RegX
.Pattern = PatternStr
.Global = True
.IgnoreCase = Not MatchCase
End With
' Test to see if there are any matches
If RegX.test(LookIn) Then
' Run RegExp to get the matches, which are returned as a zero-based collection
Set TheMatches = RegX.Execute(LookIn)
' If Pos is missing, user wants array of all matches. Build it and assign it as the
' function's return value
If IsMissing(Pos) Then
ReDim Answer(0 To TheMatches.Count - 1) As String
For Counter = 0 To UBound(Answer)
Answer(Counter) = TheMatches(Counter)
Next
RegExpFind = Answer
' User wanted the Nth match (or last match, if Pos = 0). Get the Nth value, if possible
Else
Select Case Pos
Case 0 ' Last match
RegExpFind = TheMatches(TheMatches.Count - 1)
Case 1 To TheMatches.Count ' Nth match
RegExpFind = TheMatches(Pos - 1)
Case Else ' Invalid item number
RegExpFind = ""
End Select
End If
' If there are no matches, return empty string
Else
RegExpFind = ""
End If
' Release object variables
Set RegX = Nothing
Set TheMatches = Nothing
End Function
18、实用的正则替换封装函数
这是一个封装的替换函数,使用正则实现替换功能,函数实现如下,使用时,把下面的函数置于VBA工程的普通模块中,就可以在代码中引用或者在单元格中直接使用函数
Function RegExpReplace(LookIn As String, PatternStr As String, Optional ReplaceWith As String = "", Optional ReplaceAll As Boolean = True, Optional MatchCase As Boolean = True)
' This function uses Regular Expressions to parse a string, and replace parts of the string
' matching the specified pattern with another string. The optional argument ReplaceAll controls
' whether all instances of the matched string are replaced (True) or just the first instance (False)
' By default, RegExp is case-sensitive in pattern-matching. To keep this, omit MatchCase or
' set it to True
' If you use this function from Excel, you may substitute range references for all the arguments
Dim RegX As Object
Set RegX = CreateObject("VBScript.RegExp")
With RegX
.Pattern = PatternStr
.Global = ReplaceAll
.IgnoreCase = Not MatchCase
End With
RegExpReplace = RegX.Replace(LookIn, ReplaceWith)
Set RegX = Nothing
End Function
19、正则表达式实战训练5
问题:判断一个邮件地址格式是否有效
Private Sub CommandButton1_Click()
If IsValidEmail(TextBox1) Then 'Email address is stored in TextBox1
MsgBox "valid email address."
Else
MsgBox "Not a valid email address."
End If
End Sub
Private Function IsValidEmail(value As String) As Boolean
Dim RE As Object
Set RE = CreateObject("vbscript.RegExp")
RE.Pattern = "^[a-zA-Z0-9\._-]+@([a-zA-Z0-9_-]+\.)+([a-zA-Z]{2,3})$"
IsValidEmail = RE.Test(value)
Set RE = Nothing
End Function |
|