|            
                         系列之十七:如何使用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</areacode><exchange>$2</exchange><local>$3</local></phoneNumber>") 
  如果你觉得这个Replace使用起来很不舒服的话,下面将给出一个  类似与VB中的Replace并且扩展了它的函数 
  Public Function Replacex(sourceStr as String, oldStr as _  String, newStr as String, optional ignoreCase as _  Boolean = False,optional isGlobal as Boolean = True)  Dim re As New RegExp  re.Pattern = oldStr  re.Global = isGlobal  re.IgnoreCase = ignoreCase  Replacex = re.Replace(sourceStr, newStr)  End Function 
  下面给出使用它的一些例子:  Debug.Print Replacex("This is a test","is","at")  --> "That at a test"  最精彩的还是使用正则表达式了  Debug.Print Replacex("This is a test","\ws","at")  --> "That at a tatt"  甚至还可以这样  Debug.Print Replacex("This is a test","(\ws)","at$1")  --> "Thatis atis a tatist" 
  正则表达式的replace方法还有两个参数。  在默认的情况时正则表达式在找到了一个满足条件的时候就会停下来  但是如果你要是将isGlobal参数设置为True的话,它就会全文替换  在默认的情况下正则表达式是区分大小写的  但是如果你将ignoreCase设置为False的话,它将不区分大小写 
  获得XML的节点  现在,我们将一起来看看正则表达式在XML中是如何运用的。  首先,在Microsoft's XML 2.0解释器里面有两大难题:  第一, 这个XML解释器在装载XML文挡的时候必须要保证入口  满足定义在DTD的范围之内。这是一个大麻烦,因为就目前的情况来看,  XML更改频繁,不时会多出一些标准,不时又会产生新的标志。  第二,如果使用XSL的话不能够操纵DTD,甚至当你使用脚本语言也是一件很费力的事情。  经常要做的是你需要在XSL使用XSL的结构表达式中设置一些变量,例如  浏览器的类型或则ASP的参数呀  这时你可以使用正则表达式来解决这些问题。  当你想获取一个XML元素的时候,也许这个对象有可能并不是你想要的东东。  例如:假设一个很简单的XML结构,一个图书目录.  XML的代码如下:  <catalog>  <book>  <title>XML for Beginners</title>  <author>Fred Fnord</author>  <description>A book on XML for programming neophytes.</description>  </book>  <book>  <title>Pair-O-Dice Lost</title>  <author>U. Wajer</author>  <description>Techniques for throwing the game.</description>  </book>  <book>  <title>The Fields of Oberon</title>  <author>Alan Landis</author>  <description>The wee folk are back, and they aren't happy.</description>  </book>  <book>  <title>Distributed Computing on a Budget</title>  <author>Fred Fnord</author>  <description>Using XML and related techniques for managing distributed applications.</description>  </book>  </catalog> 
   
 |