当我们在浏览网页的时候,常常会在浏览器中键入网址,如 www.cctv.com,然后网址就会被转换成相应的 IP 地址,其的结果可能会是“123.0.111.108”,每一个部分都是一个小于256的非负整数。严格地讲,像007这样的内容也是允许作为 IP 地址的一部分的,但在一般情况下,前面的“0”是不需要的。
在对 IP 地址格式的概念简单了解之后,我们就来做一个验证 IP 地址的实验。首先构造一个能够匹配三位数字的正则表达式 /d{1,3} ,它所匹配的结果就是 123 . 0 . 111 . 108 。注意,间隔的小数点并不作为匹配的结果。接下来,从结果的四组数字上可以看出,除了第一组数字之外,剩余的三组数字前面都有一个“小数点”,所以,我们可以构造一个新的表达式 /d{1,3}(/./d{1,3}){3} 来匹配整个字符串,第一个 /d{1,3} 匹配的是第一组数字,后面的内容匹配的是剩余的三组数字,并且每组数字前面必须要有一个“小数点”,因为“小数点”在正则表达式中可以代表任意字符,所以在使用时需要进行转义。
在第一章概述的内容中我们就了解到,正则表达式主要用于字符串匹配,而对于逻辑校验它就不太擅长了,不过为了更好地掌握正则表达式应用技巧,在这里我们仍用它做一次逻辑上的校验,之后,我们再看一个更为合理并且简单的校验方法。之前我们已经构造了一个能够简单验证 IP 地址的正则表达式了,下面要做的就是逻辑校验,更确切地说,就是保证每组数字处于 0 至 255 之间即可。从范围上看,每组数字最大位数也就是三位,最大值为 255。因此,我们需要先构造一个 200 至 255 的三位数样式 2([0-4]/d|5[0-5]) ,然后构造出一个匹配大于等于 0 小于 200 的样式 [0-1]?/d{1,2} 。现在把两部分样式合并起来 2([0-4]/d|5[0-5])|[0-1]?/d{1,2} ,这样就可以满足每组的数字验证,最后统一整理就是 (2([0-4]/d|5[0-5])|[0-1]?/d{1,2})(/.(2([0-4]/d|5[0-5])|[0-1]?/d{1,2})){3} 。这个样式与之前构造的 /d{1,3}(/./d{1,3}){3} 类似,只是将不含有数字范围的 /d 替换成含有数字范围验证的 2([0-4]/d|5[0-5])|[0-1]?/d{1,2} 。
现在,我们再尝试一种较为合理而且简单的编程实现方法,首先用正则表达式 /. 分割成四组数字,用程序验证每组数字的范围,判断它们是否存在于 0 至 255 之间,以下是 VB.Net 所实现的代码:
-
Imports System
.
Text
.
RegularExpressions
-
-
Module
ModuleForTest
-
-
Sub
Main
()
-
Try
-
Console
.
WriteLine
(
"请输入要验证的IP地址:"
)
-
Dim
userInput
As
String
=
System
.
Console
.
ReadLine
()
-
If
Regex
.
IsMatch
(
userInput
,
"^[0-9]{1,3}(/.[0-9]{1,3}){3}$"
)
Then
-
Dim
ipSections
As
String
() =
Regex
.
Split
(
userInput
,
"/."
)
-
For Each
ipNumber
As
String
In
ipSections
-
Dim
tempNumber
As
Integer
=
Convert
.
ToInt32
(
ipNumber
)
-
If
tempNumber
<
0
OrElse tempNumber
>
256
Then
-
Console
.
WriteLine
(
"非法IP地址!"
)
-
Exit Sub
-
End If
-
Next
-
Console
.
WriteLine
(
"合法IP地址!"
)
-
Else
-
Console
.
WriteLine
(
"非法IP地址!"
)
-
End If
-
Catch ex
As
Exception
-
Console
.
WriteLine
(
ex
.
Message
)
-
Finally
-
Console
.
ReadLine
()
-
End
Try
-
End Sub
-
-
End Module
接下来我们再来看一个关于文件打印的页码设置的校验例子。从下图可以看出,打印范围可以用以逗号分隔的多个具体的页码值来表现,也可以用以逗号分隔的多个页码范围来表现,或者是将页码值与页面范围进行的组合来表现;而对于页码的内容的设置,则可以通过页码和页码加小节组成。在这里我们先从简单的页码设置入手,然后再处理比较复杂的设置。
对于具体的单一页码来说,直接利用数字表达式 /d+ 来表示就可以了,但页码不可能从零开始,所以首位应该用 1 至 9 表示,而后续的数字则不受零的限制,可以采用 /d 来表示,所以修改后的非零自然数样式应该为 [1-9]/d* ;而对于页码范围来说,应该由一对具体的页码来表现,再在中间放一个标示范围的标识符“-”,最终构造出来的样式就应该为 [1-9]/d*-[1-9]/d* 。现在,我们把这两个样式结合起来,组合出我们想要的具体页码与页码范围的混合样式 ([1-9]/d*|[1-9]/d*-[1-9]/d*)(,([1-9]/d*|[1-9]/d*-[1-9]/d*))* 。先来分析下逗号之前的样式 ([1-9]/d*|[1-9]/d*-[1-9]/d*) ,它所匹配的内容就是一个单一的具体的页码或是一个页码范围,而在后面的重复样式中,除了逗号之外,其余的样式前面的完全一致,这是便于后续添加多个这样的单一的具体页码或页码范围。现在构造出来的样式就可以完全满足数字表现形式的页码或页码范围组合了。
接下来我们继续构造页码加小节的组合样式。从图中我们可以看出,单一的页码或单一的小节,都是用字母加数字组合,如第一页为 p1,第二页为 p2,依此类推,小节的表述形式与页码类似,只是字母为 s,第一节为 s1,第二节为 s2……这样一来,页码加小节的组合就是 p[1-9]/d*s[1-9]/d* ,同数字页码表现形式的规则一样,我们重复利用这个表达式,构造一个完整的样式就应该是 (p[1-9]/d*s[1-9]/d*|p[1-9]/d*s[1-9]/d*-p[1-9]/d*s[1-9]/d*)(,(p[1-9]/d*s[1-9]/d*|p[1-9]/d*s[1-9]/d*-p[1-9]/d*s[1-9]/d*))* ,逗号之前是匹配单一的页码与小节,或是页码与小节的范围,逗号以及逗号之后部分利用“*”使这种规则延续下去。
通过第一章的内容我们可以知道,正则表达式并不擅长逻辑处理,所以我们构造出的页码表达式也仅仅是对形式的一种约束,其中并不含有任何逻辑,如果想加入逻辑功能,在必要的情况下就得配合一段程序代码了。在这里,我们暂时不考虑页码之间空白字符问题并且假定页码出现的顺序必须从小到大,然后用下面这段 Javascript 程序,配合第一种样式来验证这个规则。
-
<
script
language
=
"javascript"
type
=
"text/javascript"
>
-
function
isValidPageRangeForPrint
{
-
var
pageRangeText
=
document
.
getElementById
(
"page_range"
).
value
;
-
// 是否录入页码范围
-
if
(!
pageRangeText
) {
-
return false
;
-
}
-
// 是否满足页码范围的格式
-
if
(!/
^
([
1
-
9
]
/d
*|[
1
-
9
]
/d
*-[
1
-
9
]
/d
*)(,([
1
-
9
]
/d
*|[
1
-
9
]
/d
*-[
1
-
9
]
/d
*))*
$
/.
test
(
pageRangeText
)) {
-
return false
;
-
}
-
// 是否满足页码从小到大的逻辑规则
-
var
regexForSinglePage
= /[
1
-
9
]
/d*/
;
-
var
matchResult
=
pageRangeText
.
match
(/[
1
-
9
]
/d
*|[
1
-
9
]
/d
*-[
1
-
9
]
/d*/
);
-
var
pageEnumCount
=
0
;
-
var
pageContainer
=
new
Object
();
-
// 将所有出现的页码按照出现的顺序保存到pageContainer中
-
for
(
var
i
=
0
;
i
<
matchResult
.
length
;
i
++) {
-
if
(
regexForSinglePage
.
test
(
matchResult
[
i
])) {
-
// 保存单一页码
-
pageContainer
[
""
+
pageEnumCount
++] =
matchResult
[
i
];
-
}
else
{
-
// 保存起始页码与结束页码
-
var
pagesForRange
=
matchResult
[
i
].
split
(/-/);
-
pageContainer
[
""
+
pageEnumCount
++] =
pagesForRange
[
0
];
-
pageContainer
[
""
+
pageEnumCount
++] =
pagesForRange
[
1
];
-
}
-
}
-
// 对pageContainer中的页码进行验证,后续的页码必须大于前方的页码
-
while
(
pageEnumCount
-- >
0
) {
-
if
(
pageContainer
[
pageEnumCount
] <
pageContainer
[
pageEnumCount
-
1
]) {
-
// 后续页码小于前方页码时
-
return false
;
-
}
-
}
-
return true
;
-
}
-
</
script
>
<!-- InstanceEndEditable -->