日期:2009-01-12  浏览次数:20820 次

系列之十七:如何使用XSL和正则表达式来验证数据的有效性
XSL现在正在逐渐的成为XML中的类似与SQL在数据库设计中的地位。
虽然Microsoft's XSL仅仅是实现了其中的某一些部分的功能

但是你已经能够实现非常复杂的查询了
虽然现在的XSL仅仅还只是一种基于纯粹文本和字符串方式
的查询语言
在下面介绍的例子中,将大量使用到文本内的字符串方式的搜索,
你会发现在XML中对数据的处理很大一部分都是要使用到文本内的查询的。
这在XSL的编写中是一件非常普及的功能。
正是因为这样,你应该了解一些正则表达式应该如何来使用。

正则表达式的简单介绍
正则表达式大部分都是来自与Unix世界的需要。
在Unix中许多编程语言几乎整个都是围绕着正则表达式进行的(例如Perl, Python, Tcl)
但是令人感到奇怪的是正则表达式好象是最近才使用在Windows系列中的,
特别是大量的使用在脚本语言中,例如javascript和VBScript,
尽管你也可以将它们使用在Visual Basic或则Java中,但是显然它们似乎在
脚本语言中更加有吸引力。也许是这个缘故把,大家平时似乎都很少使用正则表达式把。

使用正则表达式,你可以根据你想查询的内容建立一个匹配模板(英文叫pattern)
一旦你使用正则表达式建立了一个模板,你就可以使用它来测试你的字符串了,
使用它可以完成很多功能:
例如判断一个字符串是否在另外一个字符串中(或则在另外一个字符串中的什么位置)
例如使用一个字符串来替换另外一个字符串
例如返回所有满足模板条件的字符串列表
例如。。。等等等等

上面我介绍了有关正则表达式的基本概念,有关它的详细说明和语法可以查阅MSDN和
javascript中的有关帮助。
在VB中如果你想引用正则表达式的话,你需要在项目中引用"Microsoft VBScript Regular Expressions"。但是如果你要是使用脚本的话就不必要了,因为在脚本里面这已经是一个
内在的对象供你引用了。
当然你需要在你的机器上安装IE4以上罗。
这个对象(在javascript中)叫RegExp
下面还是让代码来说明问题把,现在假设你想查看一个文挡里面是否包含一个特定的
字符串(例如"regular expressions")
代码见下:
代码使用VB写成。
Public Function IsTermInDocument(filePath As String,_
expr As String) As Boolean
Dim fs As FileSystemObject
Dim ts As TextStream
Dim re As RegExp
Dim text As String

Set fs = New FileSystemObject
Set ts = fs.OpenTextFile(filePath, ForReading)
text = ts.ReadAll
Set re = New RegExp
re.Pattern = expr
IsTermInDocument = re.Test(text)
End Function

Debug.print IsTermInDocument("c:\bin\myPage.htm",_
"regular expression")
上面的那个函数将根据文挡里面是否有满足条件的字符串返回True/False.
注意我加粗的部分:
第一句是建立一个正则表达式的对象
第二句是指定该正则表达式的模板
第三句就是根据模板执行查询了
呵呵,如果正则表达式的功能仅仅是这么简单的话,也许你会说
VB中的instr()不就能够代替了吗?

但是,在进行XML的数据格式化的时候,对字符串的处理远比这个复杂得多。
例如:假设你要确保你要验证的字段中是否包含一个well-formed的zip编码
(well-formed意味着它是一个有效的编码,
也许它对于某个给定的地方或则区域或则国家又是无效的
这种界于well-formed和valid的表达式将是本文里面讨论的重点)
如果你要是使用VB来进行这种判断的话将非常的难看
你需要判断是否表达式有5位或则10位数字,或则是否为字母,
然后第6位字母又必须得是一个破则号
但是如果是使用正则表达式的话,将会是这样的简单:

Set IsZipCode = New RegExp
IsZipCode.Pattern = "^\d{5}(-\d{4})?$"
if IsZipCode.test("32545-2198") then

下面将简单解释一下其中模板的含义:
^ 说明在这个表达式之前没有任何其它的字符串,
意味着要验证的表达式不是某个字符串中间的一部分,而是它的开头
\d 表示下一个字符必须是0-9中的一个数字
\d{5} 并且必须是连着的5个数字
-\d{4} 4个数字必须出现在字符"-"的后面
(-\d{4})? 这4个数字是可选的,即可有也可以没有
$ 这个表达式后面应该不会再有其它的什么东西了

最有意思的是一旦你定义好了这么一个模板,你就可以将它使用在
任何其它的正则表达式对象中,而不需要再重新建立一个正则表达式对象了了。
使用这个办法,你甚至可以把一个近2000行代码的javascript程序
减少到只有几百行,设置当你把一些模板组合在一起的时候,就能够完成
正则表达式本来不可能完成的东东了。

下面我再举一个用来验证数据有效性的例子:
例如你现在想验证一个电话号码数据是否有效
对于通用一个电话号码一般下面这几种写法都是有效的:
(800)555-1212
1(800) 555-1212
1-800-555-1212
1.800.555.1212
等等.

如果你使用脚本来写一段满足上面所有要求的代码将非常的复杂。
但是如果你使用正则表达式的话,将非常的简单,只有下面这两句代码:
Set IsPhoneNumber=new RegExp
IsPhoneNumber.pattern="^[01]?\s*[\(\.-]?(\d{3})[\)\.-]?\s*(\d{3})[\.-](\d{4})$"

你可以仔细体会上面这个代码的意义。
首先它验证第一个字符是否为0或则是否为1或则根本就没有。
然后再进行下面的验证,大家可以自己琢磨其它部分的意思把,呵呵。

查询和替换数据
当然,验证数据的有效性仅仅是它能够做的一件小事而已,
但是更有用的是:如果你能够把上面那么多种电话号码的表达方式转换
成一种统一的方式显示出来。
例如我要把上面的电话号码格式化成XML中的一个片段如下:

<phoneNumber>
<areacode>123</areacode>
<exchange>456</exchange>
<local>7890</local>
</phoneNumber>

这时的电话号码的模板分成三个部分:
(\d{3}), (\d{3}), (\d{4}), 分别表示area code, exchange, 和local number
. 在正则表达式中,正则解释器会自动
将匹配的字符赋值给变量 $1, $2, $3,等.
这样你使用下面的代码就能够实现

re.Replace("1(352)351-4159", "<phoneNumber><areacode>$1&