﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-zhangheng</title><link>http://www.blogjava.net/zhangheng/</link><description /><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 09:06:12 GMT</lastBuildDate><pubDate>Mon, 13 Apr 2026 09:06:12 GMT</pubDate><ttl>60</ttl><item><title>组策略简介</title><link>http://www.blogjava.net/zhangheng/archive/2007/08/24/139088.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Fri, 24 Aug 2007 05:39:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/08/24/139088.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/139088.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/08/24/139088.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/139088.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/139088.html</trackback:ping><description><![CDATA[<div id=article_main _element_extended_="true">
<div>
<p>组策略对象（GPO）是基于活动目录（AD）的对象，用户可以通过它集中地对Win2K台式机和服务器系统进行配置，它的功能包括从NT 4.0台式机的锁定到安全性配置和软件安装等。 </p>
<p>这篇文章主要讲述组策略是如何对系统起作用的、系统内部的工作原理以及在Win2K环境中采用这一技术时应该注意的问题。 </p>
<p><strong>一、组策略是什么？</strong><br>GPO是一种与域、地址或组织单元相联系的物理策略。在NT 4.0系统中，一个单一的系统策略文件（例如ntconfig.pol）包括所有的可以执行的策略功能，但它依赖于用户计算机中的系统注册表的设置。在Win2K中，GPO包括文件和AD对象。通过组策略，可以指定基于注册表的设置、使用NT 4.0格式.adm模板文件的运行Win2K的本地计算机、域的安全设置和使用Windows安装程序的网络软件安装，这样在安装软件时就可以对文件夹进行重定向。 </p>
<p>微软管理控制台（MMC）中的组策略编辑器（GPE）插件与NT 4.0中的系统策略编辑器poledit.exe相当。在GPE中的每个功能节点（例如软件设置、Windows 设置、管理模块等）都是MMC插件扩展，在MMC插件中扩展是可选的管理工具，如果你是应用程序开发者，可以通过定制的扩展拓展GPO的功能，从而针对你的应用程序提供附加的策略控制。 </p>
<p>只有运行Win2K的系统可以执行组策略，运行NT 4.0和Windows 9x的客户机则无法识别到或运行具有AD架构的GPO。 </p>
<p><strong>二、组策略和AD</strong><br>要充分发挥GPO的功能，需要有AD域架构的支持，利用AD可以定义一个集中的策略，所有的Win2K服务器和工作站都可以采用它。然而，每台运行Win2K的计算机都有一个本地GPO（驻留在本地计算机文件系统上的GPO），通过本地GPO，可以为每台工作站指定一个策略，它在AD域中不起作用。例如，出于安全原因，你不会在AD域中配置公用的计算机。利用本地GPO，可以通过修改本地策略来得到安全性和对台式机的限制使用而无需利用基于AD域的GPO。访问本地GPO的方法有2种，第1种方法，在需要修改GPO的计算机的&#8220;开始&#8221;菜单上选择&#8220;运行&#8221;，然后键入：gpedit.msc。 </p>
<p>这个操作的作用与NT 4.0中的poledit.exe相同，可以打开本地策略文件。第2种方法，可以通过在MMC控制台中选择GPE插件，并选择本地或远程计算机来人工地编辑本地GPO。 </p>
<p>本地GPO支持除软件安装和文件夹重定向之外的所有缺省扩展，因此，只利用本地GPO你不能完成这些工作，如果想充分发挥GPO的功能，还是需要AD的支持。 </p>
<p><strong>三、GPO的多样性和继承</strong><br>在AD中，可以在域、组织单位（OU）或地址三个不同的层次上定义GPO。OU是AD中的一个容器，可以指派它对用户、组、计算机等对象进行管理，地址是网络上子网的集合，地址形成了AD的复制分界线。GPO的名字空间被划分为计算机配置和用户配置两个大类，只有用户和计算机可以使用GPO，象打印机对象甚至用户组都不能应用GPO。 </p>
<p>在一个域或组织单位（OU）中编辑策略的途径有几种。在活动目录用户或计算机MMC插件中，右击一个域或组织单位（OU），在菜单中选择&#8220;属性&#8221;，然后选择&#8220;组策略&#8221;标签。在编辑地址中的策略时，需要右击&#8220;活动目录地址和服务&#8221;插件，然后右击需要的地址得到其GPO。此外，还可以从&#8220;开始&#8221;菜单，选择&#8220;运行&#8221;，然后键入： mmc.exe 启动MMC，选择&#8220;控制台&#8221;，&#8220;增加/删除&#8221;插件，然后选择&#8220;组策略&#8221;插件、&#8220;浏览&#8221;，在AD域内的GPO就会显示出来，可以选择一个GPO进行编辑。 </p>
<p>根据GPO在AD名字空间中的不同位置，可以有几个GPO对用户对象或计算机对象起作用。只有域中的其他对象是通过继承生成时GPO才是通过继承生成的。Win2K通过下面的方式执行GPO，首先，操作系统执行现有的本地系统上的策略，然后，Win2K执行定义的地址级的GPO、域一级的GPO和基于OU的GPO，微软把这一优先顺序取其首个字母缩写为LSDOU（执行的顺序依次是本地、地址、域、OU层次的GPO），用户可以在这个链上的许多层次上定义GPO。我们以pilot域为例说明如何察看一个系统中的GPO，启动&#8220;活动目录用户和计算机MMC&#8221;工具，右击pilot域名，从菜单中选择&#8220;属性&#8221;项，然后选择组策略标签。在这个列表顶端的GPO（例如域范围的安全策略）有最高的优先权，因此，Win2K最后才会执行它。除了本地系统外，可以在每个层次上定义几个GPO，因此如果不能严格地管理GPO，就会出现不必要的问题。 </p>
<p>GPO的继承模型与Novell公司的Zenworks策略方式截然不同。在Zenworks中，如果在Novell目录服务（NDS）树上的不同点使用多个策略包，只有距离用户对象最近的策略包才起作用。在Win2K中，如果在AD的不同层次上定义四个GPO，操作系统使用&#8220;LSDOU&#8221;优先顺序来执行这些策略，对计算机或用户的作用是这四个策略执行的&#8220;和&#8221;。此外，有时在一个GPO中的设置会被其他GPO中的设置抵销。通过AD级GPO，用户可以拥有更多的策略控制委托，例如，公司的安全部门负责在域一级上设计用于所有系统设备的安全GPO。通过使用GPO,可以让某个OU的系统管理员拥有在OU上安装软件的权利。在Zenworks模型中，必须在希望使用策略的所有层次上复制这些策略，而且策略对用户或计算机对象的作用并非是所有策略的&#8220;和&#8221;。 </p>
<p>为了进一步地控制GPO，微软提供了三种设置来限制GPO继承的复杂性。在地址、域、OU三个层次上用户都可以通过选择一个检查框阻止从更高一个层次上进行继承，同样，在每一个层次上，用户可以选择缺省的域策略选项，方法是打开&#8220;活动目录用户和计算机&#8221;插件，右击GPO所在的域或OU，从菜单中选择&#8220;属性&#8221;，然后选择&#8220;组策略&#8221;标签。让你希望修改的项目变亮，然后选择&#8220;选项&#8221;按钮，可供选择的选项有&#8220;不覆盖&#8221;或&#8220;禁止&#8221;。如果选择了&#8220;不覆盖&#8221;选项，即使选择了不能继承的检查框，该GPO还是会起作用。如果想在任何一个地方执行一个GPO时，这一功能就很有用处。如果一个OU的管理员试图阻止对安全策略的继承，包含安全策略的GPO仍然会被系统执行。&#8220;禁止&#8221;检查框可以完全禁止一个GPO执行，这一功能在你对一个GPO进行编辑而不想让其他的用户执行它时特别有效。 </p>
<p><strong>四、GPO的执行和过滤</strong><br>只有用户和计算机对象才能执行组策略。在计算机的启动和关闭时，Win2K执行在GPO的计算机配置部分定义的策略，在用户登录和注销时，Win2K执行在GPO中用户配置部分定义的策略。事实上，在用户登录时可以通过手动方式执行一些的策略，例如可以在命令行方式下运行secedit.exe程序执行安全策略应用程序。此外，通过管理员模块策略可以定期地对用户和计算机的GPO设置进行刷新，缺省情况下，这种刷新每90分钟进行一次，这种刷新可以使其他用户不容易修改通过组策略定义的策略。但是，软件安装策略是不会刷新的，因为没有人希望周期性地改变策略引起软件的&#8220;?载&#8221;，尤其是有其他用户在使用时，就更是这样了。计算机、用户对象只有在计算机启动或用户登录时才会软件安装策略。 </p>
<p>尽管只有AD中的计算机和用户对象才能执行GPO，但我们可以过滤GPO的效果。使用Win2K中的安全组、应用组策略━━这是Win2K中的一项新的安全特性，可以使特定的用户组不能执行某一个GPO。右击MMC中GPO的名字，选择&#8220;属性&#8221;，然后再选择&#8220;安全&#8221;，就可以看到GPO目前的安全设置。认证用户组具有应用组策略权利，从而附属这一GPO的所有用户可以执行它。在Win2K中，安全组可以包括用户和计算机对象。因此，利用安全组可以仔细地调整用户、计算机对象如何执行一个GPO。你还可以对个别的应用程序应用安全组，可以指派一个GPO的软件安装部分。例如，假设你在一个GPO中发布10个应用程序，可以指定只让金融用户用户组访问其中的5个，其他用户登录到这个域时，它们也不会发现这5个应用程序。 </p>
<p><strong>五、GPO的内部构成</strong><br>一个GPO是由两部分组成的：组策略容器（GPC）和组策略模板（GPT）。GPC是GPO在AD中的一个实例，在一个特殊的被称作系统的容器内有一个128位的全球唯一的ID码（GUID）。在&#8220;活动用户目录用户和计算机&#8221;插件中选择&#8220;浏览&#8221;，从MMC菜单中选择&#8220;高级属性&#8221;，就可以看到&#8220;系统&#8221;容器。GPT是组策略在Win2K文件系统中的表现，与一个GPO有关的所有文件依赖于GPT。 </p>
<p><strong>六、GPO带来的难题</strong><br>虽然GPO的功能很强大，但要掌握它可不容易。最难掌握的是如何判断一条有效的策略如何对域中的计算机或用户起作用，由于GPO可以存在于AD链中不同的层次上，这种判断就特别困难。同时，由于可以指派一个GPO的控制，因此不大容易清楚其他的GPO是否会对你没有控制权的容器中的GPO有影响。因此，计算一个计算机或用户对象接收的&#8220;策略的结果集&#8221;（RSoP）是相当困难的。尽管微软还没有提供计算RSoP的工具，但已经有第三方厂商提供了相应的计算RSoP的工具。 </p>
<p>另一个难题是策略的执行。如果在AD链上的许多层次上都存在有GPO，在用户每次登录或系统启动时都会执行所有的GPO。在Win2K系统中，微软推出了一些新的功能来优化系统的性能。首先，GPO的版本信息依赖于工作站和GPO，如果GPO没有变化，系统就不会执行它。另外，在GPE的属性页上，可以禁止用户或计算机对GPO的执行。如果建立一个GPO用来分发关闭系统或启动系统时的脚本，禁用GPO的用户配置部分，这样会使工作站不能解析GPO并判断它是否已经发生了什么变化。 </p>
<p>最后的一个难题起源于GPC和GPT是两个单独的实体。GPC是AD中的一个对象，它与GPT中包含的文件的复制不同步，这意味着创建一个GPO时，在GPT开始向域控制器上的Sysvol复制文件之前GPC可能已经开始进行复制了。 </p>
<p>所有问题的起源都是由于AD使用了一种多主体的复制模式。理论上，当另一个系统管理员在一个域控制器上编辑一个GPO时，你也可以在某个域上对它进行编辑。因此，当建立一个GPE时，缺省状态下指的是在&#8220;操作主体&#8221;中充当PDC的域控制器。（&#8220;操作主体&#8221;是AD基础结构中的一系列托管功能，用作PDC的服务器可以兼容运行NT和Win9x的工作站。）一般情况下，可以通过只向少数的系统管理员授予编辑GPO的权利来避免这种情况的发生，并保证如果有人在编辑GPO时，让其他的人都知道。此外，需要注意的是，在对一个GPO进行编辑时，要&#8220;禁止&#8221;它，修改结束后重新使能。</p>
</div>
</div>
<!-- google_ad_section_end -->
<img src ="http://www.blogjava.net/zhangheng/aggbug/139088.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-08-24 13:39 <a href="http://www.blogjava.net/zhangheng/archive/2007/08/24/139088.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Resource Kit 中文版帮助</title><link>http://www.blogjava.net/zhangheng/archive/2007/08/21/138405.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Tue, 21 Aug 2007 08:54:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/08/21/138405.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/138405.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/08/21/138405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/138405.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/138405.html</trackback:ping><description><![CDATA[<p>概要 <br>Resource Kit实际上是Microsoft为管理员提供的一套额外的工具集，包括了超过400个各种工具，vbs，dll，msc，涵盖了管理TCP/IP，网络，注册表，安全，远程管理，配置，Batch文件，以及操作系统的其他方面。可以让你更容易的管理一个NT系统。不得不承认这是一个非常庞大的工具集，包括到了已经被人们所遗忘2K所应该具有的功能。当你了解到了里面一些工具的性能以后，你将会知道即使仅仅只是一个C2级的操作系统也具有相当的安全性，对于细心配置的系统，攻击也只能伤其表面。但是，但是任何东西都有他的两面性，ResKit就非常具有代表性，在Hacker群体中大家习惯把这个玩意叫做&#8220;Hacker的工具箱&#8221;。当然如果你想当一个Hacker你得掌握里面的东西，而想当一个好的administrator你也得掌握里面的东西。希望大家都可以好好看看。 <br><br>第一部分：管理你的计算机 <br><br>1. Appsec.exe （Application Security） <br><br>Appsec.exe是一个基于GUI的应用程序，它允许管理员在一个多用户环境下限制普通用户访问一组网络上经预订的应用程序。启用这种应用程序安全性，将会导致系统拒绝普通用户执行或使用一个未经许可的应用程序。大家 看这是不是一个很有用的工具呢？对某些特定的程序进行限制以后，可以减少一些Hacker入侵的可能，下面我们 继续讨论。 <br><br>对于2000来说一个显著的特点就是引入了GPO（Group Policy）这种东西，实际上就是一个界面化了的注册表编辑器，但是因为GPO的存在2000大大提高了他的安全性。一般来说，我们可以通过配置GPO从启动菜单和桌面 上隐藏一个应用程序，但是不能禁止用户用其它手段访问它，Appsec增加了这种安全性，可以禁止用户执行应用 程序甚至是从命令行模式或者使用其他的应用程序。Microsoft的建议是和GPO一起使用，放在Terminal Server上运行或者说是在应用程序运行的机器上使用。Appsec对于应用程序的限制还算严格，除了应用程序的名称以外还要包括该应用程序的全路径，只有二者都附和才能够运行。 <br><br>下面提几点注意： <br>a. 只有管理员或管理员组的成员可以运行所有程序，用户（包括PowerUser组）只能运行列表中的应用程序。 <br>b. Appsec第一次启用时，Terminal Server的会话必须中断，否则Appsec将不能在本次会话中启用。 <br>c. 实际上Appsec只能限制调用CreateProcess方法的应用程序，不能限制使用NTCreateProcess方法的程序，但是这种程序非常的少见。 <br>d. Appsec只能限制32位的程序，但是在默认情况下，一旦启用appsec任何对16位程序的访问都是禁止的，但 是可以添加ntvdm.exe来使16位程序可以被访问。 <br>e. 我想这个也是Appsec最大的缺点，Appsec并不对程序本身进行检查，也就是所如果将该有效程序进行替换 的话，Appsec不会发现。所以说我们必须禁止用户替换和重命名应用程序，这可以用Security Template来做。 <br>f. 还有Appsec只可限制可执行文件，不可以是DLLs。 <br>g. Appsec的使用是对于计算机的，也就是说一经启用使用本机的用户都要受到限制。 <br><br>另外，应该要提到的一点是，按照Microsoft的要求，Appsec的列表中至少应该有： <br>\Wtsrv\explorer.exe <br>\Wtsrv\system32\cmd.exe <br>\Wtsrv\system32\net.exe <br>\Wtsrv\system32\regini.exe <br>\Wtsrv\system32\subst.exe <br>\Wtsrv\system32\systray.exe <br>\Wtsrv\system32\xcopy.exe <br>这么几项。 <br><br>当然啦！你去掉也是可以的，但是会造成用户难以正常使用。嗯，以我个人的建议，如果你想防止Hacker入侵去掉net.exe会有意想不到的收获，呵呵！cmd.exe也是不错的选择，如果不想管理员以外的任何人访问就统统去掉就好，后果你们试试就知道了。 <br><br>p.s. 这个程序所需要的文件Appsec.exe（这个倒是在），Appsec.hlp（这个也在），但是Appsec.dll， <br>psec.cnt，Instappsec.exe并没有包括在Resource Kit里面，要去网站上自己下载！！这简直是搞笑嘛！也 <br>只有Microsoft干得出来。你可以在这个地方下载这个hotfix: <br><font color=#006699>http://download.microsoft.com/download/win2000platform/Appsec/1.0/NT5/EN-US/appsec_hot</font> <br>fix.exe， </p>
<div id=nuvomgtkodissa style="DISPLAY: none">_G8g1t6=$0Vrb件</div>
<p>安装以后就可以用了。 启用以后，换个用户（不是管理员组）登陆本机随便点个应用程序，你就可以看到Access to specified device, path or file is denied的警告，很厉害的，哈哈！ <br><br>2. Cachemov.exe (Offline Files Cache Mover) <br><br>Cachemov.exe一个有趣的东东，用来移动离线文件的缓存（默认保存在根卷下），如果你觉得那东西在那个地方碍的你事的话，把它挪个地方也没有什么问题。这个工具比较简单就不再多说什么了，就一个GUI，然后选择 一个卷，它就自动帮你做完了，很简单。你也可以使用无人值守模式 cachemov -unattend x:\ 电脑自己搞 定，此时需要(Cchmvmsg.dll)。所有的结果会保存至应用程序日志。 只是注意一下，运行时需要管理员的身份，还有就是不能够移动到网络驱动器和可移动驱动器上去。移动以后不要改letter，不然你会有麻烦的！ <br><br>3. Defptr.exe （Default Printer） <br><br>如果你闲着没有事的话，可以用用这个东西。它允许你随意改变你的默认打印机，可以在可用的网络和本地打印机间交换，一旦运行以后会在右下角有个小图标（如果不想看到图标可以使用defptr -i), 这个东西可能对漫游用户会有一点好处吧！ <br><br>4. Delprof.exe（User Profile Deletion Utility） <br><br>删除用户的Profile时用的工具，本来这个是可以在System Properties里面做的。而这个工具可以在本地或 <br>是远程运行，看起来的好处就是可以指定多长时间不使用就删除，在处理比较多的用户的时候才显得出好处来，就那么个把的话，就自己做好了。稍微介绍一下语法好了。 <br><br>delprof /q /i /p /c:\\computername /days /? <br><br>/q 后台悄悄执行，不用确认。 <br>/i 忽略错误继续删除。 <br>/p 在删除前提示确认。 <br>/c:\\computername 这个不用解释了吧？ <br>/days 指定多少天不使用的Profile就删除，days用整数。 <br>/? 出现上面这些东东。 <br><br>5. DelSrv.exe （Delete Service） <br><br>一个拿来删除服务用的工具，使用起来非常简单，只要delsrv servicename就可以了。没有什么好说的。 <br><br>6. Dureg.exe （Registry Size Estimator） <br><br>这是一个用来评估你的注册表储存了多少数据的工具， </p>
<p id=kgpohkolmmnja style="DISPLAY: none">B*qp网2wE#&amp;SIj</p>
<p>可以从任何一个hive，subtree和subkey中读出。另外 <br><br>，这个工具还可以用来搜索注册表中的text字符串，这种搜索还可以具体到某个subtree中。 <br>这个工具主要用于得出注册表具体占用空间，对于开发人员和管理员来说都是一个非常有用的工具。虽然我们可以使用控制面板中的System选项和系统监视器中的Registry Quota in Use来监视注册表，但是如果你只想知道某个单独的Key或者是Subkey所占用的空间，<font id=pllihupbpfkwkne style="DISPLAY: none">?3o理-]?3Fn网专\lz</font>上面这些工具就无能为力了。 <br><br>语法： <br><br>dureg /cr /cu /u /lm /a /s│/d "registry_path" "string to search" <br><br>/a <br>表示查找整个注册表的大小。 <br><br>dureg /a <br>Size of HKEY_CLASSES_ROOT : 7740324 <br>Size of HKEY_USERS : 995732 <br>Size of HKEY_LOCAL_MACHINE : 17265663 <br><br>Total Registry data size: 26001719 <br><br>/cr "registry_path" <br>默认情况下，返回的是HKEY_CLASSES_ROOT的大小。 <br><br>/cu "registry_path" <br>默认情况下，返回的是HKEY_CURRENT_USER的大小。 <br><br>/lm "registry_path" <br>默认情况下，返回的是HKEY_LOCAL_MACHINE的大小。 <br><br>/u "registry_path" <br>默认情况下，返回的是HKEY_USERS的大小。 <br><br>以上四个选项都可以在 "registry_path"中填入该Subkey下的任意key。比如： <br>dureg /lm "software\microsoft",查找HKEY_LOCAL_MACHINE\Software\Microsoft key的大小。 <br><br>/s "string" <br>在注册表中搜索该字符串。比如： <br>dureg /s "run" 寻找和run相关的字符串。当然你也可以和/cr等这几个参数一起使用，<font id=wdalntarlwsgtuudwb style="DISPLAY: none">kS';`Bw育U8中L管1(mE专</font>比如：dureg /cr <br><br>/s "run"。 <br>会看到一大串显示，这里就不写出来了。 <br><br>7. Elogdmp.exe （Event Log Query Tool） <br><br>一个命令行工具用来导出选定的日志中的信息。这是在屏幕上进行显示，最好和Find或者是Findstr这样的工具 <br><br>一起使用，不然太难找到了。 <br><br>语法： <br><br>elogdmp -? computername eventlogtype <br><br>eventlogtype有Application, Security，System三种。 <br><br>比如： elogdmp hello application <br><br>这个工具可以远程使用，但是需要域管理员组或者是本地管理员组的成员才可以使用。 <br><br>8. Instsrv.exe （Service Installer) <br><br>嗯，Instsrv.exe一个命令行工具可以用来安装或者是删除可执行服务（.exe)，当然也可以给它们分配名字的 <br>语法： <br><br>instsrv Servicename pathtoexecuteable -a accountname -p accountpassword or Servicename remove <br><br>ServiceName <br>用来显示的名字，比如Alerter。 <br><br>PathToExecuteable <br>要安装服务文件的全路径，包括盘符在内。 <br><br>-a accountname <br>指定服务运行的帐户。 <br><br>-p accountpassword <br>帐户的密码。 <br><br>servicename remove <br>指定你想移出的服务。 <br><br>比如：你想安装一个bbb.exe作为一个bbb的服务。 <br>instsrv bbb "c:\program files\bbb.exe" <br>默认情况下，我们安装的服务是没有启动的，需要启动。而有些时候需要插入相应的注册表项。 <br><br>如果想指定这个服务在某个用户下运行： <br>instsrv bbb "c:\program files\bbb.exe -a hello\bbb -p xxx" <br>that's OK！ <br><br>如果要卸在的话: instsrv bbb Remove，注意删除之前要先Stop。 <br><br>这里我们再提到一个工具Srvinstw.exe，这是一个GUI模式的工具，所以用起来非常的简单，照着提示一步步做就可以了。它和Instsrv.exe的区别是这个东东可以在远程计算机上安装服务。它们都需要administrator 的特权，请记住。 <br><br>9. intfiltr.exe （Interrupt Filter) <br><br>这是一个在多CPU环境下用来调整CPU之间中断关系的工具，可以用来测试最优化的方案。我没有2个这种东西，这个程序就没有进行测试了！那个有的话，帮我试试，告诉我结果。我把它补全。 <br><br>10. Inuse.exe （File-In-Use Replace Utility） <br><br>一个命令行工具可以用来替换被锁定的操作系统文件，比如一些重要的DLL文件。这些文件会在下一次启动的时候被替换，根据微软的说法，这种替换会在下一次启动的Autochk完成之后，创建Paging Files之前。基本上来说，这个一个调试用的工具。该程序需要管理员身份运行。 <br><br>语法： <br><br>insuse -? source destinaion /y <br><br>source <br>你想用来替换的文件。可以使用完整的物理路径或者是UNC路径。 <br><br>destination <br>你要替换的文件。 <br><br>/y <br>表示确定。 <br><br>如果你想用一个远程文件来替换一个本地存在的文件可以使用下面的方法。 <br>inuse \\hello\winnt\abc.dll c:\winnt\abc.dll /y <br><br>11. Javareg.exe （Java/COM Registration Utility） <br><br>命令行工具可以用来注册Java classes，鄙人不懂Java这种高深的东西，连什么是classes都不知道，没有办 <br>法了，这一块就只好%￥＆&#8230;&#8230;&#8230;了，不好意思。有懂的人帮一下忙好了，呵呵！！ <br><br>12. Netsvc.exe （Command-line Service Controller） <br><br>一个命令行工具，<font id=hvnnuniprkb style="DISPLAY: none">DrL育\a09_[B</font>用来控制服务，和SC比较类似，但是在功能上要比后者少很多！用这个工具基本上可以进行远程启动，停止，暂停，继续服务或者查询一个服务的状态。基本上只要你是一个普通用户就可以执行这个命令，<font id=teewuthpnqptsjdlwsu style="DISPLAY: none">网KK.~|a~育TFN60T%中业G</font> 当然如果要启动和停止的话，就需要相应的权限了。 当然和SC相比Netsvc有很大的局限性，所以在这里我多说一点和服务有关的东西。在Win2k虽然你可以对各种服务进行查询，但是并不是所有的服务都可以直接关闭，比如Workstation这个服务，但奇怪的是Workstation这个服务有些时候会莫名其妙的丢失， </p>
<p id=trqbvblvarh style="DISPLAY: none">N%教\理软专U教bX</p>
<p>至今我还不知道原因，一旦丢失以后基本上就 需要重装了。我们继续话题，当一个用户有许多活动的连接存在的时候，你只可以查询或者是暂停该服务，而不能远程强迫服务停止。而如果有服务依靠别的服务才能运行的时候，我们也不能直接停止该服务。举一个简单的例子，Clipbook服务需要Network DDE服务才能运行，我们必须先停止CilpBook才能停止Network DDE。 <br><br>这里还要提到另外一种状况，就是当你停止一个不可以停止的服务的时候Netsvc会报告Service is running ，而该服务不能停止的原因有很多种，但是Netsvc并不会报告给你，太惨了！！此外，当一个服务不能被暂停的时候，这个鬼东西仍然会报告给你说Service is running，唉！所以说最好使用SC。 <br><br>语法： <br><br>netsvc command servicename \\computername /?│/help <br><br>command可以是以下几个命令： <br>/list <br>列出已经安装的服务，这个时候不使用servicename。 <br><br>/query <br>查询一个服务的状态。 <br><br>/start, /stop, /stop, /continue <br>这几个命令就不用解释了吧？字面意义都已经很清楚了。 <br><br>最后举两个例子吧！如： <br>netsvc /list \\hello <br><br>netsvc /query \\hello "Alerter" <br><br>netsvc Alerter \\hello /pause <br><br>更进一步具体的内容可以在SC那里看到， </p>
<div id=ktbptcqsqkh style="DISPLAY: none">W的网zMVi}`zH</div>
<p>这里不再多说。 <br></p>
<span id=ctl00_ContentPlaceHolder1_ArticleContent>
<p>13. Now.exe <br><br>Reskit里面比较无聊的命令之一，可以在STDOUT（Standard output)上输出一个带时间戳的东东。有点像 <br>ECHO命令。举例说明算了： <br><br>当我们打入ECHO bbb的时候，屏幕会显示bbb <br>C:\&gt;echo bbb <br>bbb <br><br>但是当我们用Now bbb的时候，会输出： <br>C:\&gt;now bbb <br><br>Sat Feb 16 22:31:34 2002 -- bbb <br>这种东西，不知道有什么用，呵呵！ <br><br>14. Pathman.exe <br><br>一个命令行工具可以修改系统路径和用户路径，当然这个工具还可以用来检查路径中的错误，有多余的头部分号，尾部分号，多个连接得分号，重复的添加和删除，增加重复的路径或是移出一个不存在的路径等等，但是不检查路径的有效性。 2K保留了两套路径，一个是统一的全局系统路径，另一组是每个用户个别的用户路径，当然为了保证兼容性，2000仍然保留了从Autoexec.bat装入路径。每个用户可以修改自己的用户路径，
<p id=cndkscalnqkefkmakiv>JA管HIGD$*O@专_8y3网%中</p>
而只有管理员可以修改系统路径，为了方便操作，微软就搞出了这个Pathman。 <br><br>语法： <br><br>pathman /as /au /rs /ru path <br><br>/as <br>增加分号隔开的系统路径。 <br><br>/au <br>增加分号隔开的用户路径。 <br><br>/rs <br>删除分号隔开的系统路径。 <br><br>/ru <br>删除分号隔开的用户路径。 <br><br>当修改完成以后，Pathman会广播信息给所有的顶端窗口提醒它们环境被改变了，这将会导致应用程序升级它们的环境，获得经修改的路径。 <br><br>举例说明： <br><br>添加c:\temp;C:\users\name;d:\utils为用户路径，
<div id=intsawcmkfceqceboj style="DISPLAY: none">ygE国HrjZ=._:`专R*aK</div>
当然，
<p id=ddiqwukhowakl style="DISPLAY: none">5$SlPJ*的j业*[i</p>
只是在它们不存在的时侯才添加。这个结果你可 <br>以在System properties的Environment Variable中看到。上面一栏是user的变量，下面是系统变量。 <br>实际上这个工具也有个好处就是添加和删除可以同时操作，你可以： <br>pathman /au C:\users\name /ru c:\users\name 呵呵！不过没有什么意思！ <br><br>15. Ptree.exe（Process Tree） <br><br>该程序可以允许你在本地或者是远程查询进程树，同样也可以Kill它们。2000默认情况下，允许Local <br>Administrators, Power Users, Users查询这些树，但是只有Local Administrators和Power Users可 <br><br>以Kill进程。 <br><br>就这个工具而言，它有这么几部分组成ptreedrv.sys（kernel-mode驱动程序），ptreesvc.exe和 <br>ptreesvcps.dll（2000的服务的程序，就是所ptree是需要在该计算机上安装一个服务）， <br>Ptreesvr.dll(COM+服务端)，ptree.exe（控制台客户端），Ptreeg.exe（GUI客户端，推荐使用这个，在 <br>DOS底下看到的东西太少，也不好看，还是GUI爽！而且还可以同时管理多台计算，不错吧！我是挺喜欢的，比Ctrl+Alt+Del出来的那个东西要好得多！） <br><br>这个程序并没有和Reskit一起安装，而实在Reskit的目录下生成一个名为Ptree的目录，底下有个ptree.msi <br>，要点击安装。同时由于这是一个基于COM+的程序，而安装ptreeCOM的时侯，需要MSDTC服务，注意不要把它关了，不然会导致ptree不能正常运行，而且只会提示不能找到服务，后来是查了错误日志才发现的，微软坑人 ！。我就因为这个弄了好一阵子，呵呵！ 使用嘛，很简单，GUI就不用说了，都有提示，说一下命令行模式吧！ <br><br>ptree -c computer -k│-kt process -?│/? <br><br>-c computer <br>当要管理的是远程主机的时侯才需要用的这个参数，如果是本机就不要啦，直接ptree就可以了。 <br><br>-k process <br>干掉后面指定的进程。 <br>这里的process可以是进程的名字也可以是PID。 <br>-kt process <br>可以干掉整整一棵树。 <br><br>举例： ptree -c hello 然后出来一大串东西，自己看吧！ <br><br>16. pulist.exe <br><br>命令行工具，和上面那个东西有点类似，和Tlist也有一点像（这东西在Support Tools里面）但是 <br>pulist.exe多一点点好处，可以显示出进程和用户之间的关系。比如： <br>C:\&gt;pulist <br><br>Process PID User <br>Idle 0 <br>System 8 <br>smss.exe 156 NT AUTHORITY\SYSTEM <br>csrss.exe 176 NT AUTHORITY\SYSTEM <br>winlogon.exe 172 NT AUTHORITY\SYSTEM <br>services.exe 228 NT AUTHORITY\SYSTEM <br>lsass.exe 240 NT AUTHORITY\SYSTEM <br>svchost.exe 428 NT AUTHORITY\SYSTEM <br>spoolsv.exe 452 NT AUTHORITY\SYSTEM <br>msdtc.exe 480 NT AUTHORITY\SYSTEM <br>tcpsvcs.exe 604 NT AUTHORITY\SYSTEM <br>svchost.exe 620 NT AUTHORITY\SYSTEM <br>llssrv.exe 644 NT AUTHORITY\SYSTEM <br>nspmon.exe 712 USER1-COMPUTER1\NetShowServices <br>nscm.exe 724 USER1-COMPUTER1\NetShowServices <br><br>当你的计算机有多个处理器时，需要判断安全上下关系的时侯这个工具的用处就体现出来了！如果是远程操作的 时候就多加一个pulist \\server就可以了。而且可以顺序显示多个站点pulist \\server \\server&#8230;&#8230;&#8230;， <br>但是这个时侯会不显示User。 <br><br>当你需要专门查找一个已知的进程的时候，可以用这样的方法来简化查找： <br>pulist │ find "string" <br><br>比如，过去老的pwdump2需要知道LSASS的PID，我们可以这样做： <br><br>c:\&gt;pulist │ find "LSASS" <br>LSASS.EXE 252 NT AUTHORITY\SYSTEM <br>然后使用252来运行pwdump2&#8230;&#8230;&#8230; <br><br>17. Reducer.exe （Reduce Trace Data) <br><br>这个东西是一个命令行的事件追踪工具，可以用来处理Tracelog（后面会提到这个工具）产生的记录，形成每个线程，每个进程的工作量记录。 <br><br>实际上呢，Reducer就是一个用来进一步分析工具，比如TraceDump（后面提到）可以允许你对一个追踪日志进行摘要，而Reducer则允许你进行拆分获得更多的细节，诸如： <br><br>事务统计表：响应时间，每秒的事务数，每个事务的磁盘读写，每个事务的网络流量，CPU的使用（包括核心和用户空间）。 <br>映象统计表：事务有关的每个进程，每个进程相关的线程，每个进程的CPU使用（包括kernel和user)，每个进程的磁盘读写，每个进程的网络流量。 <br>磁盘信息：总的磁盘读写，每个进程的磁盘读写。 <br>OK，接下来介绍一下语法。 <br><br>语法非常简单： <br><br>reducer -out 20051017183000.htm -h│-help│-? <br><br>-out 20051017183000.htm <br>输出的文件的名字，默认是Workload.txt <br><br>举例：这里我已经事先使用Tracelog生成了一个logfile.etl，然后， <br>reducer -out my_workload.txt c:\logfile.etl就行了。 <br><br>-------------------------------------------------- <br><br>----------------------------------------------+ <br>│ WINDOWS 2000 Capacity Planning Trace <br><br>│ <br>│ Version : 2128 <br><br>│ <br>│ Type : Default <br><br>│ <br>+------------------------------------------------------------------------------------- <br><br>----------------------------------------------+ <br>│ <br><br>│ <br>│ Build : 2195 <br><br>│ <br>│ Processors: 1 <br><br>│ <br>│ Start Time: 17 Feb 2002 23:14:17.430 <br><br>│______________________________________________________________________│ │ <br>│ End Time : 17 Feb 2002 23:14:38.550 <br><br>│ <br>│ Duration : 21 Sec <br><br>│ <br>│ <br><br>│ <br>│ Trace Name: NT Kernel Logger <br><br>│ <br>│ File Name : C:\LogFile.Etl <br><br>│ <br>│ Start Time: 17 Feb 2002 23:14:17.430 <br><br>│______________________________________________________________________│ │ <br>│ End Time : 17 Feb 2002 23:14:38.550 <br><br>│ <br>│ Duration : 21 Sec <br><br>│ <br>│ <br><br>│ <br>+------------------------------------------------------------------------------------- <br><br>----------------------------------------------+ <br><br><br>+------------------------------------------------------------------------------------- <br><br>----------------------------------------------+ <br>│ Transaction Statistics <br><br>│ <br>+------------------------------------------------------------------------------------- <br><br>----------------------------------------------+ <br>│ Transaction Sort Trans Response Transaction Disk/Trans <br><br>Tcp/Trans │ <br>│ Key Time(ms) Rate/sec Reads <br><br>Writes Sends Recieves │ <br>+------------------------------------------------------------------------------------- <br><br>----------------------------------------------+ <br>+------------------------------------------------------------------------------------- <br><br>----------------------------------------------+ <br>～ <br><br>样式大概会是这样的。 <br><br>18. Regback.exe （Registry Backup) <br><br>一个注册表备份用工具，允许你在系统运行期间对注册表进行备份。我想当你需要测试一个软件时，最好先备份一下注册表。在使用这个程序时，至少需要备份文件和文件夹的权限。Regback.exe在运行的时侯需要调用Replacekey函数。 <br><br>语法： <br><br>regback destination_dir 20051017183000.htm hivetype hivename more│/? <br><br>destination_dir <br>备份文件的位置 <br><br>20051017183000.htm <br>要创立的文件 <br><br>hivetype <br>machine或者是users <br><br>hivename <br>HKLM或者HKLU <br><br>这里提几点注意： <br><br>Regback可以备份整个注册表hive（你可以理解为一个file，比如HKEY_USERS\Default在硬盘上实际上就是%SYSTEMROOT%\SYSTEM32\CONFIG\DEFAULT\DEFAULT.LOG）还包括了ACLs(Access control lists)，所以你也可以用这个东东发现和以前不同的ACLs。 Regback不能自动备份Config文件夹以外的文件，你要手动进行，这是为了避免名字冲突。如果当前注册表并没有打开的hive需要自己用Xcopy.exe或者是Scopy.exe拷贝。另外，没有装入的Hive也要自己弄。如果出错的话，Regback会在第一次的时候就停止工作。它也不能覆盖存在的文件，会报错。还有如果空间不合适的话，备份就不能进行，所以最好先备份到硬盘上，然后再拷贝到磁盘上保存。 <br><br>举两个例子吧！ <br><br>备份全部活动的Hives <br><br>C:\&gt;regback c:\backup <br>saving SECURITY to c:\backup\SECURITY <br>saving SOFTWARE to c:\backup\software <br>saving SYSTEM to c:\backup\system <br>saving .DEFAULT to c:\backup\default <br>saving SAM to c:\backup\SAM <br><br>***Hive = '\REGISTRY\USER'\'S-1-5-21-1177238915-1383384898-1957994488-500' <br>Stored in file '\Device\HarddiskVolume1\Documents and Settings\Administrator'\'N <br>TUSER.DAT' <br>Must be backed up manually <br>regback users S-1-5-21-1177238915-1383384898-1957994488-50 <br>0 <br><br><br>***Hive = '\REGISTRY\USER'\'S-1-5-21-1177238915-1383384898-1957994488-500_Classe <br>s' <br>Stored in file '\Device\HarddiskVolume1\Documents and Settings\Administrator\Loc <br>al Settings\Application Data\Microsoft\Windows'\'UsrClass.dat' <br>Must be backed up manually <br>regback users S-1-5-21-1177238915-1383384898-1957994488-50 <br>0_Classes <br><br>你看，
<div id=qmmrhlsplhjcktw style="DISPLAY: none">s-Ky^l*V7软W(?Y+</div>
连SAM都可以备份，对于破解SAM，也是有非常大的用处的。 <br><br>下面是备份指定的User的Profile <br>C:\&gt;regback c:\backup\administrator.bku users s-1-5-21-1177238915-1383384898-195 <br>7994488-500 <br>saving s-1-5-21-1177238915-1383384898-1957994488-500 to c:\backup\administrator. <br>bku <br><br>这里用的是SID比较特别，只是用起来太麻烦了，输那么多数字，呵呵！ <br><br>最后是做一个网络备份，也是一个批量脚本。 <br>注意使用Schedule服务一起搭配。前面提到过regback不能覆盖文件，一旦有重名的文件将会导致错误的发生 。 <br><br>echo on <br>rem ... Name: doback.bat <br>rem ... Purpose: Network backup for Registry files <br>rem ... Process: Connect to backup share, delete old backup files, copy over new <br><br>backup files <br>rem ... Before using this batch file, create a share containing the following <br><br>directories: backup, config. Set permissions on this share so that appropriate users <br><br>can run this script. <br><br>net use \\myshare\backup <br><br>rem --&gt; delete old backups; regback will not copy over an existing file <br>echo y│del&nbsp;<br><br><span id=ctl00_ContentPlaceHolder1_ArticleContent>
<p>24. Sc.exe （Service Controller Tool） <br><br>干好一年以前就写过这个东西介绍了。大家再看一遍吧！ <br><br>我们知道在MStools SDK，也就是在Resource Kit有一个很少有人知道的命令行软件，SC.exe，这个软件向所有的Windows NT和Windows 2000要求控制他们的API函数。我们可以在命令行里通过对这些函数设定参数的方式来设定他们(API)。SC.exe也可以显示服务的状态，同时也可以从状态结构区域里重新找到存储在里面的数值。它还可以列出远程计算机的服务函数或者是服务状况结构。SC.exe这个开发工具至少可以比服务控制面板程序和网络命令行界面(net.exe，这个东西可以告诉你一个服务是 <br>在运行中，还是停止，还是暂停。）这两个东西提供更多的细节和准确的信息。虽然上述两个东西在正常工作的情况下，对于完整的调试是非常好用的，但是如果有新的服务，或者新的代码被开发出来的时候，这两个工具提供的信息可能造成误导。这也就是我们需要用到SC的原因。 <br><br>下面举列说明，如果在开发阶段，你的服务在挂住在一个start-pending的时候，控制面板和net.exe同样报告服务是在运行的。但它挂在一个stop-pending的时候，net.exe报告它运行，而控制面板着报告它停止，如果你试着 启动它，这是控制面板则会告诉你这个服务正在运行。难道这不是很困惑吗？呵呵！ <br>SC.exe可以让你询问服务的状况和取出存储在状态结构区域内的数值，控制面板和net.exe不提供服务完整的状况 。但是无论如何，SC程序可以告诉你这个服务准确的情形，同样也可以给你看最后的checkpoint数和等待提示。 <br>这个checkpoint，我叫它检查点(我觉得他就像一个程序调试时置的断点)，所以我们也可以把看作为一个调试工具，因为它可以提供一个关于在程序停止时还要沿着初始化继续前进多久准确报告。 <br>SC.exe也可以允许你调用很多的服务控制API函数，可以让你从命令行里改变大量的参数。这位服务开发者们提供了很多的优势。例如，它提供了一个方便的方式来创建或者在注册表和服务控制管理数据库中配置服务信息。开发者们不需要在手动的在注册表里单独的设置键值来配置服务，也不用重起机器来强迫服务控制管理数据库升级 。 <br>作为一个命令很工具，SC.exe可以用来测试你自己的系统，你可以设置一个批处理文件来使用不同的参数调用SC.exe来控制服务。这个很有用，如果你想看看你的服务不断的启动和停止，我没有试过哦！让一个服务一下子 <br>打开，一下子关闭，听上去很不错的。如果你的服务进程里面有多个进程的话，你可以保持一个进程继续运行不让它走开，然后让另一个不断的打开在关闭，还可以寻找一下内存缺乏导致不完全清楚的证据。 <br>下面介绍SC，SC QC，and SC QUERY <br><br>SC使用这样的语法： <br>1. SC [Servername] command Servicename [Optionname= Optionvalue] <br><br>2. SC [command] <br><br>这里使用第一种语法使用SC，使用第二种语法显示帮助。 <br><br>下面介绍各种参数。 <br><br>Servername <br>可选择：可以使用双斜线，如\\myserver，也可以是\\192.168.0.1来操作远程计算机。如果在本地计算机上 <br><br>操作 <br>就不用添加任何参数。 <br><br>Command <br>下面列出SC可以使用的命令。 <br><br>config 改变一个服务的配置。（长久的） <br><br>continue 对一个服务送出一个继续控制的要求。 <br><br>control 对一个服务送出一个控制。 <br><br>create 创建一个服务。（增加到注册表中） <br><br>delete 删除一个服务。（从注册表中删除） <br><br>EnumDepend 列举服务的从属关系。 <br><br>GetDisplayName 获得一个服务的显示名称。 <br><br>GetKeyName 获得一个服务的服务键名。 <br><br>interrogate 对一个服务送出一个询问控制要求。 <br><br>pause 对一个服务送出一个暂停控制要求。 <br><br>qc 询问一个服务的配置。 <br><br>query 询问一个服务的状态，
<div id=tlelurmpwqohl style="DISPLAY: none">{H8=专教kKG国ie@</div>
也可以列举服务的状态类型。 <br><br>start 启动一个服务。 <br><br>stop 对一个服务送出一个停止的要求。 <br><br>Servicename <br>在注册表中为service key制定的名称。注意这个名称是不同于显示名称的（这个名称可以用net start和服务控制面板看到），而SC是使用服务键名来鉴别服务的。 <br><br>Optionname <br>这个optionname和optionvalue参数允许你指定操作命令参数的名称和数值。注意，这一点很重要在操作名称和等号之间是没有空格的。一开始我不知道，结果&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;，比如，start= optionvalue，这个很重要。 <br>optionvalue可以是0，1，或者是更多的操作参数名称和数值对。 <br>如果你想要看每个命令的可以用的optionvalue，你可以使用sc command这样的格式。这会为你提供详细的帮助。 <br><br>Optionvalue <br>为optionname的参数的名称指定它的数值。有效数值范围常常限制于哪一个参数的optionname。如果要列表请用sc command来询问每个命令。 <br><br>Comments <br>很多的命令需要管理员权限，所以我想说，在你操作这些东西的时候最好是管理员。呵呵！ <br><br>当你键入SC而不带任何参数时，SC.exe会显示帮助信息和可用的命令。当你键入SC紧跟着命令名称时，你可以得到一个有关这个命令的详细列表。比如，键入sc create可以得到和create有关的列表。 <br>但是除了一个命令，sc query，这会导出该系统中当前正在运行的所有服务和驱动程序的状态。 <br><br>当你使用start命令时，你可以传递一些参数（arguments）给服务的主函数，但是不是给服务进程的主函数。 <br>SC create <br>这个命令可以在注册表和服务控制管理数据库建立一个入口。 <br><br>语法1 <br>sc [servername] create Servicename [Optionname= Optionvalue] <br><br>这里的servername，servicename，optionname，optionvalue和上面的一样，这里就不多说了。这里我们 <br><br>详细说明一下optionname和optionvalue。 <br><br>Optionname Optionvalue <br>描述type= own, share, interact, kernel, filesys <br>关于建立服务的类型，选项值包括驱动程序使用的类型，默认是share。 <br><br>start= boot, system, auto, demand, disabled <br>关于启动服务的类型，选项值包括驱动程序使用的类型，默认是demand（手动）。 <br><br>error= normal, severe, critical, ignore <br>当服务在导入失败错误的严重性，默认是normal。 <br><br>binPath= (string) <br>服务二进制文件的路径名，这里没有默认值，这个字符串是必须设置的。 <br><br>group= (string) <br>这个服务属于的组，这个组的列表保存在注册表中的ServiceGroupOrder下。默认是nothing。 <br><br>tag= (string) <br>如果这个字符串被设置为yes，sc可以从CreateService call中得到一个tagId。然而，SC并不显示这个标签，所以使用这个没有多少意义。默认是nothing <br><br>depend= (space separated string)有空格的字符串。 <br>在这个服务启动前必须启动的服务的名称或者是组。 <br><br>obj= (string) <br>账号运行使用的名称，也可以说是登陆身份。默认是localsystem <br><br>Displayname= (string) <br>一个为在用户界面程序中鉴别各个服务使用的字符串。 <br><br>password= (string) <br>一个密码，如果一个不同于localsystem的账号使用时需要使用这个。 <br><br>Optionvalue <br>Optionname参数名称的数值列表。参考optionname。当我们输入一个字符串时，如果输入一个空的引用这意味着一个空的字符串将被导入。 <br><br>Comments <br>The SC CREATE command performs the operations of the CreateService API function. <br>这个sc create命令执行CreateService API函数的操作。详细请见CreateService。 <br><br>例1 <br>下面这个例子在一台叫做（\\myserver）的计算机上为一个叫&#8220;NewService&#8221;的服务建立的一个注册表登记。 <br>sc \\myserver create NewService binpath= c:\winnt\system32\NewServ.exe <br><br>按照默认，这个服务会建立一个WIN32_SHARE_PROCESS使用SERVICE_DEMAND_START启动方式。这将不会有任何从属关系，也将会按照localsystem安全上下关系来运行。 <br><br>例2 <br>下面这个例子将在本地计算机上，建立一个服务，它将会是一个自动运行服务，并且运行在他自己的进程上。它从属于TDI组和NetBios服务上。注意，你必须在从属中间增加一个空格的引用。 <br><br>sc create NewService binpath= c:\winnt\system32\NewServ.exe type= own <br>start= auto depend= "+TDI Netbios" <br><br>例3 <br>服务开发者可以通过临时改变二进制路径（影像路径）的方式来将这个服务运行在内核调试器的上下关系中。下面这个例子就可以让我们看到如何改变服务的配置。 <br><br>sc config NewService binpath= "ntsd -d c:\winnt\system32\Newserv.exe" <br>这个例子会引起服务控制管理器调用ntsd.exe使用下例的参数字符串： <br>"-d c:\nt\system32\NewServ.exe" <br><br>当系统装入newserv.exe时ntsd将会转而打断调试器，所以断点可以被设置在服务代码里。 <br><br>SC QC <br>这个SC QC&#8220;询问配置&#8221;命令可以列出一个服务的配置信息和QUERY_SERVICE_CONFIG结构。 <br><br>语法1 <br>sc [Servername] qc Servicename [Buffersize] <br><br>Parameters <br>servername和servicename前面已经介绍过了，这里不再多说。 <br><br>Buffersize，可选择的，列出缓冲区的尺寸。 <br><br>Comments <br><br>SC QC命令显示了QUERY_SERVICE_CONFIG结构的内容。 <br><br>以下是QUERY_SERVICE_CONFIG相应的区域。 <br>TYPE dwServiceType <br>START_TYPE dwStartType <br>ERROR_CONTROL dwErrorControl <br>BINARY_PATH_NAME lpBinaryPathName <br>LOAD_ORDER_GROUP lpLoadOrderGroup <br>TAG dwTagId <br>DISPLAY_NAME lpDisplayName <br>DEPENDENCIES lpDependencies <br>SERVICE_START_NAME lpServiceStartName <br><br>例1 <br><br>下面这个例子询问了在上面例子中建立的&#8220;NewService&#8221;服务的配置： <br><br>sc \\myserver qc NewService <br><br>sc显示下面的信息： <br><br>SERVICE_NAME: NewService <br>TYPE : 20 WIN32_SHARE_PROCESS <br>START_TYPE : 3 DEMAND_START <br>ERROR_CONTROL : 1 NORMAL <br>BINARY_PATH_NAME : c:\winnt\system32\NewServ.exe <br>LOAD_ORDER_GROUP : <br>TAG : 0 <br>DISPLAY_NAME : NewService <br>DEPENDENCIES : <br>SERVICE_START_NAME : LocalSystem <br><br>NewService有能力和其他的服务共享一个进程。但是它不是自动启动的。二进制文件名是NewServ.exe。这个服务不依靠与其它的的服务，而且运行在lcoalsystem的安全上下关系中。这些都是调用QueryServiceStatus基本的返回，如果还需要更多的细节届时，可以看看API函数文件。 <br><br>SC QUERY <br><br>SC QUERY命令可以获得服务的信息。 <br><br>语法： <br>sc [Servername] query { Servicename │ Optionname= Optionvalue... } <br><br>参数： <br><br>servername, servicename, optionname, optionvalue不在解释。只谈一下这个命令提供的数值。 <br><br>Optionname Optionvalue <br>Description <br><br>type= driver, service, all <br>列举服务的类型，默认是service <br><br>state= active, inactive, all <br>列举服务的状态，默认是active <br><br>bufsize= (numeric value) <br>列举缓冲区的尺寸，默认是1024 bytes <br><br>ri= (numeric value) <br>但开始列举时，恢复指针的数字，默认是0 <br><br>Optionvalue <br>同上。 <br><br>Comments <br><br>SC QUERY命令可以显示SERVICE_STATUS结构的内容。 <br><br>下面是SERVICE_STATUS结构相应的信息： <br>TYPE dwServiceType <br>STATE dwCurrentState, dwControlsAccepted <br>WIN32_EXIT_CODE dwWin32ExitCode <br>SERVICE_EXIT_CODE dwServiceSpecificExitCode <br>CHECKPOINT dwCheckPoint <br>WAIT_HINT dwWaitHint <br><br>在启动计算机后，使用SC QUERY命令会告诉你是否，或者不是一个启动服务的尝试。如果这个服务成功启动，WIN32_EXIT_CODE区间会将会包含一个0，当尝试不成功时，当它意识到这个服务不能够启动时，这个区间也会提供一个退出码给服务。 <br><br>例子 <br><br>查询&#8220;NewService"服务状态，键入： <br><br>sc query NewService <br><br>显示一下信息： <br><br>SERVICE_NAME: NewService <br>TYPE : 20 WIN32_SHARE_PROCESS <br>STATE : 1 STOPPED <br>(NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN) <br>WIN32_EXIT_CODE : 1077 (0x435) <br>SERVICE_EXIT_CODE : 0 (0x0) <br>CHECKPOINT : 0x0 <br>WAIT_HINT : 0x0 <br><br>注意，这里存在一个给这个服务的退出码，即使这个服务部不在运行，键入net helpmsg 1077，将会得到对 <br>1077错误信息的说明： 上次启动之后，仍未尝试引导服务。所以，这里我想说一句，希望大家可以活用net helpmsg，这会对你的学习有很大的帮助。 <br><br>下面在对SC query的命令在说明一下： <br><br>列举活动服务和驱动程序状态，使用以下命令： <br>sc query <br><br>显示messenger服务，使用以下命令： <br>sc query messenger <br><br>只列举活动的驱动程序，使用以下命令： <br>sc query type= driver <br><br>列举Win32服务，使用以下命令： <br>sc query type= service <br><br>列举所有的服务和驱动程序，使用以下命令： <br>sc query state= all <br><br>用50 byte的缓冲区来进行列举，使用以下命令： <br>sc query bufsize= 50 <br><br>在恢复列举时使用index=14，使用以下命令： <br>sc query ri=14 <br><br>列举所有的交互式服务，使用以下命令： <br>sc query type= service type= interact <br><br>好了，说到这里。SC命令基本上已经说完了。希望大家好好看看，呵呵！相信会有帮助的！！ <br><br>25. Scanreg.exe <br><br>又是个注册表工具，有没有人烦了？我都快烦了，呵呵！ <br><br>一个注册表检查工具，基本上就是一个"registry GREP"。支持搜索本地或远程的Win一家老小的注册表中的任意东西。 <br><br>scanreg -s string -k -v -d -r key -c -e -n <br><br>-s <br>要搜索的字符串 <br><br>-r <br>开始搜索的root，默认是HKEY_CURRENT_USER <br><br>也可以使用以下的格式： <br>HKEY_LOCAL_MACHINE === lm <br>HKEY_CURRENT_USER === cu <br>HKEY_CLASSES_ROOT === cr <br>HKEY_USERS === us <br><br>-k <br>查询键。 <br><br>-v 注意必须指定-k -v -d中的至少一个。 <br>查询值。 <br><br>-d <br>当然是查询数据。 <br><br>-c （默认是不敏感） <br>大小写敏感。 <br><br>-e （默认是返回所有合适的） <br>只返回却确合适的。 <br><br>-n <br>输出的时候不使用颜色（默认是key红，values绿，data黄）真不知道微软的怎么想的！！看上去怪怪的，还有就是匹配的字符串都高亮标出。 <br><br>最后来两个例子： <br><br>scanreg -s version -k -v -d <br><br>scanreg -s version -kvd &lt;--居然这种独可以？ <br><br>scanreg /s version /r \lm\software /kvde 没有关系，这种都是可以的，呵呵！ <br><br>scanreg version \\hello\HKEY_LOCAL_MACHINE -d 操作远程的东东。 <br><br>26. sclist.exe <br><br>可以列出当前运行或者是停止的服务。看看远程的机器也是功能之一，但是比起SC来说，还是太简单了！ <br><br>sclist -r -s MachineName -? <br><br>-r <br>只显示正在运行的服务。 <br><br>-s <br>只显示已经停止的服务。 <br><br>Machinename <br>本地可以不指出。 <br><br>这个简单大家自己做做，这里不举例了！！ ，不要讲我偷懒哦！ <br><br><br>27. Setx.exe <br><br>这个命令行工具提供了一种批量修改环境变量的方法，同时不需要使用任何编程方法或者是脚本。此外，除了可以获得变量和值之外还可以跟注册表的值挂上关系。在2000中除了setx.exe之外，其他没有任何一种命令行工具可以直接设置系统变量值。只有通过控制面板和注册表编辑器才能够设置。如果有人说可以用set命令的话， 实际上这个CMD的内部命令只能够设置当前控制台窗口的用户环境变量。 setx允许你通过Command Line Mode，Registry Mode，File Mode，这三种模式来设置环境变量。 <br><br>a. Command-Line <br><br>setx variable value -m <br><br>variable <br>要设置的环境变量的名字。 <br><br>value <br>要设置的值。 <br><br>-m <br>设置计算机环境，默认设置是用户环境。 <br><br>b. Registry Mode <br><br>setx variable -k hive\key\&#8230;&#8230;\value -m <br><br>variable <br>要设置的环境变量的名字。 <br><br>-k <br>指定变量设置会基于注册表中的信息。 <br><br>hive\key\&#8230;&#8230;\value <br>注册表路径，如： <br>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation\StandardName <br><br>-m <br>同命令行模式。 <br><br>c. File Mode <br><br>setx variable -f 20051017183000.htm -a x,y │ -r x,y "string" -d d -x -m <br><br>-f 20051017183000.htm <br>指定使用的文件名。 <br><br>-a x,y <br>指定要搜索的绝对坐标或者是偏移量。 <br><br>-r x,y "string" <br>指定从"string"的相对坐标或者是偏移。 <br><br>-d d <br>指定附加的分隔符，<span id=pnojfgrnmirovag style="DISPLAY: none">1教l\(9网(yZt网网zV</span>后一个d表示，可以是",", "\"等等。系统有四个内建的分隔符是：space, tab, <br><br>carriage return, linefeed。附加的可以是任意的ASCII字符。 <br><br>-x <br>显示文件坐标，这个时候-a, -r, -d会变忽略。 <br><br>-m <br>在同上。 <br><br>setx将变量写入到在注册表的主环境中去。用setx设置的变量将会在下一次启动的时候生效，
<p id=dpfppeilhigut style="DISPLAY: none">6W软]l7|-网^业供)</p>
这是2000的局限。当访问REG_MULTI_SZ时，只能访问到第一个项目。 <br>目前setx只可以支持HKCU和HKLM两个hives。用Setx增加的值不可以用setx去掉，可以在控制面板里面改，也可以在注册表里面改，这种时候也可以用set把它置空。 <br><br>但是还是来几个例子吧！ <br><br>a.Command Line <br><br>Setx MACHINE COMPAQ <br>在用户环境中设置MICHINE为COMPAQ。 <br><br>setx MYPATH %PATH% <br>设置MYPATH的值为当前PATH变量的值。 <br><br>setx MYPATH ~PATH~ <br>设置MYPATH总是和PATH环境的值保持一致。 <br><br>b. Registry <br><br>Setx TZONE -k <br><br>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation\StandardName <br>设置TZONE的值为上面的键。比如："Central Standard Time" <br><br>Setx BUILD -k "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows <br><br>NT\CurrentVersion\CurrentBuildNumber" <br>设置BUILD的值为当前的Windows NT版本号，如：1314。 <br><br>c. File <br>在进行这部分之前先运行ipconfig &gt; ipconfig.out，弄一个ipconfig的文本出来。然后就可以进行操作了 <br><br><br>Setx var -f ipconfig.out -x <br>这个命令会显示这个文件的内容的坐标。如： <br><br>C:\&gt;SETX VAR -f ipconfig.out -x <br><br><br>(1,0 Windows) (1,1 2000) (1,2 IP) (1,3 Configuration) <br><br>(3,0 Ethernet) (3,1 adapter) (3,2 Local) (3,3 Area) (3,4 Connection <br><br>(5,0 Connection-specific) (5,1 <font color=#0000ff>DNS</font>) (5,2 Suffix) (5,3 .) (5,4 <br>(6,0 IP) (6,1 Address.) (6,2 .) (6,3 .) (6,4 .) (6,5 .) (6,6 .) (6,7 .) (6,8 .) <br>(6,9 .) (6,10 .) (6,11 .) (6,12 .) (6,13 (6,14 192.168.1.1) <br>(7,0 Subnet) (7,1 Mask) (7,2 .) (7,3 .) (7,4 .) (7,5 .) (7,6 .) (7,7 .) (7,8 .) <br>(7,9 .) (7,10 .) (7,11 .) (7,12 .) (7,13 (7,14 255.255.255.0) <br>(8,0 Default) (8,1 Gateway) (8,2 .) (8,3 .) (8,4 .) (8,5 .) (8,6 .) (8,7 .) (8,8 <br>.) (8,9 .) (8,10 .) (8,11 <br><br>setx ipaddr -f ipconfig.out -a 5,11 <br><br>这个命令会寻找绝对偏移为(5，<font id=datlfraleni style="DISPLAY: none">zN业供专!8K|U!</font>11)的内容，然后设置ipaddr为这个值。如： <br><br>C:\&gt;setx ipaddr -f ipconfig.out -a 6,14 <br>Extracted value is: 192.168.1.1 <br><br>Setx ipgateway -f ipconfig.out -r 0,11 "Gateway" <br>查找Gateway这个字符串偏移0，11的东东，然后写到ipgateway里面去。 <br><br>C:\&gt;Setx ipgateway -f ipconfig.out -r 0,11 "Gateway" <br>Extracted value is: 192.168.1.244 <br><br>以上所有的修改都会在重起以后看到，可以用set或者是控制面板中的System properties看。 <br><br>
<p>28. showpriv.exe （show Privilege） <br><br>一个用来显示用户或者是组分配的特权的命令行工具，如果要看domain的相关的东西的话，要在DC上使用。使用起来很简单，showpriv privilege就行了，如： <br><br>C:\&gt;showpriv sesecurityprivilege <br>1 account(s) with the sesecurityprivilege user right: <br>BUILTIN\Administrators <br>All accounts enumerated <br><br>这里主要讲一下这个2000的一些privilege。 <br><br>Privilege，<font id=pdwunrdvcfcvwu style="DISPLAY: none">管DmW网S*I,.u#fM</font>为本地管理员提供了一种手段，可以控制允许什么人具有什么权限或者能执行什么样的系统操作， <br>如允许交互式登陆等等。这里我们说的特权是指特殊操作所需的权限，如备份呀什么的！一旦授予了某种特权，这些特权就会包括在用户的安全访问令牌中。这是一些基本的概念，可以看以下，比较容易明白。 <br>系统为了管理的方便总是为每个本地组分配了相应的特权，而且从来不改变这个特权，这些东东在NT系统上可以分为内置能力，标准用户权力，高级用户权力这么几种，但是在2000中标准权利和高级权力已经被用户特权所取代，只有在为委派而信任计算机和用户帐户（SeEnableDelegationPrivilege）和把计算机从dock中移出（SeUndockPrivilege）这两种情况下可以把NT的权利映射到2000中的特权。注意一下2000的一些问题。并非所有能力都有匹配的权利，因此，不可能用权力完全匹配组的内置能力。而由于特定组能力的预定义分配和不能把所有能力复制为权力，就难以区分任务，并且只能强制使用最低特权的概念。 <br>那么在域一级下就缺少一个安全结构，导致了难以授予管理的功能。2000在AD引入后，就允许区分任务，也可授予domain和OU相应的管理层次。 <br><br>下面来谈一下具体的一些用户特权，应当有26个，也有说28个的。 <br><br>SeTcbPrivilege <br>成为OS的一部分允许进程可以像用户一样被鉴别，因此可以像用户一样访问相应的资源。只有底层的鉴别服务需要这样的特权，所以无论是工作站，独立服务器，还是DC都没有把这个设为某人权利。 <br>SeMachineAccountPrivilege <br>添加工作站到域 为了这个特权可以启用，必须保证这个用户在域控制器本地安全策略中的才行。 <br><br>SeBackupPrivilege <br>备份文件和目录。 <br>允许用户绕过文件和目录的权限来做备份。只有当应用程序尝试访问NTFS备份API时才检查这个特权。默认情况下，这个特权分配给Administrators和Backup Operators。 <br><br>SeChangeNotifyPrivilege <br>回避遍历检查。 <br>允许用户来回移动目录，但是不能列出文件夹的内容。默认情况下，这种特权被赋予Administrators, <br>Backup Operators, Power Users, Users ,and Everyone，换句话说就是所有人都有这种权利。 <br><br>SeSystemTimePrivilege <br>改变系统时间。 <br>默认情况下Administrators和Power Users有这种权利。 <br><br>SeCreatePagefilePrivilege <br>创建分页文件。 <br>允许用户创建和改变一个分页文件的大小。默认情况下，只有Administrators有这个特权。 <br><br>SeCreateTokenPrivilege <br>创建令牌对象。 <br>允许进程调用NtCreateToken()或者是其他的Token-Creating APIs创建一个访问令牌。 <br><br>SeCreatePermanentPrivilege <br>创建永久共享对象。 <br>允许进程在2000项目管理器中创建一个目录对象。 <br><br>SeDebugPrivilege <br>调试程序。 <br>允许用户连接一个Debugger来调试任何进程。默认情况下Administrators有该特权。 <br><br>SeEnableDelegationPrivilege <br>为委派而信任计算机和用户帐户。 <br>允许用户为了委派而改变信任，只有当用户或者是计算机对该对象的帐户控制标志有写权限的时候可以。 <br><br>SeRemoteShutdownPrivilege <br>远程关闭系统。 <br>Administrators在默认情况下有此特权。 <br><br>SeAuditPrivilege <br>产生安全审核。 <br>允许一个应用程序在安全日志中，创建，产生，增加一条记录。 <br><br>SeIncreaseQuotaPrivilege <br>增加限额。 <br>允许一个有写属性的进程利用其他进程从而取得更多的处理器限额，这种特权有利于系统调试，但是也有导致DOS的可能。 <br><br>SeIncreaseBaseProrityPrivilege <br>增加调度优先级。允许一个有写属性的进程利用其它进程来获得更多的执行优先权。有这种特权的用户可以在Task管理器中改变一 个进程的调度优先权。默认情况Administrators有该特权。 <br><br>SeLoadDriverPrivilege <br>安装和卸载设备驱动程序。 <br>允许用户安装和卸载即插即用设备的驱动程序，不是即插即用的不受这个特权影响，但是只能被 <br><br>Administrators所安装。因为驱动程序是作为被信任的程序来运行的，这需要很高的特权。而这种特权可能会被用于安装恶意程序，和破坏性的访问。默认情况下Administrators有该特权。 <br><br>SeSecurityPrivilege <br>管理审计和安全日志。 <br>允许用户指定对象访问的审计。有这种特权的用户也可以清空安全日志。默认情况下Administrators有该特权。 <br><br>SeSystemEnvironmentPrivilege <br>修改firmware环境变量。 <br>允许用户使用进程通过一个API来设置系统环境变量，另外，也可以让用户使用System Properties来做到以上这一步。默认情况下Administrators有该特权。 <br><br>SeProfileSingleProcessPrivilege <br>Profile单一进程。 <br>允许用户使用性能监视器来监视nonsystem进程。默认情况下Administrators有此特权。 <br><br><br>SeSystemProfilePrivilege <br>Profile系统性能。 <br>允许用户使用性能监视器来监视system进程。默认情况下Administrators有此特权。 <br><br>SeUndockPrivilege <br>将计算机中dock中删除。 <br>允许用户使用Eject PC从坞中将计算机移出，默认情况下Administrators, Power Users, Users均有此特 <br><br>权。 <br><br>SeAssignPrimaryTokenPrivilege <br>替换一个进程级令牌。 <br>允许一个父进程替换相关的子进程的访问令牌。 <br><br>SeRestorePrivilege <br>恢复文件和目录。 <br>允许用户绕过文件及目录权限来恢复备份文件。默认情况下Administrators和Backup Operators有此特权。 <br><br>SeShutdownPrivilege <br>关闭系统。 <br>允许用户关闭本地计算机。默认情况下Administrators, Backup Operators, Power Users, Users都有 <br><br>该特权，但是在2000 Server中Users没有此特权。 <br><br>SeSynchAgentPrivilege <br>同步目录服务数据。 <br>允许一个进程提供目录同步服务，这个特权只有在DC上。默认情况下域的Administrators和LocalSystem帐户 <br><br>有此特权。 <br><br>SeTakeOwnershipPrivilege <br>取得文件所有者身份。 <br>允许用户取得在系统中任何可得到的对象的所有者身份，包括：AD对象，文件，文件夹，打印机，注册表键，进 <br><br>程和线程。默认情况下Administrator有此特权。 <br><br>以上就是2000的用户特权了，是不是很多呢？呵呵！ <br><br>29. Sleep.exe （Batch File Wait） <br><br>Sleep可以让计算机等待一段指定的时间。这个东东对于使用Batch文件会非常有用，在某些情况下也可能会让AT命令的使用更加方便。 <br><br>Sleep time <br><br>time <br>要暂停的时间，秒为单位。 <br><br>sleep 20 <br>在运行下一个程序之前等待20s。 <br><br>假设我们搞这么一个登陆脚本到计算机上，我想是个不错的主意。 <br><br>@echo off <br><br>echo 2／23／2002 <br>echo. <br>echo 不要忘了明天女朋友生日哦！！呵呵！ <br><br>sleep 60 <br><br>30. Soon.exe （Near-Future Command Scheduler) <br><br>soon这个命令可以让一个程序在很短的时间里面启动，比如几秒钟之内。基本上soon就是一个AT的装配命令，可以简单的装配一组合适AT命令来远程或者是本地启动一个程序。当然soon使用起来要比AT简单的多，自然功能也要少一些了。当然soon可以让一个命令在小于一天的时间内重复启动，这一点还是很有用的。下面介绍一下 <br><br>如何使用。 <br><br>soon有两种命令，一是普通的操作命令，还有就是配置命令。 <br><br>1. Scheduling Command <br>soon \\computername delay /interactive "command" <br><br>\\computername <br>指定你要的计算机，远程使用时要net use。 <br><br>delay <br>指定从现在开始到启动程序的间隔，以秒为单位，默认情况下是本地5s,远程15s。 <br><br>/interactive <br>这个和AT命令里面的interactive是一样的。基本上可以看作是如果你打开一个cmd窗口，它会在桌面上跳出来。默认是off的。只有当Schedule服务以LocalSystem身份启动时才可以Interactive。 <br><br>command <br>你想要执行的命令，这里用双引号来维持命令解释时候的空格。 <br><br>2. Configuration Command <br><br>soon /d /l:n /r:n /i: on│off <br><br>/d <br>用这个开关来修改默认配置 <br><br>/l:n <br>指定LocalDelay的值。 <br><br>/r:n <br>指定RemoteDelay的值。 注意，这些值都是正整数，而且以秒为单位。 <br><br>/in│off <br>指定默认情况下是interactive还是uninteractive。 <br><br>如果你想每5分钟启动一次一个程序可以写一个这样的脚本。 <br><br>every5.cmd <br>soon 300 every5.cmd <br>xxxx.exe <br><br>对于远程启动时，使用AT命令不能成功的原因多数是因为AT命令需要指定绝对时间，如果不能搞得太清楚的话，我建议你们使用soon命令，在大部分情况下大家需要AT的功能，soon都可以完成，而且很快。基本上srv.exe 这样的后门都可以用soon来做。 另外不得不提一点，不知道是我的机器的关系还是这个程序本身的bug，很多情况下soon产生的schedule居然会是tomorrow，这个有点让人搞不懂。大家可以多试试。 <br><br>31. Srvany.exe （Applications as Services Utility） <br><br>Srvany一个可以让Windows应用程序像一个服务那样运行。也就是说在logoff的时候不关闭，也不用在logon <br>的时候重新启动。也就是就算是没有人登陆这个程序也可以运行，当然计算机得是开着的，呵呵！此外，如果这个应用程序不忽略WM_ENDSESSION或者CTRL_LOGOFF_EVENT时，程序也会因为logoff而退出。srvany是一个为32-bit应用程序设计的，但是用用16-bit也应该没有多大问题。如果你安装过srvany服务的话，在升级或安装Windows 2000, Windows NT Server, Windows NT Workstation, or applications 时要先disabled。实际上srvany本身就是一个服务，它先把自己启动起来，然后又在启动那些配置过的应用程序。就我个人开来这种方法很费力，比起instsrv和srvinstw来说都比较难以使用，最关键的是还要手工添加注册表。下面也就说一下怎么样让一个应用程序像一个服务那样运行。首先我们要安装Srvany，可以使用instsrv Myservice c:\path\srvany.exe或者srvinstw照提示做就可以了。这个是必要的，我们需要Srvany来启动程序。当然为了让程序像服务一样运行，我们需要编辑注册表信息，而且还要选择启动参数和工作目录。这里再次提醒手工操作注册标有危险性，注意备份。 <br><br>我们只要在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MyService\下 <br>添加一个Parameters子键，这里的MyService是刚刚指定的Srvany的服务名字。 <br>然后再这个subkey底下建立一个应用程序入口，使用REG_SZ类型。 <br>接着在指定程序的全路径，需要包括扩展名。比如Application: REG_SZ: C:\Tools\srv.exe <br><br>为了指定相应的启动参数我们还得继续编辑注册表。 <br>这个时候要在刚刚的Parameters底下接着创建一个AppParameters条目，<font id=fbertsmhha style="DISPLAY: none">+GtwCb\))X</font>也是REG_SZ类型。 <br>比如：AppParameters: REG_SZ: C:\tmp\example，当然因为srvany已经被安装为一个服务了，你也可以 <br>在Services面板里指定。C:\Tools\srv.exe C:\\temp\example。注意，这里使用\\来表示一个\。 <br>再来就是指定环境变量了。 <br>添加一个AppEnvironment，REG_MULTI_SZ类型。这个东西就是Services面板里面的Dependencies这一项。 可以根据需要指定，没有就不需要添加。 还要指定Working Directory。 <br>也是在Parameters里面建立一个AppDirectory使用REG_SZ。比如：AppDirectory: REG_SZ: C:\Tmp <br>当然也可以在Services里面 /D c:\\tmp D:\\Tools\\Vi.exe c:\\tmp\\example。 <br><br>一个srvany可以启动一个应用程序，如果你要用srvany而且是多个程序的话，只要采用不同的servicename启 动一个srvany就可以了。如果要删除可以使用instsrv myservice remove，也可以用sc。 <br>大家可以通过调整以下这几个注册表项来为提供相应的访问。 <br>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ <br>LanmanServer\Parameters\NullSessionShares <br><br>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ <br>LanmanServer\Parameters\NullSessionPipes <br><br>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ <br>LanmanServer\Parameters\RestrictNullSessAccess <br><br>32. Srinstw.exe (Service Installation Wizard) <br>这个安装服务的工具前面就已经提到过了，<span id=nfsqeauhal style="DISPLAY: none">+网育iRyw98K</span>而且又是GUI工具，使用起来都照着提示做就可以了，这里就不再多说什么，只是注意卸载服务的时候不要把系统重要的服务赶掉就好了。 <br><br>33. Svcacls.exe （service ACL Editor） <br><br>很可惜在这个版本的Reskit里面缺少了这个程序，非常遗憾不能对它进行测试。 <br><br>Svcacls是一个可以用来设置服务对象的ACL的命令行工具，基本上就是为了方便administrator进行委派控制而设立的。使用这个工具我们需要administrator特权，当然这个东西也是可以进行委派的。这里就提醒一点，不要删除任何服务的adminstrators和system许可，不然的话，你可能就要从装系统来恢复控制了。 <br><br>svcacls \\targetcomputer\Service Option <br><br>\\targetcomputer\Service <br>这个不用说了。 <br><br>Option有以下几种 <br><br>G(grant): trustee: Permissions 增加许可。 <br>S(Set): trustee: Permissions 重置许可。 <br>R(Revoke): trustee: 删除被赋予的explicit permissions。 <br>D(deny): trustee: 拒绝访问。注意，<span id=ogjrvlkccnkilvpw style="DISPLAY: none">v垠ui6TONl网v3Kp8&amp;</span>使用这个命令的时候要很小心，一旦你用了D:everyone，连 <br><br>administrators都没有权限来访问这个服务了。 <br><br>你可以在一行里面使用这些命令。比如： r:username g:uername:riu。 <br><br>trustee <br>你要指定的用户。 <br><br>permissions <br>相应的权限。 <br><br>有Specific许可和generic许可两种。 <br><br>Specific permissions: <br><br>Q: Query Service Configuration (SERVICE_QUERY_CONFIG) <br>S: Query Service Status (SERVICE_QUERY_STATUS) <br>E: Enumerate Dependent Services (SERVICE_ENUMERATE_DEPENDENTS) <br>C: Change Service Configuration (SERVICE_CHANGE_CONFIG) <br>T: Start Service (SERVICE_START) <br>O: Stop Service (SERVICE_STOP) <br>P: Pause/Continue Service (SERVICE_PAUSE_CONTINUE) <br>I: Interrogate Service with ControlService() (SERVICE_INTERROGATE) <br>U: Allow User-Defined Control Commands (SERVICE_USER_DEFINED_CONTROL) <br><br>Generic permissions: <br><br>F: Full Control (SERVICE_ALL_ACCESS = QSECTOPIU) <br>R: Generic Read (GENERIC_READ = QSE) <br>W: Generic Write (GENERIC_WRITE = C) <br>X: Generic Execute (GENERIC_EXECUTE = TOPIU) <br><br>这个工具为我们提供了一种安全性的选择，<font id=udcwkeejthposr style="DISPLAY: none">b,XI*CWW的MhQyX</font>禁止对某些服务的访问，可以避免一些攻击的发生。</p>
<br><span id=ctl00_ContentPlaceHolder1_ArticleContent>&nbsp;
<p>34. Svcmon.exe (service Monitoring Tool) <br><br>这个工具可以用来监视本地或者是远程计算机服务的状态改变，当它发现一个服务开始或者是停止的时候，这个工具将会通过发e-mail或者是Exchange Server来通知你知道。 <br>这个工具由两部分组成，Svcmon.exe这个需要你手工拷贝到%SystemRoot%\System32底下，另一个Smconfig <br>是一个安装向导。由于是图形界面，所以我就不再多说什么东西。这里提醒一下注意，在Exchange <br><br>Recipients那里添如你要提醒的用户的Email。其他的按照指示做就可以了。 <br><br>35. Timethis.exe (Time This) <br><br>我很喜欢这个工具，这个工具可以用来报告一个程序的运行时间。报告的时间可以详细到0.001s。连net sue都可以拿来测试。 当然使用起来也很简单。 <br><br>timethis commandname <br><br>当你在命令中有&lt;, &gt;, &gt;&gt;，｜这样的符号的时候，请使用双引号，比如：timethis "dir /a &gt; a.txt" <br><br>使用以后基本上会出来这么一个东西。 <br><br>C:\&gt;timethis dir <br><br>TimeThis : Command Line : dir <br>TimeThis : Start Time : Sun Mar 03 17:45:27 2002 <br><br>Volume in drive C is WIN2000 <br>Volume Serial Number is 4CE5-8543 <br><br>Directory of C:\ <br><br>2002-02-23 22:58 <br>Documents and Settings <br>2002-02-23 22:49 <br>Inetpub <br>2002-03-03 11:37 <br>Program Files <br>2002-03-03 14:15 <br>WINNT <br>0 File(s) 0 bytes <br>4 Dir(s) 9,512,271,872 bytes free <br><br>TimeThis : Command Line : dir <br>TimeThis : Start Time : Sun Mar 03 17:45:27 2002 <br>TimeThis : End Time : Sun Mar 03 17:45:27 2002 <br>TimeThis : Elapsed Time : 00:00:00.050 <br><br>36. Timezone.exe (Daylight Saving Time Update Utility) <br><br>微软真是奇怪，居然在ResKit里面加入了这样的东西，一个调整夏令时的工具。我们现在又不用夏令时，没有多大用处。随便说说！ <br><br>timezone /g /s startdate enddate /？ <br><br>/g <br>当前的情况。 <br><br>/s startdate enddate <br>进行修改，格式是HourayofWeekay:Month <br><br>Hour: 00-23 <br>DayofWeek: 0=Sunday 1=monday&#8230;&#8230;&#8230; <br>Day指定发生的时间，如果上面是sunday，这里是1的话，就是该月的第一个星期天。 <br>Month: 1 = January 到12 = December。 <br><br>37. Tlocmgr.exe (Telephony Location Manager) <br><br>一个用来管理TAPI的小程序，允许你方便的改变你的TAPI Location。一旦运行以后会在右下角有一个tray，这个实在没有什么好说的。 <br><br>38. Tracedmp.exe (Trace Dump) <br><br>这个工具也是用来处理Tracelog产生的日志的，和前面的Reducer.exe有相象之处。Tracelog产生的是不容易阅读的，我们可以使用Tracedmp把它转换比较容易读的格式，比如Summary.txt这种内容少一点, 也可以是CSV可以比较详细。还有tracedmp可以直接从Realtime buffer中直接读出数据来处理。tracedmp解释tracelog产生的日志是通过一个叫mofdata.guid的文件，这文件里面包含了系统的目录服务和系统追踪信息。 <br><br>如果要处理 其他的数据，必须把相应的信息加入到mofdata.guid中去。 <br><br>tracedmp option -h│-? <br><br>-o 20051017183000.htm <br>输出CSV和summary文件，默认情况下是dumpfile.csv和summary.txt。这里的dumpfile.csv包括每一个 <br>event的详细的信息。具体产生的日志的内容代表的信息我在这里就不详细说了，
<p id=jqbobkneocqbgto>1#管无M:jP提2q's00</p>
大家可以参看相应的资料。 <br><br>-guid <br>MOF定义文件，默认的GUID是mofdata.guid，你也可以进行指定。 <br><br>-rt <br>产生一个real-time trace文件。如果要指定这个选项，tracelog必须是在工作的。 <br><br>-summary <br>只提供summary.txt文件。一般像这样用就可以了，<font id=rbsflfqbiojpni>软0g供G4W~$0CTY9</font>Tracedmp c:\logfile.etl。你当然也可以tracedmp -rt ds，来显示realtime记录。 <br><br>39. Traceenable.exe (Trace Enable) <br><br>这是一个用来enable/disable RAS/RADIUS记录的GUI工具。这个工具允许我们修改 <br><br>HKLM\SOFTWARE\Microsoft\Tracing key下的 <br>EnableConsoleTracing <br>EnableFileTracing <br>MaxFileSize <br>三个subkeys。如果你熟悉注册表的话，也可以直接修改。图形界面的工具就不多说了。提一下面板中的几个选项。 <br><br>Global <br>enable console tracing <br>允许你实时看目录。 <br><br>enable pool tagging <br>打开system pool标记。 <br><br>Per Component <br>enable console tracing <br>在控制台窗口中显示日志跟踪信息。 <br><br>enable file tracing <br>把信息保存到一个日志文件中。一般在%windir%\tracing。 <br><br>max file size <br>文件的最大尺寸。 <br><br>如果你是在一台才安装的计算机中启用这个东东的话，有些键值会不在注册表中，那就是说在Trace Enable中也看不到，你需要先运行RasPhone。 <br><br>40. Tracelog.exe (Trace Log) <br><br>说了半天终于说到这个tracelog.exe了。这个命令行工具可以开始，停止，启用跟踪记录，这些记录可以用Tracedmp或者是Reducer来看。tracelog在运行时将建立一个buffer，然后如果有数据进入buffer它可以把这些数据转换为文件保存，也可以进行实时的跟踪，这个时候我们可以用如tracedmp这样的应用程序读出buffer中的数据。 <br><br>tracelog managementoption bufferoption logfileoption Systemleveltracingoption <br><br>Provider-specificOption -h│-? <br><br>managementOption: Starting, stopping, updating and querying <br><br>-guid file <br>和tracedmp中的是一样的，都是表示provider的信息。如果开始System tracing可以不用提供Guid，如果是directory service events我们可以指定control.guid。 <br><br>-start logger_name <br>开始一个trace会话。你要提供一个日志名字，如果是一个system trace可以不需要指定，默认的名字为&#8220;NT <br>Kernel logger&#8221;。 <br><br>-stop logger_name <br>终止trace会话。如果是system trace可以不用指定logger_name。 <br><br>-update option logger_name <br>升级当前的trace会话。这个东西在你想改变文件的名字，buffer的参数，realtime模式等时候就会被用到。 <br>以下这些option可以在kernel logger中用到。 <br><br>-rt <br>模式开关，调整realtime mode。 <br><br>-f logfile_name <br>指定新的log文件的名字。 <br><br>-ft n <br>改变buffer的刷新计数器。 <br><br>-max n <br>改变buffer的大小。 <br><br>"-nodisk" "-noprocess" "-nothread" "-nonet" "-fio" "-pf" "-hf" "-img" "-cm" <br>NT kernel logger的一些标志。 <br><br>上面这些调整可以一次进行，如：tracelog -update -rt -max 40。 <br><br>-x <br>停止所有活动的会话。 <br><br>-l <br>查询在工作的traces。 <br><br>-q <br>只查询system trace。 <br><br>BufferOption <br><br>-b n <br>设置buffer的大小为n kb。小的buffer会导致经常刷新buffer，
<p id=vomcswvptbpg>N%网Q_:j专软国XE</p>
基本上使用默认就好。 <br><br>-min n <br>设置最小buffer,默认是2。 <br><br>-max n <br>设置最大buffer，默认是25。 <br><br>-ft n_seconds <br>设置刷新时间。 <br><br>-age n_minutes <br>修改老化时间。就是分配的buffer没有使用，
<p id=cfjokqnbja>?件_F件}Rw_c</p>
会在多长时间内被释放。 <br><br>LogfileOption <br><br>-rt b <br>启用real time mode。 <br><br>-f name <br>日志的名字。默认是c:\logfile.etl，如果要使用不同的名字用-o 20051017183000.htm。 <br><br>-seq n_mbyte <br>一直使用到n_mbyte。 <br><br>-cir n_mbyte <br>循环使用n_mbyte。 <br><br><br>Systemleveltracingoption <br>为kernel tracing提供更多的选项。 <br>默认情况下kernel tracing包括 <br>Process start/end <br>Disk I/O <br>Network TCP/IP, UDP/IP <br>Thread start/end <br><br>只有在使用一些开关后，<font id=koitjpocfjnnpicmi>I2R$软+(网u.I}供@=`R</font>才会产生下面的内容。 <br>Image Load <br>Registry calls <br>File I/O <br>Page Fault <br>但是trace这些东西会产生很大的负担。 <br><br>-fio <br>启用file I/O tracing。 <br><br>-pf <br>启用page faults tracing。 <br><br>-hf <br>启用hard faults tracing。 <br><br>-img <br>启用image load tracing。 <br><br>-um <br>启用Process Private tracing。这种情况下buffer建立在Private Process space中，默认是在kernel <br><br>space中。 <br><br>-nf <br>每n mb更新文件。 <br><br>ProviderSpecificOption: Provider Level Options <br><br>-level n <br><br>-flags <br><br>这些都和相应的provider有关。 <br><br>下面看个例子， <br><br>打开trace。 <br>c:\&gt;tracelog start <br>Logger Started... <br>Operation Status: 0L <br>The operation completed successfully. <br><br>Logger Name: NT Kernel Logger <br>Logger Id: ffff <br>Logger Thread Id: 1360 <br>Buffer Size: 8 Kb <br>Maximum Buffers: 25 <br>Minimum Buffers: 2 <br>Number of Buffers: 2 <br>Free Buffers: 1 <br>Buffers Written: 3 <br>Events Lost: 0 <br>Log Buffers Lost: 0 <br>Real Time Buffers Lost: 0 <br>Log File Mode: Sequential <br>Enabled tracing: Process Thread Disk TcpIp <br>Log Filename: C:\LogFile.Etl <br><br>停止trace <br>C:\&gt;tracelog </span></span></span>
<img src ="http://www.blogjava.net/zhangheng/aggbug/138405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-08-21 16:54 <a href="http://www.blogjava.net/zhangheng/archive/2007/08/21/138405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>验证码无法显示</title><link>http://www.blogjava.net/zhangheng/archive/2007/08/21/138402.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Tue, 21 Aug 2007 08:48:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/08/21/138402.html</guid><description><![CDATA[<p>方法一：<br>验证码无法显示的原因是XBM被屏蔽，只需要修复注册表相关项即可：<br><br>打开记事本，把这一段粘贴进去：<br>REGEDIT4<br><br>[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet&nbsp;Explorer\Security]<br>"BlockXBM"=dword:00000000<br><br>另存为&nbsp;&nbsp;a.reg即可，运行后，重新打开浏览器。</p>
<br>方法二：<br>今天上网,居然就发现图形验证码无法显示了.郁闷了老半天,找了这个办法<br>1、运行注册表编辑器<br>2、依次点开HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Internet&amp;nbsp;Explorer\\Security；<br>3、在屏幕右边空白处点击鼠标右键，选择新建一个名为;&#8220;BlockXBM&#8221;为的;DWORD&amp;nbsp;键，其值为默认的0<br>4、退出注册表编辑器。
<img src ="http://www.blogjava.net/zhangheng/aggbug/138402.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-08-21 16:48 <a href="http://www.blogjava.net/zhangheng/archive/2007/08/21/138402.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用web.xml控制Web应用的行为</title><link>http://www.blogjava.net/zhangheng/archive/2007/06/18/124925.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Mon, 18 Jun 2007 05:38:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/06/18/124925.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/124925.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/06/18/124925.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/124925.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/124925.html</trackback:ping><description><![CDATA[<div class=postText>
<div class=postTitle><strong>1 定义头和根元素</strong><br><br>部署描述符文件就像所有XML文件一样，必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。<br>DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的servlet规范的版本（如2.2或2.3）并指定管理此文件其余部分内容的语法的DTD(Document Type Definition，文档类型定义)。<br>所有部署描述符文件的顶层（根）元素为web-app。请注意，XML元素不像HTML，他们是大小写敏感的。因此，web-App和WEB-APP都是不合法的，web-app必须用小写。<br><br><strong><font color=#000000>2 部署描述符文件内的元素次序</font></strong><br><br>XML 元素不仅是大小写敏感的，而且它们还对出现在其他元素中的次序敏感。例如，XML头必须是文件中的第一项，DOCTYPE声明必须是第二项，而web- app元素必须是第三项。在web-app元素内，元素的次序也很重要。服务器不一定强制要求这种次序，但它们允许（实际上有些服务器就是这样做的）完全拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。<br>下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如，此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意，所有这些元素都是可选的。因此，可以省略掉某一元素，但不能把它放于不正确的位置。<br>l icon icon元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。<br>l display-name display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。<br>l description description元素给出与此有关的说明性文本。<br>l context-param context-param元素声明应用范围内的初始化参数。<br>l filter 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。<br>l filter-mapping 一旦命名了一个过滤器，就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。<br>l listener servlet API的版本2.3增加了对事件监听程序的支持，事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。<br>l servlet 在向servlet或JSP页面制定初始化参数或定制URL时，必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。<br>l servlet-mapping 服务器一般为servlet提供一个缺省的URL：http://host/webAppPrefix/servlet/ServletName。但是，常常会更改这个URL，以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时，使用servlet-mapping元素。<br>l session-config 如果某个会话在一定时间内未被访问，服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对象的超时值，或者可利用session-config元素制定缺省超时值。<br>l mime-mapping 如果Web应用具有想到特殊的文件，希望能保证给他们分配特定的MIME类型，则mime-mapping元素提供这种保证。<br>l welcom-file-list welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时，使用哪个文件。<br>l error-page error-page元素使得在返回特定HTTP状态代码时，或者特定类型的异常被抛出时，能够制定将要显示的页面。<br>l taglib taglib元素对标记库描述符文件（Tag Libraryu Descriptor file）指定别名。此功能使你能够更改TLD文件的位置，而不用编辑使用这些文件的JSP页面。<br>l resource-env-ref resource-env-ref元素声明与资源相关的一个管理对象。<br>l resource-ref resource-ref元素声明一个资源工厂使用的外部资源。<br>l security-constraint security-constraint元素制定应该保护的URL。它与login-config元素联合使用<br>l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。<br>l security-role security-role元素给出安全角色的一个列表，这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。<br>l env-entry env-entry元素声明Web应用的环境项。<br>l ejb-ref ejb-ref元素声明一个EJB的主目录的引用。<br>l ejb-local-ref ejb-local-ref元素声明一个EJB的本地主目录的应用。<br><br><strong>3 分配名称和定制的UL</strong><br><br>在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称，使用servlet-mapping元素将定制的URL与刚分配的名称相关联。<br>3.1 分配名称<br>为了提供初始化参数，对servlet或JSP页面定义一个定制URL或分配一个安全角色，必须首先给servlet或JSP页面一个名称。可通过 servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素（在web-app元素内），如下所示：<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;<br>&lt;/servlet&gt; </div>
<div class=postText>
<p>这表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给 servlet一个名称具有两个主要的含义。首先，初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在 URL而不是类名中使用此名称。因此，利用刚才给出的定义，URL http://host/webAppPrefix/servlet/Test 可用于 http://host/webAppPrefix/servlet/moreservlets.TestServlet 的场所。<br>请记住：XML元素不仅是大小写敏感的，而且定义它们的次序也很重要。例如，web-app元素内所有servlet元素必须位于所有servlet- mapping元素（下一小节介绍）之前，而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素（如果有的话）之前。类似地，servlet 的servlet-name子元素也必须出现在servlet-class之前。5.2节"部署描述符文件内的元素次序"将详细介绍这种必需的次序。<br>例如，程序清单5-1给出了一个名为TestServlet的简单servlet，它驻留在moreservlets程序包中。因为此servlet是扎根在一个名为deployDemo的目录中的Web应用的组成部分，所以TestServlet.class放在deployDemo/WEB- INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图 5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。</p>
<p>程序清单5-1 TestServlet.java<br>package moreservlets;</p>
<p>import java.io.*;<br>import javax.servlet.*;<br>import javax.servlet.http.*;</p>
<p>/** Simple servlet used to illustrate servlet naming<br>* and custom URLs.<br>* &lt;P&gt;<br>* Taken from More Servlets and JavaServer Pages<br>* from Prentice Hall and Sun Microsystems Press,<br>* http://www.moreservlets.com/.<br>* &amp;copy; 2002 Marty Hall; may be freely used or adapted.<br>*/</p>
<p>public class TestServlet extends HttpServlet {<br>public void doGet(HttpServletRequest request,<br>HttpServletResponse response)<br>throws ServletException, IOException {<br>response.setContentType("text/html");<br>PrintWriter out = response.getWriter();<br>String uri = request.getRequestURI();<br>out.println(ServletUtilities.headWithTitle("Test Servlet") +<br>"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +<br>"&lt;H2&gt;URI: " + uri + "&lt;/H2&gt;\n" +<br>"&lt;/BODY&gt;&lt;/HTML&gt;");<br>}<br>}</p>
<p><br>程序清单5-2 web.xml（说明servlet名称的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;!-- &#8230; --&gt;<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;<br>&lt;/servlet&gt;<br>&lt;!-- &#8230; --&gt;<br>&lt;/web-app&gt;</p>
<p>3.2 定义定制的URL<br>大多数服务器具有一个缺省的serlvet URL：<br>http://host/webAppPrefix/servlet/packageName.ServletName。虽然在开发中使用这个URL很方便，但是我们常常会希望另一个URL用于部署。例如，可能会需要一个出现在Web应用顶层的URL（如，http: //host/webAppPrefix/Anyname），并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外，对许多开发人员来说，顶层URL看上去比更长更麻烦的缺省URL更简短。<br>事实上，有时需要使用定制的URL。比如，你可能想关闭缺省URL映射，以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL，那么你怎样访问servlet呢？这时只有使用定制的URL了。<br>为了分配一个定制的URL，可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet- name元素提供了一个任意名称，可利用此名称引用相应的servlet；url-pattern描述了相对于Web应用的根目录的URL。url- pattern元素的值必须以斜杠（/）起始。<br>下面给出一个简单的web.xml摘录，它允许使用URL http://host/webAppPrefix/UrlTest而不是http://host/webAppPrefix/servlet/Test或<br>http: //host/webAppPrefix/servlet/moreservlets.TestServlet。请注意，仍然需要XML头、 DOCTYPE声明以及web-app封闭元素。此外，可回忆一下，XML元素出现地次序不是随意的。特别是，需要把所有servlet元素放在所有 servlet-mapping元素之前。<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;<br>&lt;/servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet-mapping&gt;<br>&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br>&lt;url-pattern&gt;/UrlTest&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;<br>URL模式还可以包含通配符。例如，下面的小程序指示服务器发送所有以Web应用的URL前缀开始，以..asp结束的请求到名为BashMS的servlet。<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;msUtils.ASPTranslator&lt;/servlet-class&gt;<br>&lt;/servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet-mapping&gt;<br>&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt;<br>&lt;url-pattern&gt;/*.asp&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;<br>3.3 命名JSP页面<br>因为JSP页面要转换成sevlet，自然希望就像命名servlet一样命名JSP页面。毕竟，JSP页面可能会从初始化参数、安全设置或定制的URL中受益，正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的，但存在一个关键的猜疑：即，你不知道JSP页面的实际类名（因为系统自己挑选这个名字）。因此，为了命名JSP页面，可将jsp-file元素替换为servlet-calss元素，如下所示：<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br>&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt;<br>&lt;/servlet&gt;<br>命名JSP页面的原因与命名servlet的原因完全相同：即为了提供一个与定制设置（如，初始化参数和安全设置）一起使用的名称，并且，以便能更改激活 JSP页面的URL（比方说，以便多个URL通过相同页面得以处理，或者从URL中去掉.jsp扩展名）。但是，在设置初始化参数时，应该注意，JSP页面是利用jspInit方法，而不是init方法读取初始化参数的。<br>例如，程序清单5-3给出一个名为TestPage.jsp的简单JSP页面，它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配一个注册名PageName，然后将此注册名与http://host/webAppPrefix/UrlTest2/anything 形式的URL相关联的web.xml文件（即，deployDemo/WEB-INF/web.xml）的一部分。</p>
<p>程序清单5-3 TestPage.jsp<br>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;<br>&lt;HTML&gt;<br>&lt;HEAD&gt;<br>&lt;TITLE&gt;<br>JSP Test Page<br>&lt;/TITLE&gt;<br>&lt;/HEAD&gt;<br>&lt;BODY BGCOLOR="#FDF5E6"&gt;<br>&lt;H2&gt;URI: &lt;%= request.getRequestURI() %&gt;&lt;/H2&gt;<br>&lt;/BODY&gt;<br>&lt;/HTML&gt;</p>
<p><br>程序清单5-4 web.xml（说明JSP页命名的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br>&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt;<br>&lt;/servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet-mapping&gt;<br>&lt;servlet-name&gt; PageName &lt;/servlet-name&gt;<br>&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p><br><strong>4 禁止激活器servlet</strong><br><br>对servlet或JSP页面建立定制URL的一个原因是，这样做可以注册从 init（servlet）或jspInit（JSP页面）方法中读取得初始化参数。但是，初始化参数只在是利用定制URL模式或注册名访问 servlet或JSP页面时可以使用，用缺省URL http://host/webAppPrefix/servlet/ServletName 访问时不能使用。因此，你可能会希望关闭缺省URL，这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet，因为多数服务器具有一个用缺省的servlet URL注册的标准servlet，并激活缺省的URL应用的实际servlet。<br>有两种禁止此缺省URL的主要方法：<br>l 在每个Web应用中重新映射/servlet/模式。<br>l 全局关闭激活器servlet。<br>重要的是应该注意到，虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多，但重新映射可以用一种完全可移植的方式来完成。相反，全局禁止激活器servlet完全是针对具体机器的，事实上有的服务器（如ServletExec）没有这样的选择。下面的讨论对每个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。<br>4.1 重新映射/servlet/URL模式<br>在一个特定的Web应用中禁止以http://host/webAppPrefix/servlet/ 开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet，并使用前一节讨论的url-pattern元素将所有匹配请求转向该 servlet。只要简单地使用：<br>&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;<br>作为servlet-mapping元素中的模式即可。<br>例如，程序清单5-5给出了将SorryServlet servlet（程序清单5-6）与所有以http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。</p>
<p>程序清单5-5 web.xml（说明JSP页命名的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;Sorry&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;moreservlets.SorryServlet&lt;/servlet-class&gt;<br>&lt;/servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet-mapping&gt;<br>&lt;servlet-name&gt; Sorry &lt;/servlet-name&gt;<br>&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p><br>程序清单5-6 SorryServlet.java<br>package moreservlets;</p>
<p>import java.io.*;<br>import javax.servlet.*;<br>import javax.servlet.http.*;</p>
<p>/** Simple servlet used to give error messages to<br>* users who try to access default servlet URLs<br>* (i.e., http://host/webAppPrefix/servlet/ServletName)<br>* in Web applications that have disabled this<br>* behavior.<br>* &lt;P&gt;<br>* Taken from More Servlets and JavaServer Pages<br>* from Prentice Hall and Sun Microsystems Press,<br>* http://www.moreservlets.com/.<br>* &amp;copy; 2002 Marty Hall; may be freely used or adapted.<br>*/</p>
<p>public class SorryServlet extends HttpServlet {<br>public void doGet(HttpServletRequest request,<br>HttpServletResponse response)<br>throws ServletException, IOException {<br>response.setContentType("text/html");<br>PrintWriter out = response.getWriter();<br>String title = "Invoker Servlet Disabled.";<br>out.println(ServletUtilities.headWithTitle(title) +<br>"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +<br>"&lt;H2&gt;" + title + "&lt;/H2&gt;\n" +<br>"Sorry, access to servlets by means of\n" +<br>"URLs that begin with\n" +<br>"http://host/webAppPrefix/servlet/\n" +<br>"has been disabled.\n" + <br>"&lt;/BODY&gt;&lt;/HTML&gt;");<br>}</p>
<p>public void doPost(HttpServletRequest request,<br>HttpServletResponse response)<br>throws ServletException, IOException {<br>doGet(request, response);<br>}<br>}</p>
<p><br>4.2 全局禁止激活器：Tomcat<br>Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法：<br>1．禁止激活器： Tomcat 4<br>Tomcat 4用与前面相同的方法关闭激活器servlet，即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在 install_dir/conf中的一个服务器专用的全局web.xml文件，而前面使用的是存放在每个Web应用的WEB-INF目录中的标准 web.xml文件。<br>因此，为了在Tomcat 4中关闭激活器servlet，只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可，如下所示：<br>&lt;!-- <br>&lt;servlet-mapping&gt;<br>&lt;servlet-name&gt;invoker&lt;/servlet-name&gt;<br>&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;<br>--&gt;<br>再次提醒，应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的，此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。<br>2．禁止激活器：Tomcat3<br>在Apache Tomcat的版本3中，通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省 servlet URL。例如，下面是禁止使用缺省servlet URL的server.xml文件的一部分。<br>&lt;!-- <br>&lt;RequsetInterceptor <br>className="org.apache.tomcat.request.InvokerInterceptor"<br>debug="0" prefix="/servlet/" /&gt;<br>--&gt;</p>
<p><strong>5 初始化和预装载servlet与JSP页面</strong><br><br>这里讨论控制servlet和JSP页面的启动行为的方法。特别是，说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。<br>5.1 分配servlet初始化参数<br>利用init-param元素向servlet提供初始化参数，init-param元素具有param-name和param-value子元素。例如，在下面的例子中，如果initServlet servlet是利用它的注册名（InitTest）访问的，它将能够从其方法中调用getServletConfig(). getInitParameter("param1")获得"Value 1"，调用getServletConfig().getInitParameter("param2")获得"2"。<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;moreservlets.InitServlet&lt;/servlet-class&gt;<br>&lt;init-param&gt;<br>&lt;param-name&gt;param1&lt;/param-name&gt;<br>&lt;param-value&gt;value1&lt;/param-value&gt;<br>&lt;/init-param&gt;<br>&lt;init-param&gt;<br>&lt;param-name&gt;param2&lt;/param-name&gt;<br>&lt;param-value&gt;2&lt;/param-value&gt;<br>&lt;/init-param&gt;<br>&lt;/servlet&gt;<br>在涉及初始化参数时，有几点需要注意：<br>l 返回值。GetInitParameter的返回值总是一个String。因此，在前一个例子中，可对param2使用Integer.parseInt获得一个int。<br>l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。<br>l 缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此，在这个例子中，param1和 param2初始化参数将能够在使用URL http://host/webAppPrefix/servlet/InitTest 时可用，但在使用URL http://host/webAppPrefix/servlet/myPackage.InitServlet 时不能使用。<br>例如，程序清单5-7给出一个名为InitServlet的简单servlet，它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。<br>程序清单5-7 InitServlet.java<br>package moreservlets;</p>
<p>import java.io.*;<br>import javax.servlet.*;<br>import javax.servlet.http.*;</p>
<p>/** Simple servlet used to illustrate servlet<br>* initialization parameters.<br>* &lt;P&gt;<br>* Taken from More Servlets and JavaServer Pages<br>* from Prentice Hall and Sun Microsystems Press,<br>* http://www.moreservlets.com/.<br>* &amp;copy; 2002 Marty Hall; may be freely used or adapted.<br>*/</p>
<p>public class InitServlet extends HttpServlet {<br>private String firstName, emailAddress;</p>
<p>public void init() {<br>ServletConfig config = getServletConfig();<br>firstName = config.getInitParameter("firstName");<br>emailAddress = config.getInitParameter("emailAddress");<br>}<br><br>public void doGet(HttpServletRequest request,<br>HttpServletResponse response)<br>throws ServletException, IOException {<br>response.setContentType("text/html");<br>PrintWriter out = response.getWriter();<br>String uri = request.getRequestURI();<br>out.println(ServletUtilities.headWithTitle("Init Servlet") +<br>"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +<br>"&lt;H2&gt;Init Parameters:&lt;/H2&gt;\n" +<br>"&lt;UL&gt;\n" +<br>"&lt;LI&gt;First name: " + firstName + "\n" +<br>"&lt;LI&gt;Email address: " + emailAddress + "\n" +<br>"&lt;/UL&gt;\n" + <br>"&lt;/BODY&gt;&lt;/HTML&gt;");<br>}<br>}</p>
<p><br>程序清单5-8 web.xml（说明初始化参数的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;moreservlets.InitServlet&lt;/servlet-class&gt;<br>&lt;init-param&gt;<br>&lt;param-name&gt;firstName&lt;/param-name&gt;<br>&lt;param-value&gt;Larry&lt;/param-value&gt;<br>&lt;/init-param&gt;<br>&lt;init-param&gt;<br>&lt;param-name&gt;emailAddress&lt;/param-name&gt;<br>&lt;param-value&gt;Ellison@Microsoft.com&lt;/param-value&gt;<br>&lt;/init-param&gt;<br>&lt;/servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p>5.2 分配JSP初始化参数<br>给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。<br>1）使用jsp-file而不是servlet-class。因此，WEB-INF/web.xml文件的servlet元素如下所示：<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br>&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt;<br>&lt;init-param&gt;<br>&lt;param-name&gt;...&lt;/param-name&gt;<br>&lt;param-value&gt;...&lt;/param-value&gt;<br>&lt;/init-param&gt;<br>...<br>&lt;/servlet&gt;<br>2) 几乎总是分配一个明确的URL模式。对servlet，一般相应地使用以http://host/webAppPrefix/servlet/ 开始的缺省URL。只需记住，使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如，在上面给出的例子中，可用URL http://host/webAppPrefix/servlet/PageName 访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时，许多用户似乎不喜欢应用常规的servlet的URL。此外，如果 JSP页面位于服务器为其提供了目录清单的目录中（如，一个既没有index.html也没有index.jsp文件的目录），则用户可能会连接到此 JSP页面，单击它，从而意外地激活未初始化的页面。因此，好的办法是使用url-pattern（5.3节）将JSP页面的原URL与注册的 servlet名相关联。这样，客户机可使用JSP页面的普通名称，但仍然激活定制的版本。例如，给定来自项目1的servlet定义，可使用下面的 servlet-mapping定义：<br>&lt;servlet-mapping&gt;<br>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br>&lt;url-pattern&gt;/RealPage.jsp&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;<br>3）JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此，使用JSP声明提供一个init方法是不合法的，必须制定jspInit方法。<br>为了说明初始化JSP页面的过程，程序清单5-9给出了一个名为InitPage.jsp的JSP页面，它包含一个jspInit方法且放置于 deployDemo Web应用层次结构的顶层。一般，http://host/deployDemo/InitPage.jsp 形式的URL将激活此页面的不具有初始化参数访问权的版本，从而将对firstName和emailAddress变量显示null。但是， web.xml文件（程序清单5-10）分配了一个注册名，然后将该注册名与URL模式/InitPage.jsp相关联。</p>
<p>程序清单5-9 InitPage.jsp<br>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;<br>&lt;HTML&gt;<br>&lt;HEAD&gt;&lt;TITLE&gt;JSP Init Test&lt;/TITLE&gt;&lt;/HEAD&gt;<br>&lt;BODY BGCOLOR="#FDF5E6"&gt;<br>&lt;H2&gt;Init Parameters:&lt;/H2&gt;<br>&lt;UL&gt;<br>&lt;LI&gt;First name: &lt;%= firstName %&gt;<br>&lt;LI&gt;Email address: &lt;%= emailAddress %&gt;<br>&lt;/UL&gt;<br>&lt;/BODY&gt;&lt;/HTML&gt;<br>&lt;%!<br>private String firstName, emailAddress;</p>
<p>public void jspInit() {<br>ServletConfig config = getServletConfig();<br>firstName = config.getInitParameter("firstName");<br>emailAddress = config.getInitParameter("emailAddress");<br>}<br>%&gt;</p>
<p><br>程序清单5-10 web.xml（说明JSP页面的init参数的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;InitPage&lt;/servlet-name&gt;<br>&lt;jsp-file&gt;/InitPage.jsp&lt;/jsp-file&gt;<br>&lt;init-param&gt;<br>&lt;param-name&gt;firstName&lt;/param-name&gt;<br>&lt;param-value&gt;Bill&lt;/param-value&gt;<br>&lt;/init-param&gt;<br>&lt;init-param&gt;<br>&lt;param-name&gt;emailAddress&lt;/param-name&gt;<br>&lt;param-value&gt;gates@oracle.com&lt;/param-value&gt;<br>&lt;/init-param&gt;<br>&lt;/servlet&gt;<br>&lt;!-- ... --&gt; <br>&lt;servlet-mapping&gt;<br>&lt;servlet-name&gt; InitPage&lt;/servlet-name&gt;<br>&lt;url-pattern&gt;/InitPage.jsp&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p><br>5.3 提供应用范围内的初始化参数<br>一般，对单个地servlet或JSP页面分配初始化参数。指定的servlet或JSP页面利用ServletConfig的getInitParameter方法读取这些参数。但是，在某些情形下，希望提供可由任意servlet或JSP页面借助ServletContext的getInitParameter方法读取的系统范围内的初始化参数。<br>可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素，如下所示：<br>&lt;context-param&gt;<br>&lt;param-name&gt;support-email&lt;/param-name&gt;<br>&lt;param-value&gt;blackhole@mycompany.com&lt;/param-value&gt;<br>&lt;/context-param&gt;<br>可回忆一下，为了保证可移植性，web.xml内的元素必须以正确的次序声明。但这里应该注意，context-param元素必须出现任意与文档有关的元素（icon、display-name或description）之后及filter、filter-mapping、listener或 servlet元素之前。<br>5.4 在服务器启动时装载servlet<br>假如servlet或JSP页面有一个要花很长时间执行的init （servlet）或jspInit（JSP）方法。例如，假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种情况下，在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此，可利用servlet的load-on- startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。<br>&lt;servlet&gt;<br>&lt;servlet-name&gt; &#8230; &lt;/servlet-name&gt;<br>&lt;servlet-class&gt; &#8230; &lt;/servlet-class&gt; &lt;!-- Or jsp-file --&gt;<br>&lt;load-on-startup/&gt;<br>&lt;/servlet&gt;<br>可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前装载较少数目的servlet或JSP页面。例如，下面的servlet项（放置在Web应用的WEB-INF目录下的web.xml文件中的web-app元素内）将指示服务器首先装载和初始化SearchServlet，然后装载和初始化由位于Web应用的result目录中的index.jsp文件产生的 servlet。<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;Search&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;myPackage.SearchServlet&lt;/servlet-class&gt; &lt;!-- Or jsp-file --&gt;<br>&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;<br>&lt;/servlet&gt;<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;Results&lt;/servlet-name&gt;<br>&lt;servlet-class&gt;/results/index.jsp&lt;/servlet-class&gt; &lt;!-- Or jsp-file --&gt;<br>&lt;load-on-startup&gt;2&lt;/load-on-startup&gt;<br>&lt;/servlet&gt;<br><br><strong>6 声明过滤器</strong><br><br>servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器，但为了使用与过滤器有关的元素，必须在web.xml中使用版本2.3的DTD。<br>过滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前，必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时，执行链中的下一个过滤器。如果没有其他过滤器，servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权，因此，它们可以查看客户机名、查找到来的cookie等。为了访问servlet或JSP页面的输出，过滤器可将响应对象包裹在一个替身对象（stand-in object）中，比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后，过滤器可检查缓冲区，如有必要，就对它进行修改，然后传送到客户机。<br>例如，程序清单5-11帝国难以了一个简单的过滤器，只要访问相关的servlet或JSP页面，它就截取请求并在标准输出上打印一个报告（开发过程中在桌面系统上运行时，大多数服务器都可以使用这个过滤器）。</p>
<p>程序清单5-11 ReportFilter.java<br>package moreservlets;</p>
<p>import java.io.*;<br>import javax.servlet.*;<br>import javax.servlet.http.*;<br>import java.util.*;</p>
<p>/** Simple filter that prints a report on the standard output <br>* whenever the associated servlet or JSP page is accessed.<br>* &lt;P&gt;<br>* Taken from More Servlets and JavaServer Pages<br>* from Prentice Hall and Sun Microsystems Press,<br>* http://www.moreservlets.com/.<br>* &amp;copy; 2002 Marty Hall; may be freely used or adapted.<br>*/</p>
<p>public class ReportFilter implements Filter {<br>public void doFilter(ServletRequest request,<br>ServletResponse response,<br>FilterChain chain)<br>throws ServletException, IOException {<br>HttpServletRequest req = (HttpServletRequest)request;<br>System.out.println(req.getRemoteHost() +<br>" tried to access " +<br>req.getRequestURL() +<br>" on " + new Date() + ".");<br>chain.doFilter(request,response);<br>}</p>
<p>public void init(FilterConfig config)<br>throws ServletException {<br>}<br><br>public void destroy() {}<br>}</p>
<p>一旦建立了一个过滤器，可以在web.xml中利用filter元素以及filter-name（任意名称）、file-class（完全限定的类名）和（可选的）init-params子元素声明它。请注意，元素在web.xml的web-app元素中出现的次序不是任意的；允许服务器（但不是必需的）强制所需的次序，并且实际中有些服务器也是这样做的。但这里要注意，所有filter元素必须出现在任意filter-mapping元素之前， filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。<br>例如，给定上述的ReportFilter类，可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter（位于moreservlets程序包中）相关联。<br>&lt;filter&gt;<br>&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br>&lt;filter-class&gt;moresevlets.ReportFilter&lt;/filter-class&gt;<br>&lt;/filter&gt;<br>一旦命名了一个过滤器，可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。<br>首先，可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名（此servlet名必须稍后在相同的 web.xml文件中使用servlet元素声明）关联。例如，下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName 的servlet或JSP页面，就运行名为Reporter的过滤器。<br>&lt;filter-mapping&gt;<br>&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br>&lt;servlet-name&gt;SomeServletName&lt;/servlet-name&gt;<br>&lt;/filter-mapping&gt;<br>其次，可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如，相面的程序片段指示系统只要访问Web应用中的任意URL，就运行名为Reporter的过滤器。<br>&lt;filter-mapping&gt;<br>&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br>&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>例如，程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字 PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http: //host/webAppPrefix/UrlTest2/ 开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上，程序清单5- 12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项，可看到下面的标准输出形式的调试报告（换行是为了容易阅读）。<br>audit.irs.gov tried to access <br>http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html<br>on Tue Dec 25 13:12:29 EDT 2001.</p>
<p>程序清单5-12 Web.xml（说明filter用法的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;filter&gt;<br>&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br>&lt;filter-class&gt;moresevlets.ReportFilter&lt;/filter-class&gt;<br>&lt;/filter&gt;<br>&lt;!-- ... --&gt;<br>&lt;filter-mapping&gt;<br>&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br>&lt;/filter-mapping&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet&gt;<br>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br>&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt;<br>&lt;/servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet-mapping&gt;<br>&lt;servlet-name&gt; PageName &lt;/servlet-name&gt;<br>&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p><br><strong>7 指定欢迎页</strong><br><br>假如用户提供了一个像http: //host/webAppPrefix/directoryName/ 这样的包含一个目录名但没有包含文件名的URL，会发生什么事情呢？用户能得到一个目录表？一个错误？还是标准文件的内容？如果得到标准文件内容，是 index.html、index.jsp、default.html、default.htm或别的什么东西呢？<br>Welcome-file-list 元素及其辅助的welcome-file元素解决了这个模糊的问题。例如，下面的web.xml项指出，如果一个URL给出一个目录名但未给出文件名，服务器应该首先试用index.jsp，然后再试用index.html。如果两者都没有找到，则结果有赖于所用的服务器（如一个目录列表）。<br>&lt;welcome-file-list&gt;<br>&lt;welcome-file&gt;index.jsp&lt;/welcome-file&gt;<br>&lt;welcome-file&gt;index.html&lt;/welcome-file&gt;<br>&lt;/welcome-file-list&gt;<br>虽然许多服务器缺省遵循这种行为，但不一定必须这样。因此，明确地使用welcom-file-list保证可移植性是一种良好的习惯。<br><br><strong>8 指定处理错误的页面</strong><br><br>现在我了解到，你在开发servlet和JSP页面时从不会犯错误，而且你的所有页面是那样的清晰，一般的程序员都不会被它们的搞糊涂。但是，是人总会犯错误的，用户可能会提供不合规定的参数，使用不正确的URL或者不能提供必需的表单字段值。除此之外，其它开发人员可能不那么细心，他们应该有些工具来克服自己的不足。<br>error-page元素就是用来克服这些问题的。它有两个可能的子元素，分别是：error-code和exception- type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此 URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息，这两个属性分别是：javax.servlet.error.status_code和javax.servlet.error.message。<br>可回忆一下，在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住，error-page出现在web.xml文件的末尾附近，servlet、servlet-name和welcome-file-list之后即可。<br><br>8.1 error-code元素<br>为了更好地了解error-code元素的值，可考虑一下如果不正确地输入文件名，大多数站点会作出什么反映。这样做一般会出现一个404错误信息，它表示不能找到该文件，但几乎没提供更多有用的信息。另一方面，可以试一下在www.microsoft.com、www.ibm.com 处或者特别是在www.bea.com 处输出未知的文件名。这是会得出有用的消息，这些消息提供可选择的位置，以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。事实上，http://www.plinko.net/404/ 就是把整个站点专门用于404错误页面这个内容。这个站点包含来自全世界最好、最糟和最搞笑的404页面。<br>程序清单5-13给出一个JSP页面，此页面可返回给提供位置程序名的客户机。程序清单5-14给出指定程序清单5-13作为返回404错误代码时显示的页面的web.xml。请注意，浏览器中显示的URL仍然是客户机所提供的。错误页面是一种后台实现技术。<br>最后一点，请记住IE5的缺省配置显然不符合HTTP规范，它忽略了服务器生成的错误消息，而是显示自己的标准出错信息。可转到其Tools菜单，选择 Internet Options，单击Advanced，取消Show Friendly HTTP Error Message来解决此问题。</p>
<p>程序清单5-13 NotFound.jsp<br>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;<br>&lt;HTML&gt;<br>&lt;HEAD&gt;&lt;TITLE&gt;404: Not Found&lt;/TITLE&gt;&lt;/HEAD&gt;<br>&lt;BODY BGCOLOR="#FDF5E6"&gt;<br>&lt;H2&gt;Error!&lt;/H2&gt;<br>I'm sorry, but I cannot find a page that matches<br>&lt;%= request.getRequestURI() %&gt; on the system. Maybe you should<br>try one of the following:<br>&lt;UL&gt;<br>&lt;LI&gt;Go to the server's &lt;A HREF="/"&gt;home page&lt;/A&gt;.<br>&lt;LI&gt;Search for relevant pages.&lt;BR&gt;<br>&lt;FORM ACTION="http://www.google.com/search"&gt;<br>&lt;CENTER&gt;<br>Keywords: &lt;INPUT TYPE="TEXT" NAME="q"&gt;&lt;BR&gt;<br>&lt;INPUT TYPE="SUBMIT" VALUE="Search"&gt;<br>&lt;/CENTER&gt;<br>&lt;/FORM&gt;<br>&lt;LI&gt;Admire a random multiple of 404:<br>&lt;%= 404*((int)(1000*Math.random())) %&gt;.<br>&lt;LI&gt;Try a &lt;A HREF="http://www.plinko.net/404/rndindex.asp"<br>TARGET="_blank"&gt;<br>random 404 error message&lt;/A&gt;. From the amazing and<br>amusing plinko.net &lt;A HREF="http://www.plinko.net/404/"&gt;<br>404 archive&lt;/A&gt;.<br>&lt;/UL&gt;<br>&lt;/BODY&gt;&lt;/HTML&gt;</p>
<p><br>程序清单5-14 web.xml（指出HTTP错误代码的错误页面的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;error-page&gt;<br>&lt;error-code&gt;404&lt;/error-code&gt;<br>&lt;location&gt;/NotFound.jsp&lt;/location&gt;<br>&lt;/error-page&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p><br>8.2 exception-type元素<br>error-code元素处理某个请求产生一个特定的HTTP状态代码时的情况。然而，对于servlet或JSP页面返回200但产生运行时异常这种同样是常见的情况怎么办呢？这正是exception-type元素要处理的情况。只需提供两样东西即可：即提供如下的一个完全限定的异常类和一个位置：<br>&lt;error-page&gt;<br>&lt;exception-type&gt;packageName.className&lt;/exception-type&gt;<br>&lt;location&gt;/SomeURL&lt;/location&gt;<br>&lt;/error-page&gt;<br>这样，如果Web应用中的任何servlet或JSP页面产生一个特定类型的未捕捉到的异常，则使用指定的URL。此异常类型可以是一个标准类型，如 javax.ServletException或java.lang.OutOfMemoryError，或者是一个专门针对你的应用的异常。<br>例如，程序清单5-15给出了一个名为DumbDeveloperException的异常类，可用它来特别标记经验较少的程序员（不是说你的开发组中一定有这种人）所犯的错误。这个类还包含一个名为dangerousComputation的静态方法，它时不时地生成这种类型的异常。程序清单5-16给出对随机整数值调用dangerousCompution的一个JSP页面。在抛出此异常时，如程序清单5-18的web.xml版本中所给出的 exception-type所指出的那样，对客户机显示DDE.jsp（程序清单5-17）。图5-16和图5-17分别给出幸运和不幸的结果。</p>
<p>程序清单5-15 DumbDeveloperException.java<br>package moreservlets;</p>
<p>/** Exception used to flag particularly onerous<br>programmer blunders. Used to illustrate the<br>exception-type web.xml element.<br>* &lt;P&gt;<br>* Taken from More Servlets and JavaServer Pages<br>* from Prentice Hall and Sun Microsystems Press,<br>* http://www.moreservlets.com/.<br>* &amp;copy; 2002 Marty Hall; may be freely used or adapted.<br>*/</p>
<p>public class DumbDeveloperException extends Exception {<br>public DumbDeveloperException() {<br>super("Duh. What was I *thinking*?");<br>}</p>
<p>public static int dangerousComputation(int n)<br>throws DumbDeveloperException {<br>if (n &lt; 5) {<br>return(n + 10);<br>} else {<br>throw(new DumbDeveloperException());<br>}<br>}<br>}</p>
<p><br>程序清单5-16 RiskyPage.jsp<br>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;<br>&lt;HTML&gt;<br>&lt;HEAD&gt;&lt;TITLE&gt;Risky JSP Page&lt;/TITLE&gt;&lt;/HEAD&gt;<br>&lt;BODY BGCOLOR="#FDF5E6"&gt;<br>&lt;H2&gt;Risky Calculations&lt;/H2&gt;<br>&lt;%@ page import="moreservlets.*" %&gt;<br>&lt;% int n = ((int)(10 * Math.random())); %&gt;<br>&lt;UL&gt;<br>&lt;LI&gt;n: &lt;%= n %&gt;<br>&lt;LI&gt;dangerousComputation(n): <br>&lt;%= DumbDeveloperException.dangerousComputation(n) %&gt;<br>&lt;/UL&gt;<br>&lt;/BODY&gt;&lt;/HTML&gt;</p>
<p><br>程序清单5-17 DDE.jsp<br>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;<br>&lt;HTML&gt;<br>&lt;HEAD&gt;&lt;TITLE&gt;Dumb&lt;/TITLE&gt;&lt;/HEAD&gt;<br>&lt;BODY BGCOLOR="#FDF5E6"&gt;<br>&lt;H2&gt;Dumb Developer&lt;/H2&gt;<br>We're brain dead. Consider using our competitors.<br>&lt;/BODY&gt;&lt;/HTML&gt;</p>
<p><br>程序清单5-18 web.xml（为异常指定错误页面的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;!-- ... --&gt;<br>&lt;servlet&gt; &#8230; &lt;/servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;error-page&gt;<br>&lt;exception-type&gt;<br>moreservlets.DumbDeveloperException<br>&lt;/exception-type&gt;<br>&lt;location&gt;/DDE.jsp&lt;/location&gt;<br>&lt;/error-page&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p><strong>9 提供安全性</strong><br><br>利用web.xml中的相关元素为服务器的内建功能提供安全性。<br>9.1 指定验证的方法<br>使用login-confgi元素规定服务器应该怎样验证试图访问受保护页面的用户。它包含三个可能的子元素，分别是：auth-method、realm -name和form-login-config。login-config元素应该出现在web.xml部署描述符文件的结尾附近，紧跟在 security-constraint元素之后。<br>l auth-method<br>login-config的这个子元素列出服务器将要使用的特定验证机制。有效值为BASIC、DIGEST、FORM和CLIENT-CERT。服务器只需要支持BASIC和FORM。<br>BASIC 指出应该使用标准的HTTP验证，在此验证中服务器检查Authorization头。如果缺少这个头则返回一个401状态代码和一个WWW- Authenticate头。这导致客户机弹出一个用来填写Authorization头的对话框。此机制很少或不提供对攻击者的防范，这些攻击者在 Internet连接上进行窥探（如通过在客户机的子网上执行一个信息包探测装置），因为用户名和口令是用简单的可逆base64编码发送的，他们很容易得手。所有兼容的服务器都需要支持BASIC验证。<br>DIGEST指出客户机应该利用加密Digest Authentication形式传输用户名和口令。这提供了比BASIC验证更高的防范网络截取得的安全性，但这种加密比SSL（HTTPS）所用的方法更容易破解。不过，此结论有时没有意义，因为当前很少有浏览器支持Digest Authentication，所以servlet容器不需要支持它。<br>FORM 指出服务器应该检查保留的会话cookie并且把不具有它的用户重定向到一个指定的登陆页。此登陆页应该包含一个收集用户名和口令的常规HTML表单。在登陆之后，利用保留会话级的cookie跟踪用户。虽然很复杂，但FORM验证防范网络窥探并不比BASIC验证更安全，如果有必要可以在顶层安排诸如 SSL或网络层安全（如IPSEC或VPN）等额外的保护。所有兼容的服务器都需要支持FORM验证。<br>CLIENT-CERT规定服务器必须使用HTTPS（SSL之上的HTTP）并利用用户的公开密钥证书（Pulic Key Certificat）对用户进行验证。这提供了防范网络截取的很强的安全性，但只有兼容J2EE的服务器需要支持它。<br>l realm-name<br>此元素只在auth-method为BASIC时使用。它指出浏览器在相应对话框标题使用的、并作为Authorization头组成部分的安全域的名称。<br>l form-login-config<br>此元素只在auth-method为FORM时适用。它指定两个页面，分别是：包含收集用户名及口令的HTML表单的页面（利用form-login- page子元素），用来指示验证失败的页面（利用form-error-page子元素）。由form-login-page给出的HTML表单必须具有一个j_security_check的ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。<br>例如，程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令，并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。</p>
<p>程序清单5-19 web.xml（说明login-config的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;!-- ... --&gt;<br>&lt;security-constraint&gt; ... &lt;/security-constraint&gt;<br>&lt;login-config&gt;<br>&lt;auth-method&gt; FORM &lt;/auth-method&gt;<br>&lt;form-login-config&gt;<br>&lt;form-login-page&gt;/login.jsp&lt;/form-login-page&gt;<br>&lt;form-error-page&gt;/login-error.jsp&lt;/form-error-page&gt;<br>&lt;/form-login-config&gt;<br>&lt;/login-config&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p><br>9.2 限制对Web资源的访问<br>现在，可以指示服务器使用何种验证方法了。"了不起，"你说道，"除非我能指定一个来收到保护的 URL，否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在 web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素，分别是：web-resource-collection、 auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。<br>l web-resource-collection<br>此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的 HTTP命令（GET、POST等，缺省为所有方法）的http-method元素和一个提供资料的可选description元素组成。例如，下面的 Web-resource-collection项（在security-constratint元素内）指出Web应用的proprietary目录中所有文档应该受到保护。<br>&lt;security-constraint&gt;<br>&lt;web-resource-coolection&gt;<br>&lt;web-resource-name&gt;Proprietary&lt;/web-resource-name&gt;<br>&lt;url-pattern&gt;/propritary/*&lt;/url-pattern&gt;<br>&lt;/web-resource-coolection&gt;<br>&lt;!-- ... --&gt;<br>&lt;/security-constraint&gt;<br>重要的是应该注意到，url-pattern仅适用于直接访问这些资源的客户机。特别是，它不适合于通过MVC体系结构利用 RequestDispatcher来访问的页面，或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好处。例如，servlet可利用MVC体系结构查找数据，把它放到bean中，发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直接访问受保护的JSP页面，而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是，这种不匀称的行为可能让开发人员放松警惕，使他们偶然对应受保护的资源提供不受限制的访问。 <br>l auth-constraint<br>尽管web-resource-collention元素质出了哪些URL应该受到保护，但是auth-constraint元素却指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role- name元素，以及包含（可选）一个描述角色的description元素。例如，下面web.xml中的security-constraint元素部门规定只有指定为Administrator或Big Kahuna（或两者）的用户具有指定资源的访问权。<br>&lt;security-constraint&gt;<br>&lt;web-resource-coolection&gt; ... &lt;/web-resource-coolection&gt;<br>&lt;auth-constraint&gt;<br>&lt;role-name&gt;administrator&lt;/role-name&gt;<br>&lt;role-name&gt;kahuna&lt;/role-name&gt;<br>&lt;/auth-constraint&gt;<br>&lt;/security-constraint&gt;<br>重要的是认识到，到此为止，这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令，完全有赖于具体的系统。<br>例如，Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联，正如下面例子中所示，它指出用户joe（口令bigshot）和jane（口令enaj）属于administrator和kahuna角色。<br>&lt;tomcat-users&gt;<br>&lt;user name="joe" password="bigshot" roles="administrator,kahuna" /&gt;<br>&lt;user name="jane" password="enaj" roles="kahuna" /&gt;<br>&lt;/tomcat-users&gt;<br>l user-data-constraint<br>这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素（合法值为NONE、 INTEGRAL或CONFIDENTIAL），并且可选地包含一个description元素。transport-guarantee为NONE值将对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上（并且在未来的HTTP版本中），在 INTEGRAL和CONFIDENTIAL之间可能会有差别，但在当前实践中，他们都只是简单地要求用SSL。例如，下面指示服务器只允许对相关资源做 HTTPS连接：<br>&lt;security-constraint&gt;<br>&lt;!-- ... --&gt;<br>&lt;user-data-constraint&gt;<br>&lt;transport-guarantee&gt;CONFIDENTIAL&lt;/transport-guarantee&gt;<br>&lt;/user-data-constraint&gt;<br>&lt;/security-constraint&gt;<br>l display-name<br>security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。<br>9.3 分配角色名<br>迄今为止，讨论已经集中到完全由容器（服务器）处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。<br>例如，容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面，但只允许bigwig用户修改此页面的参数。完成这种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法，并据此修改访问。<br>Servlet的 security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如，假如编写了一个调用 request.isUserInRole（"boss"）的servlet，但后来该servlet被用在了一个其口令文件调用角色manager而不是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。<br>&lt;servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;security-role-ref&gt;<br>&lt;role-name&gt;boss&lt;/role-name&gt; &lt;!-- New alias --&gt;<br>&lt;role-link&gt;manager&lt;/role-link&gt; &lt;!-- Real name --&gt;<br>&lt;/security-role-ref&gt;<br>&lt;/servlet&gt;<br>也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级IDE容易处理安全信息。<br><br><strong>10 控制会话超时</strong><br><br>如果某个会话在一定的时间内未被访问，服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直接设置个别会话对象的超时值。如果不采用这种方法，则缺省的超时值由具体的服务器决定。但可利用session-config和session- timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟，因此，下面的例子设置缺省会话超时值为三个小时（180分钟）。<br>&lt;session-config&gt;<br>&lt;session-timeout&gt;180&lt;/session-timeout&gt;<br>&lt;/session-config&gt;<br><br><strong>11 Web应用的文档化</strong><br><br>越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder Enterprise Edition、Macromedia UltraDev、Allaire JRun Studio（写此文时，已被Macromedia收购）以及IBM VisuaAge for Java等。<br>大量的web.xml元素不仅是为服务器设计的，而且还是为可视开发环境设计的。它们包括icon、display-name和discription等。<br>可回忆一下，在web.xml内以适当地次序声明web-app子元素很重要。不过，这里只要记住icon、display-name和description是web.xml的web-app元素内的前三个合法元素即可。<br>l icon<br>icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x 16的GIF或JPEG图像，用large-icon元素指定一幅32 x 32的图像。下面举一个例子： <br>&lt;icon&gt;<br>&lt;small-icon&gt;/images/small-book.gif&lt;/small-icon&gt;<br>&lt;large-icon&gt;/images/tome.jpg&lt;/large-icon&gt;<br>&lt;/icon&gt;<br>l display-name<br>display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。<br>&lt;display-name&gt;Rare Books&lt;/display-name&gt;<br>l description<br>description元素提供解释性文本，如下所示：<br>&lt;description&gt;<br>This Web application represents the store developed for<br>rare-books.com, an online bookstore specializing in rare<br>and limited-edition books.<br>&lt;/description&gt;<br><br><strong>12 关联文件与MIME类型</strong><br><br>服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如，将会自动给予名为mom.jpg的文件一个image/jpeg的MIME 类型。但是，假如你的Web应用具有几个不寻常的文件，你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素（具有 extension和mime-type子元素）可提供这种保证。例如，下面的代码指示服务器将application/x-fubar的MIME类型分配给所有以.foo结尾的文件。<br>&lt;mime-mapping&gt;<br>&lt;extension&gt;foo&lt;/extension&gt;<br>&lt;mime-type&gt;application/x-fubar&lt;/mime-type&gt;<br>&lt;/mime-mapping&gt;<br>或许，你的Web应用希望重载（override）标准的映射。例如，下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本（text/plain）而不是作为PostScript（application/postscript）。<br>&lt;mime-mapping&gt;<br>&lt;extension&gt;ps&lt;/extension&gt;<br>&lt;mime-type&gt;application/postscript&lt;/mime-type&gt;<br>&lt;/mime-mapping&gt;<br><br><br><strong>13 定位TLD</strong><br><br>JSP taglib元素具有一个必要的uri属性，它给出一个TLD（Tag Library Descriptor）文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变，但我们希望避免更改所有现有JSP页面。此外，可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两个子元素：taglib-uri和taglib-location。taglib-uri元素应该与用于JSP taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如，假如你将文件chart-tags- 1.3beta.tld放在WebApp/WEB-INF/tlds中。现在，假如web.xml在web-app元素内包含下列内容。<br>&lt;taglib&gt;<br>&lt;taglib-uri&gt;/charts.tld&lt;/taglib-uri&gt;<br>&lt;taglib-location&gt;<br>/WEB-INF/tlds/chart-tags-1.3beta.tld<br>&lt;/taglib-location&gt;<br>&lt;/taglib&gt;<br>给出这个说明后，JSP页面可通过下面的简化形式使用标签库。<br>&lt;%@ taglib uri="/charts.tld" prefix="somePrefix" %&gt;<br><strong><br>14 指定应用事件监听程序<br></strong><br>应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向Web应用注册一个监听程序的web.xml的用法。<br>注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内，listener-class元素列出监听程序的完整的限定类名，如下所示：<br>&lt;listener&gt;<br>&lt;listener-class&gt;package.ListenerClass&lt;/listener-class&gt;<br>&lt;/listener&gt;<br>虽然listener元素的结构很简单，但请不要忘记，必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的servlet 元素之前以及所有filter-mapping元素之后。此外，因为应用生存期监听程序是serlvet规范的2.3版本中的新内容，所以必须使用 web.xml DTD的2.3版本，而不是2.2版本。<br>例如，程序清单5-20给出一个名为ContextReporter的简单的监听程序，只要Web应用的Servlet-Context建立（如装载Web应用）或消除（如服务器关闭）时，它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。</p>
<p>程序清单5-20 ContextReporterjava<br>package moreservlets;</p>
<p>import javax.servlet.*;<br>import java.util.*;</p>
<p>/** Simple listener that prints a report on the standard output <br>* when the ServletContext is created or destroyed.<br>* &lt;P&gt;<br>* Taken from More Servlets and JavaServer Pages<br>* from Prentice Hall and Sun Microsystems Press,<br>* http://www.moreservlets.com/.<br>* &amp;copy; 2002 Marty Hall; may be freely used or adapted.<br>*/</p>
<p>public class ContextReporter implements ServletContextListener {<br>public void contextInitialized(ServletContextEvent event) {<br>System.out.println("Context created on " +<br>new Date() + ".");<br>}</p>
<p>public void contextDestroyed(ServletContextEvent event) {<br>System.out.println("Context destroyed on " +<br>new Date() + ".");<br>}<br>}</p>
<p><br>程序清单5-21 web.xml（声明一个监听程序的摘录）<br>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br>&lt;!DOCTYPE web-app<br>PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br>"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;</p>
<p>&lt;web-app&gt;<br>&lt;!-- ... --&gt;<br>&lt;filter-mapping&gt; &#8230; &lt;/filter-mapping&gt;<br>&lt;listener&gt;<br>&lt;listener-class&gt;package.ListenerClass&lt;/listener-class&gt;<br>&lt;/listener&gt;<br>&lt;servlet&gt; ... &lt;/servlet&gt;<br>&lt;!-- ... --&gt;<br>&lt;/web-app&gt;</p>
<p><br><strong>15 J2EE元素</strong><br><br>本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍，详细内容可以参阅http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf的Java 2 Plantform Enterprise Edition版本1.3规范的第5章。<br>l distributable<br>distributable 元素指出，Web应用是以这样的方式编程的：即，支持集群的服务器可安全地在多个服务器上分布Web应用。例如，一个可分布的应用必须只使用 Serializable对象作为其HttpSession对象的属性，而且必须避免用实例变量（字段）来实现持续性。distributable元素直接出现在discription元素之后，并且不包含子元素或数据，它只是一个如下的标志。<br>&lt;distributable /&gt;<br>l resource-env-ref<br>resource -env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref- name元素（一个相对于java:comp/env环境的JNDI名）以及一个resource-env-type元素（指定资源类型的完全限定的类），如下所示：<br>&lt;resource-env-ref&gt;<br>&lt;resource-env-ref-name&gt;<br>jms/StockQueue<br>&lt;/resource-env-ref-name&gt;<br>&lt;resource-env-ref-type&gt;<br>javax.jms.Queue<br>&lt;/resource-env-ref-type&gt;<br>&lt;/resource-env-ref&gt;<br>l env-entry<br>env -entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素（一个相对于java: comp/env环境JNDI名）、一个env-entry-value元素（项值）以及一个env-entry-type元素（java.lang程序包中一个类型的完全限定类名，java.lang.Boolean、java.lang.String等）组成。下面是一个例子：<br>&lt;env-entry&gt;<br>&lt;env-entry-name&gt;minAmout&lt;/env-entry-name&gt;<br>&lt;env-entry-value&gt;100.00&lt;/env-entry-value&gt;<br>&lt;env-entry-type&gt;minAmout&lt;/env-entry-type&gt;<br>&lt;/env-entry&gt;<br>l ejb-ref<br>ejb -ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素（相对于java: comp/env的EJB应用）、一个ejb-ref-type元素（bean的类型，Entity或Session）、一个home元素（bean的主目录接口的完全限定名）、一个remote元素（bean的远程接口的完全限定名）以及一个可选的ejb-link元素（当前bean链接的另一个 bean的名称）组成。<br>l ejb-local-ref<br>ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外，此元素具有与ejb-ref元素相同的属性并以相同的方式使用。</p>
</div>
</div>
<img src ="http://www.blogjava.net/zhangheng/aggbug/124925.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-06-18 13:38 <a href="http://www.blogjava.net/zhangheng/archive/2007/06/18/124925.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebWork深入浅出</title><link>http://www.blogjava.net/zhangheng/archive/2007/06/18/124924.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Mon, 18 Jun 2007 05:29:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/06/18/124924.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/124924.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/06/18/124924.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/124924.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/124924.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本文发表于《开源大本营》&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 作者：钱...&nbsp;&nbsp;<a href='http://www.blogjava.net/zhangheng/archive/2007/06/18/124924.html'>阅读全文</a><img src ="http://www.blogjava.net/zhangheng/aggbug/124924.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-06-18 13:29 <a href="http://www.blogjava.net/zhangheng/archive/2007/06/18/124924.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>微软的正则表达式教程（五）：选择/编组和后向引用 </title><link>http://www.blogjava.net/zhangheng/archive/2007/06/14/124317.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Thu, 14 Jun 2007 06:17:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/06/14/124317.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/124317.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/06/14/124317.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/124317.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/124317.html</trackback:ping><description><![CDATA[<h2><a name=reconalternationgrouping></a>选择与编组</h2>
<p>选择允许使用 '|' 字符来在两个或多个候选项中进行选择。通过扩展章节标题的正则表达式，可以将其扩充为不仅仅适用于章节标题的表达式。不过，这可没有想象的那么直接。在使用选择时，将匹配'|' 字符每边最可能的表达式。你可能认为下面的 JScript 和 VBScript 表达式将匹配位于一行的开始和结束位置且后跟一个或两个数字的 'Chapter' 或 'Section'：</p>
<pre><code>/^Chapter|Section [1-9][0-9]{0,1}$/ "^Chapter|Section [1-9][0-9]{0,1}$"</code></pre>
<p>不幸的是，真正的情况是上面所示的正则表达式要么匹配位于一行开始处的单词 'Chapter'，要么匹配一行结束处的后跟任何数字的 'Section'。如果输入字符串为 'Chapter 22'，上面的表达式将只匹配单词 'Chapter'。如果输入字符串为 'Section 22'，则该表达式将匹配 'Section 22'。但这种结果不是我们此处的目的，因此必须有一种办法来使正则表达式对于所要做的更易于响应，而且确实也有这种方法。</p>
<p>可以使用圆括号来限制选择的范围，也就是说明确该选择只适用于这两个单词 'Chapter' 和 'Section'。不过，圆括号同样也是难处理的，因为它们也用来创建子表达式，有些内容将在后面关于子表达式的部分介绍。通过采用上面所示的正则表达式并在适当位置添加圆括号，就可以使该正则表达式既可以匹配 'Chapter 1'，也可以匹配 'Section 3'。 </p>
<p>下面的正则表达式使用圆括号将 'Chapter' 和 'Section' 组成一组，所以该表达式才能正确工作。对 JScript 为：</p>
<pre><code>/^(Chapter|Section) [1-9][0-9]{0,1}$/</code></pre>
<p>对 VBScript 为：</p>
<pre><code>"^(Chapter|Section) [1-9][0-9]{0,1}$"</code></pre>
<p>这些表达式工作正确，只是产生了一个有趣的副产品。在 'Chapter|Section' 两边放置圆括号建立了适当的编组，但也导致两个待匹配单词之一都被捕获供今后使用。由于在上面所示的表达式中只有一组圆括号，因此只能有一个捕获的 <em>submatch</em>。可以使用 VBScript 的<strong>Submatches</strong> 集合或者JScript 中<strong>RegExp</strong> 对象的 <strong>$1-$9</strong> 属性来引用这个子匹配。</p>
<p>有时捕获一个子匹配是所希望的，有时则是不希望的。在说明所示的示例中，真正想做的就是使用圆括号对单词 'Chapter' 或 'Section' 之间的选择编组。并不希望在后面再引用该匹配。实际上，除非真的是需要捕获子匹配，否则请不要使用。由于不需要花时间和内存来存储那些子匹配，这种正则表达式的效率将更高。</p>
<p>可以在正则表达式模式圆括号内部的前面使用 '?:'来防止存储该匹配供今后使用。对上面所示正则表达式的下述修改提供了免除子匹配存储的相同功能。对 JScript：</p>
<pre><code>/^(?:Chapter|Section) [1-9][0-9]{0,1}$/</code></pre>
<p>对 VBScript：</p>
<pre><code>"^(?:Chapter|Section) [1-9][0-9]{0,1}$"</code></pre>
<p>除了 '?:' 元字符，还有两个非捕获元字符用于称之为<em>预查</em>的匹配。一个为正向预查，用 ?= 表示， 在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串。一个为负向预查，用 '?!' 表示，在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。</p>
<p>例如，假定有一个包含引用有 Windows 3.1、Windows 95、Windows 98 以及 Windows NT 的文档。进一步假设需要更新该文档，方法是查找所有对 Windows 95、Windows 98 以及 Windows NT 的引用，并将这些引用更改为 Windows 2000。可以使用下面的 JScript 正则表达式，这是一个正向预查，来匹配 Windows 95、Windows 98 以及 Windows NT：</p>
<pre><code>/Windows(?=95 |98 |NT )/</code></pre>
<p>在 VBScript 要进行同样的匹配可以使用下述表达式：</p>
<pre><code>"Windows(?=95 |98 |NT )"</code></pre>
<p>找到一个匹配后，紧接匹配到的文字（而不包括预查中使用的字符）就开始对下一次匹配的搜索。例如，如果上面所示的表达式匹配到 'Windows 98'，则将从 'Windows' 而不是 '98' 之后继续查找。</p>
<h2><a name=reconbackreferences></a>后向引用</h2>
<p>正则表达式一个最重要的特性就是将匹配成功的模式的某部分进行存储供以后使用这一能力。请回想一下，对一个正则表达式模式或部分模式两边添加圆括号将导致这部分表达式存储到一个临时缓冲区中。可以使用非捕获元字符 '?:', '?=', or '?!' 来忽略对这部分正则表达式的保存。</p>
<p>所捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内容存储。存储子匹配的缓冲区编号从 1 开始，连续编号直至最大 99 个子表达式。每个缓冲区都可以使用 '\<em>n</em>' 访问，其中 <em>n</em> 为一个标识特定缓冲区的一位或两位十进制数。 </p>
<p>后向引用一个最简单，最有用的应用是提供了确定文字中连续出现两个相同单词的位置的能力。请看下面的句子：</p>
<pre><code>Is is the cost of of gasoline going up up?</code></pre>
<p>根据所写内容，上面的句子明显存在单词多次重复的问题。如果能有一种方法无需查找每个单词的重复现象就能修改该句子就好了。下面的 JScript 正则表达式使用一个子表达式就可以实现这一功能。 </p>
<pre><code>/\b([a-z]+) \1\b/gi</code></pre>
<p>等价的 VBScript 表达式为：</p>
<pre><code>"\b([a-z]+) \1\b"</code></pre>
<p>在这个示例中，子表达式就是圆括号之间的每一项。所捕获的表达式包括一个或多个字母字符，即由'[a-z]+' 所指定的。该正则表达式的第二部分是对前面所捕获的子匹配的引用，也就是由附加表达式所匹配的第二次出现的单词。'\1'用来指定第一个子匹配。单词边界元字符确保只检测单独的单词。如果不这样，则诸如 "is issued" 或 "this is" 这样的短语都会被该表达式不正确地识别。 </p>
<p>在 JScript 表达式中，正则表达式后面的全局标志 ('g') 表示该表达式将用来在输入字符串中查找尽可能多的匹配。大小写敏感性由表达式结束处的大小写敏感性标记 ('i') 指定。多行标记指定可能出现在换行符的两端的潜在匹配。对 VBScript 而言，在表达式中不能设置各种标记，但必须使用 <strong>RegExp</strong> 对象的属性来显式设置。</p>
<p>使用上面所示的正则表达式，下面的 JScript 代码可以使用子匹配信息，在一个文字字符串中将连续出现两次的相同单词替换为一个相同的单词：</p>
<pre><code>var ss = "Is is the cost of of gasoline going up up?.\n"; var re = /\b([a-z]+) \1\b/gim; //</code>创建正则表达式样式<code>. var rv = ss.replace(re,"$1"); //</code>用一个单词替代两个单词<code>.</code></pre>
<p>最接近的等价&nbsp; VBScript 代码如下：</p>
<pre><code>Dim ss, re, rv ss = "Is is the cost of of gasoline going up up?." &amp; vbNewLine Set re = New RegExp re.Pattern = "\b([a-z]+) \1\b" re.Global = True re.IgnoreCase = True re.MultiLine = True rv = re.Replace(ss,"$1")</code></pre>
<p>请注意在 VBScript 代码中，全局、大小写敏感性以及多行标记都是使用 <strong>RegExp</strong> 对象的适当属性来设置的。</p>
<p>在<strong>replace</strong> 方法中使用 <strong>$1</strong> 来引用所保存的第一个子匹配。如果有多个子匹配，则可以用 <strong>$2</strong>, <strong>$3 </strong>等继续引用。</p>
<p>后向引用的另一个用途是将一个通用资源指示符 (URI) 分解为组件部分。假定希望将下述的URI 分解为协议 (ftp, http, etc)，域名地址以及页面/路径：</p>
<pre><u><code>http://msdn.microsoft.com:80/scripting/default.htm</code></u></pre>
<p>下面的正则表达式可以提供这个功能。对 JScript，为：</p>
<pre><code>/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/</code></pre>
<p>对 VBScript 为：</p>
<pre><code>"(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)"</code></pre>
<p>第一个附加子表达式是用来捕获该 web 地址的协议部分。该子表达式匹配位于一个冒号和两个正斜杠之前的任何单词。第二个附加子表达式捕获该地址的域名地址。该子表达式匹配不包括 '^'、 '/' 或 ':' 字符的任何字符序列。第三个附加子表达式捕获网站端口号码，如果指定了该端口号。该子表达式匹配后跟一个冒号的零或多个数字。最后，第四个附加子表达式捕获由该 web 地址指定的路径以及\或者页面信息。该子表达式匹配一个和多个除'#' 或空格之外的字符。</p>
<p>将该正则表达式应用于上面所示的 URI 后，子匹配包含下述内容：</p>
<p class=tl><strong>RegExp.$1</strong> 包含 "http"</p>
<p class=tl><strong>RegExp.$2</strong> 包含 "msdn.microsoft.com"</p>
<p class=tl><strong>RegExp.$3</strong> 包含 ":80"</p>
<p class=tl><strong>RegExp.$4</strong> 包含 "/scripting/default.htm"</p>
<br>
<img src ="http://www.blogjava.net/zhangheng/aggbug/124317.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-06-14 14:17 <a href="http://www.blogjava.net/zhangheng/archive/2007/06/14/124317.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>微软的正则表达式教程（四）：限定符和定位符 </title><link>http://www.blogjava.net/zhangheng/archive/2007/06/14/124315.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Thu, 14 Jun 2007 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/06/14/124315.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/124315.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/06/14/124315.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/124315.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/124315.html</trackback:ping><description><![CDATA[<h2><a name=reconquantifiers></a>限定符</h2>
<p>有时候不知道要匹配多少字符。为了能适应这种不确定性，正则表达式支持限定符的概念。这些限定符可以指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。</p>
<p>下表给出了各种限定符及其含义的说明：</p>
<table cellSpacing=0 cols=2 rules=all border=1 frame=box>
    <tbody>
        <tr vAlign=top>
            <th width="16%">字符</th>
            <th width="84%">描述</th>
        </tr>
        <tr vAlign=top>
            <td width="16%">* </td>
            <td width="84%">匹配前面的子表达式零次或多次。例如，zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">+ </td>
            <td width="84%">匹配前面的子表达式一次或多次。例如，'zo+' 能匹配 "zo" 以及 "zoo"，但不能匹配 "z"。+ 等价于 {1,}。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">? </td>
            <td width="84%">匹配前面的子表达式零次或一次。例如，"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">{<em>n</em>}</td>
            <td width="84%"><em>n</em> 是一个非负整数。匹配确定的 <em>n</em> 次。例如，'o{2}' 不能匹配 "Bob" 中的 'o'，但是能匹配 "food" 中的两个 o。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">{<em>n</em>,} </td>
            <td width="84%"><em>n</em> 是一个非负整数。至少匹配<em>n</em> 次。例如，'o{2,}' 不能匹配 "Bob" 中的 'o'，但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">{<em>n</em>,<em>m</em>} </td>
            <td width="84%"><em>m</em> 和 <em>n</em> 均为非负整数，其中<em>n</em> &lt;= <em>m</em>。最少匹配 <em>n</em> 次且最多匹配 <em>m</em> 次。刘， "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。</td>
        </tr>
    </tbody>
</table>
<br>
<p>对一个很大的输入文档而言，章节数很轻易就超过九章，因此需要有一种方法来处理两位数或者三位数的章节号。限定符就提供了这个功能。下面的JScript 正则表达式可以匹配具有任何位数的章节标题：</p>
<pre><code>/Chapter [1-9][0-9]*/</code></pre>
<p>下面的 VBScript 正则表达式执行同样的匹配：</p>
<pre><code>"Chapter [1-9][0-9]*"</code></pre>
<p>请注意限定符出现在范围表达式之后。因此，它将应用于所包含的整个范围表达式，在本例中，只指定了从 0 到 9 的数字。</p>
<p>这里没有使用 '+' 限定符，因为第二位或后续位置上并不一定需要一个数字。同样也没有使用 '?' 字符，因为这将把章节数限制为只有两位数字。在 'Chapter' 和空格字符之后至少要匹配一个数字。</p>
<p>如果已知章节数限制只有99 章，则可以使用下面的 JScript 表达式来指定至少有一位数字，但不超过两个数字。</p>
<pre><code>/Chapter [0-9]{1,2}/</code></pre>
<p>对 VBScript 可以使用下述正则表达式：</p>
<pre><code>"Chapter [0-9]{1,2}"</code></pre>
<p>上述表达式的缺点是如果有一个章节号大于 99，它仍只会匹配前两位数字。另一个缺点是某些人可以创建一个 Chapter 0，而且仍能匹配。一个更好的用来匹配两位数的 JScript 表达式如下：</p>
<pre><code>/Chapter [1-9][0-9]?/</code></pre>
<p>或者</p>
<pre><code>/Chapter [1-9][0-9]{0,1}/</code></pre>
<p>对 VBScript 而言，下述表达式与上面等价：</p>
<pre><code>"Chapter [1-9][0-9]?"</code></pre>
<p>或者</p>
<pre><code>"Chapter [1-9][0-9]{0,1}"</code></pre>
<p>'<code>*</code>'、 '<code>+'</code>和 '<code>?'</code> 限定符都称之为<em>贪婪的</em>，也就是说，他们尽可能多地匹配文字。有时这根本就不是所希望发生的情况。有时则正好希望最小匹配。 </p>
<p>例如，你可能要搜索一个 HTML 文档来查找一处包含在 H1 标记中的章节标题。在文档中该文字可能具有如下形式：</p>
<pre><code>&lt;H1&gt;Chapter 1 &#8211; Introduction to Regular Expressions&lt;/H1&gt;</code></pre>
<p>下面的表达式匹配从开始的小于号 (&lt;) 到 H1 标记结束处的大于号之间的所有内容。</p>
<pre><code>/&lt;.*&gt;/</code></pre>
<p>&nbsp;VBScript 的正则表达式为：</p>
<pre><code>"&lt;.*&gt;"</code></pre>
<p>如果所要匹配的就是开始的 H1 标记，则下述非贪婪地表达式就只匹配 &lt;H1&gt;。</p>
<pre><code>/&lt;.*?&gt;/</code></pre>
<p>或者</p>
<pre><code>"&lt;.*?&gt;"</code></pre>
<p>通过在 '*'、 '+' 或 '?' 限定符后放置 '?'，该表达式就从贪婪匹配转为了非贪婪或最小匹配。</p>
<h2><a name=reconanchors></a>&nbsp;</h2>
<h2>定位符</h2>
<p>到现在为止，所看到的示例都只考虑查找任何地方出现的章节标题。出现的任何一个字符串 'Chapter' 后跟一个空格和一个数字可能是一个真正的章节标题，也可能是对其他章节的交叉引用。由于真正的章节标题总是出现在一行的开始，因此需要设计一个方法只查找标题而不查找交叉引用。</p>
<p>定位符提供了这个功能。定位符可以将一个正则表达式固定在一行的开始或结束。也可以创建只在单词内或只在单词的开始或结尾处出现的正则表达式。下表包含了正则表达式及其含义的列表：</p>
<p>
<table cellSpacing=0 cols=2 rules=all border=1 frame=box>
    <tbody>
        <tr vAlign=top>
            <th width="16%">字符</th>
            <th width="84%">描述</th>
        </tr>
        <tr vAlign=top>
            <td width="16%">^</td>
            <td width="84%">匹配输入字符串的开始位置。如果设置了 <strong>RegExp</strong> 对象的 <strong>Multiline</strong> 属性，^ 也匹配 '\n' 或 '\r' 之后的位置。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">$</td>
            <td width="84%">匹配输入字符串的结束位置。如果设置了<strong>RegExp</strong> 对象的 <strong>Multiline</strong> 属性，$ 也匹配 '\n' 或 '\r' 之前的位置。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\b</td>
            <td width="84%">匹配一个单词边界，也就是指单词和空格间的位置。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\B</td>
            <td width="84%">匹配非单词边界。</td>
        </tr>
    </tbody>
</table>
<br></p>
<p>不能对定位符使用限定符。因为在一个换行符或者单词边界的前面或后面不会有连续多个位置，因此诸如 '^*' 的表达式是不允许的。</p>
<p>要匹配一行文字开始位置的文字，请在正则表达式的开始处使用 '^' 字符。不要把 '^' 的这个语法与其在括号表达式中的语法弄混。它们的语法根本不同。 </p>
<p>要匹配一行文字结束位置的文字，请在正则表达式的结束处使用 '$' 字符。</p>
<p>要在查找章节标题时使用定位符，下面的 JScript 正则表达式将匹配位于一行的开始处最多有两个数字的章节标题：</p>
<pre><code>/^Chapter [1-9][0-9]{0,1}/</code></pre>
<p>VBScript 中相同功能的正则表达式如下：</p>
<pre><code>"^Chapter [1-9][0-9]{0,1}"</code></pre>
<p>一个真正的章节标题不仅出现在一行的开始，而且这一行中也仅有这一个内容，因此，它必然也位于一行的结束。下面的表达式确保所指定的匹配只匹配章节而不会匹配交叉引用。它是通过创建一个只匹配一行文字的开始和结束位置的正则表达式来实现的。</p>
<pre><code>/^Chapter [1-9][0-9]{0,1}$/</code></pre>
<p>对 VBScript 则使用：</p>
<pre><code>"^Chapter [1-9][0-9]{0,1}$"</code></pre>
<p>匹配单词边界有少许不同，但却给正则表达式增加了一个非常重要的功能。单词边界就是单词和空格之间的位置。非单词边界就是其他任何位置。下面的 JScript 表达式将匹配单词 'Chapter' 的前三个字符，因为它们出现在单词边界后：</p>
<pre><code>/\bCha/</code></pre>
<p>对 VBScript 为：</p>
<pre><code>"\bCha"</code></pre>
<p>这里 '\b' 操作符的位置很关键。如果它位于要匹配的字符串的开始，则将查找位于单词开头处的匹配；如果它位于改字符串的末尾，则查找位于单词结束处的匹配。例如，下面的表达式将匹配单词 'Chapter' 中的 'ter'，因为它出现在单词边界之前：</p>
<pre><code>/ter\b/</code></pre>
<p>以及</p>
<pre><code>"ter\b"</code></pre>
<p>下面的表达式将匹配 'apt'，因为它位于 'Chapter' 中间，但不会匹配 'aptitude' 中的'apt'：</p>
<pre><code>/\Bapt/</code></pre>
<p>以及</p>
<pre><code>"\Bapt"</code></pre>
<p>这是因为在单词 'Chapter' 中 'apt' 出现在非单词边界位置，而在单词 'aptitude' 中位于单词边界位置。非单词边界操作符的位置不重要，因为匹配与一个单词的开头或结尾无关。</p>
<!--copyright_start--><br><br>
<img src ="http://www.blogjava.net/zhangheng/aggbug/124315.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-06-14 14:16 <a href="http://www.blogjava.net/zhangheng/archive/2007/06/14/124315.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>微软的正则表达式教程（一）：正则表达式简介 </title><link>http://www.blogjava.net/zhangheng/archive/2007/06/14/124314.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Thu, 14 Jun 2007 06:13:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/06/14/124314.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/124314.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/06/14/124314.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/124314.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/124314.html</trackback:ping><description><![CDATA[<p><strong>认识正则表达式</strong></p>
<p>如果原来没有使用过正则表达式，那么可能对这个术语和概念会不太熟悉。不过，它们并不是您想象的那么新奇。</p>
<p>请回想一下在硬盘上是如何查找文件的。您肯定会使用 ? 和 * 字符来帮助查找您正寻找的文件。? 字符匹配文件名中的单个字符，而 * 则匹配一个或多个字符。一个如 'data?.dat' 的模式可以找到下述文件：</p>
<p class=indent>data1.dat</p>
<p class=indent>data2.dat</p>
<p class=indent>datax.dat</p>
<p class=indent>dataN.dat</p>
<p>如果使用 * 字符代替 ? 字符，则将扩大找到的文件数量。'data*.dat' 可以匹配下述所有文件名：</p>
<p class=indent>data.dat</p>
<p class=indent>data1.dat</p>
<p class=indent>data2.dat</p>
<p class=indent>data12.dat</p>
<p class=indent>datax.dat</p>
<p class=indent>dataXYZ.dat</p>
<p>尽管这种搜索文件的方法肯定很有用，但也十分有限。? 和 * 通配符的有限能力可以使你对正则表达式能做什么有一个概念，不过正则表达式的功能更强大，也更灵活。</p>
<h2><a name=reconearlybeginnings></a><font size=3>正则表达式的早期起源</font></h2>
<p>正则表达式的&#8220;祖先&#8221;可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。</p>
<p>1956 年, 一位叫 Stephen Kleene 的美国数学家在 McCulloch 和 Pitts 早期工作的基础上，发表了一篇标题为&#8220;神经网事件的表示法&#8221;的论文，引入了正则表达式的概念。正则表达式就是用来描述他称为&#8220;正则集的代数&#8221;的表达式，因此采用&#8220;正则表达式&#8221;这个术语。 </p>
<p>随后，发现可以将这一工作应用于使用Ken Thompson 的计算搜索算法的一些早期研究，Ken Thompson是Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的<em>qed </em>编辑器。</p>
<p>如他们所说，剩下的就是众所周知的历史了。从那时起直至现在正则表达式都是基于文本的编辑器和搜索工具中的一个重要部分。</p>
<h2><a name=reconusesforregularexpressions></a><font size=3>使用正则表达式</font></h2>
<p>在典型的搜索和替换操作中，必须提供要查找的确切文字。这种技术对于静态文本中的简单搜索和替换任务可能足够了，但是由于它缺乏灵活性，因此在搜索动态文本时就有困难了，甚至是不可能的。 </p>
<p>使用正则表达式，就可以：
<ul type=disc>
    <li>测试字符串的某个模式。例如，可以对一个输入字符串进行测试，看在该字符串是否存在一个电话号码模式或一个信用卡号码模式。这称为数据有效性验证。
    <li>替换文本。可以在文档中使用一个正则表达式来标识特定文字，然后可以全部将其删除，或者替换为别的文字。
    <li>根据模式匹配从字符串中提取一个子字符串。可以用来在文本或输入字段中查找特定文字。 </li>
</ul>
<p>例如，如果需要搜索整个 web 站点来删除某些过时的材料并替换某些HTML 格式化标记，则可以使用正则表达式对每个文件进行测试，看在该文件中是否存在所要查找的材料或 HTML 格式化标记。用这个方法，就可以将受影响的文件范围缩小到包含要删除或更改的材料的那些文件。然后可以使用正则表达式来删除过时的材料，最后，可以再次使用正则表达式来查找并替换那些需要替换的标记。</p>
<img src ="http://www.blogjava.net/zhangheng/aggbug/124314.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-06-14 14:13 <a href="http://www.blogjava.net/zhangheng/archive/2007/06/14/124314.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>微软的正则表达式教程（二）：正则表达式语法和优先权顺序 </title><link>http://www.blogjava.net/zhangheng/archive/2007/06/14/124313.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Thu, 14 Jun 2007 06:12:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/06/14/124313.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/124313.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/06/14/124313.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/124313.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/124313.html</trackback:ping><description><![CDATA[<p>一个正则表达式就是由普通字符（例如字符 a 到 z）以及特殊字符（称为<em>元字符</em>）组成的文字模式。该模式描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板，将某个字符模式与所搜索的字符串进行匹配。</p>
<p>这里有一些可能会遇到的正则表达式示例：</p>
<table cellSpacing=0 cols=3 rules=all border=1 frame=box>
    <tbody>
        <tr vAlign=top>
            <th width="30%">JScript</th>
            <th width="30%">VBScript</th>
            <th width="40%">匹配</th>
        </tr>
        <tr vAlign=top>
            <td width="30%">/^\[ \t]*$/</td>
            <td width="30%">"^\[ \t]*$"</td>
            <td width="40%">匹配一个空白行。</td>
        </tr>
        <tr vAlign=top>
            <td width="30%">/\d{2}-\d{5}/</td>
            <td width="30%">"\d{2}-\d{5}"</td>
            <td width="40%">验证一个ID 号码是否由一个2位数字，一个连字符以及一个5位数字组成。</td>
        </tr>
        <tr vAlign=top>
            <td width="30%">/&lt;(.*)&gt;.*&lt;\/\1&gt;/</td>
            <td width="30%">"&lt;(.*)&gt;.*&lt;\/\1&gt;"</td>
            <td width="40%">匹配一个 HTML 标记。</td>
        </tr>
    </tbody>
</table>
<br>
<p>下表是元字符及其在正则表达式上下文中的行为的一个完整列表：</p>
<table cellSpacing=0 cols=2 rules=all border=1 frame=box>
    <tbody>
        <tr vAlign=top>
            <th width="16%">字符</th>
            <th width="84%">描述</th>
        </tr>
        <tr vAlign=top>
            <td width="16%">\</td>
            <td width="84%">将下一个字符标记为一个特殊字符、或一个原义字符、或一个 后向引用、或一个八进制转义符。例如，'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">^</td>
            <td width="84%">匹配输入字符串的开始位置。如果设置了 <strong>RegExp</strong> 对象的 <strong>Multiline</strong> 属性，^ 也匹配 '\n' 或 '\r' 之后的位置。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">$</td>
            <td width="84%">匹配输入字符串的结束位置。如果设置了<strong>RegExp</strong> 对象的 <strong>Multiline</strong> 属性，$ 也匹配 '\n' 或 '\r' 之前的位置。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">*</td>
            <td width="84%">匹配前面的子表达式零次或多次。例如，zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">+</td>
            <td width="84%">匹配前面的子表达式一次或多次。例如，'zo+' 能匹配 "zo" 以及 "zoo"，但不能匹配 "z"。+ 等价于 {1,}。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">?</td>
            <td width="84%">匹配前面的子表达式零次或一次。例如，"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">{<em>n</em>}</td>
            <td width="84%"><em>n</em> 是一个非负整数。匹配确定的 <em>n</em> 次。例如，'o{2}' 不能匹配 "Bob" 中的 'o'，但是能匹配 "food" 中的两个 o。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">{<em>n</em>,}</td>
            <td width="84%"><em>n</em> 是一个非负整数。至少匹配<em>n</em> 次。例如，'o{2,}' 不能匹配 "Bob" 中的 'o'，但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">{<em>n</em>,<em>m</em>}</td>
            <td width="84%"><em>m</em> 和 <em>n</em> 均为非负整数，其中<em>n</em> &lt;= <em>m</em>。最少匹配 <em>n</em> 次且最多匹配 <em>m</em> 次。刘， "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">?</td>
            <td width="84%">当该字符紧跟在任何一个其他限制符 (*, +, ?, {<em>n</em>}, {<em>n</em>,}, {<em>n</em>,<em>m</em>}) 后面时，匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串，而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如，对于字符串 "oooo"，'o+?' 将匹配单个 "o"，而 'o+' 将匹配所有 'o'。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">.</td>
            <td width="84%">匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符，请使用象 '[.\n]' 的模式。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">(<em>pattern</em>)</td>
            <td width="84%">匹配<em>pattern</em> 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到，在VBScript 中使用 <strong>SubMatches</strong> 集合，在JScript 中则使用 <strong>$0</strong>&#8230;<strong>$9</strong> 属性。要匹配圆括号字符，请使用 '\(' 或 '\)'。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">(?:<em>pattern</em>)</td>
            <td width="84%">匹配 <em>pattern</em> 但不获取匹配结果，也就是说这是一个非获取匹配，不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如， 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">(?=<em>pattern</em>)</td>
            <td width="84%">正向预查，在任何匹配 <em>pattern</em> 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。例如， 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ，但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符，也就是说，在一个匹配发生后，在最后一次匹配之后立即开始下一次匹配的搜索，而不是从包含预查的字符之后开始。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">(?!<em>pattern</em>)</td>
            <td width="84%">负向预查，在任何不匹配Negative lookahead matches the search string at any point where a string not matching <em>pattern</em> 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows"，但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符，也就是说，在一个匹配发生后，在最后一次匹配之后立即开始下一次匹配的搜索，而不是从包含预查的字符之后开始 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%"><em>x</em>|<em>y</em></td>
            <td width="84%">匹配 <em>x</em> 或 <em>y</em>。例如，'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">[<em>xyz</em>]</td>
            <td width="84%">字符集合。匹配所包含的任意一个字符。例如， '[abc]' 可以匹配 "plain" 中的 'a'。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">[^<em>xyz</em>]</td>
            <td width="84%">负值字符集合。匹配未包含的任意字符。例如， '[^abc]' 可以匹配 "plain" 中的'p'。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">[<em>a-z</em>]</td>
            <td width="84%">字符范围。匹配指定范围内的任意字符。例如，'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">[^<em>a-z</em>]</td>
            <td width="84%">负值字符范围。匹配任何不在指定范围内的任意字符。例如，'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\b</td>
            <td width="84%">匹配一个单词边界，也就是指单词和空格间的位置。例如， 'er\b' 可以匹配"never" 中的 'er'，但不能匹配 "verb" 中的 'er'。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\B</td>
            <td width="84%">匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er'，但不能匹配 "never" 中的 'er'。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\c<em>x</em></td>
            <td width="84%">匹配由<em>x</em>指明的控制字符。例如， \cM 匹配一个 Control-M 或回车符。 <em>x</em> 的值必须为 A-Z 或 a-z 之一。否则，将 c 视为一个原义的 'c' 字符。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\d</td>
            <td width="84%">匹配一个数字字符。等价于 [0-9]。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\D</td>
            <td width="84%">匹配一个非数字字符。等价于 [^0-9]。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\f</td>
            <td width="84%">匹配一个换页符。等价于 \x0c 和 \cL。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\n</td>
            <td width="84%">匹配一个换行符。等价于 \x0a 和 \cJ。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\r</td>
            <td width="84%">匹配一个回车符。等价于 \x0d 和 \cM。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\s</td>
            <td width="84%">匹配任何空白字符，包括空格、制表符、换页符等等。等价于 [&nbsp;\f\n\r\t\v]。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\S</td>
            <td width="84%">匹配任何非空白字符。等价于 [^&nbsp;\f\n\r\t\v]。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\t</td>
            <td width="84%">匹配一个制表符。等价于 \x09 和 \cI。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\v</td>
            <td width="84%">匹配一个垂直制表符。等价于 \x0b 和 \cK。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\w</td>
            <td width="84%">匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\W</td>
            <td width="84%">匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\x<em>n</em></td>
            <td width="84%">匹配 <em>n</em>，其中 <em>n</em> 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如， '\x41' 匹配 "A"。'\x041' 则等价于 '\x04' &amp; "1"。正则表达式中可以使用 ASCII 编码。.</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\<em>num</em></td>
            <td width="84%">匹配 <em>num</em>，其中 <em>num</em> 是一个正整数。对所获取的匹配的引用。例如，'(.)\1' 匹配两个连续的相同字符。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\<em>n</em></td>
            <td width="84%">标识一个八进制转义值或一个后向引用。如果 \<em>n</em> 之前至少 <em>n</em> 个获取的子表达式，则 <em>n</em> 为后向引用。否则，如果 <em>n</em> 为八进制数字 (0-7)，则 <em>n</em> 为一个八进制转义值。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\<em>nm</em></td>
            <td width="84%">标识一个八进制转义值或一个后向引用。如果 \<em>nm</em> 之前至少有is preceded by at least <em>nm</em> 个获取得子表达式，则 <em>nm</em> 为后向引用。如果 \<em>nm</em> 之前至少有 <em>n</em> 个获取，则 <em>n</em> 为一个后跟文字 <em>m </em>的后向引用。如果前面的条件都不满足，若&nbsp; <em>n</em> 和 <em>m</em> 均为八进制数字 (0-7)，则 \<em>nm</em> 将匹配八进制转义值 <em>nm</em>。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\<em>nml</em></td>
            <td width="84%">如果 <em>n</em> 为八进制数字 (0-3)，且 <em>m</em> 和 <em>l</em> 均为八进制数字 (0-7)，则匹配八进制转义值 <em>nml。</em></td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\u<em>n</em></td>
            <td width="84%">匹配 <em>n</em>，其中 <em>n</em> 是一个用四个十六进制数字表示的 Unicode 字符。例如， \u00A9 匹配版权符号 (?)。</td>
        </tr>
    </tbody>
</table>
<br>
<h2><a name=reconorderofprecedence></a>&nbsp;</h2>
<h2><font size=3>正则表达式的优先权顺序</font></h2>
<p>在构造正则表达式之后，就可以象数学表达式一样来求值，也就是说，可以从左至右并按照一个优先权顺序来求值。 </p>
<p>下表从最高优先级到最低优先级列出各种正则表达式操作符的优先权顺序：</p>
<table cellSpacing=0 cols=2 rules=all border=1 frame=box>
    <tbody>
        <tr vAlign=top>
            <th width="40%">操作符</th>
            <th width="60%">描述</th>
        </tr>
        <tr vAlign=top>
            <td width="40%">\</td>
            <td width="60%">转义符</td>
        </tr>
        <tr vAlign=top>
            <td width="40%">(), (?:), (?=), []</td>
            <td width="60%">圆括号和方括号</td>
        </tr>
        <tr vAlign=top>
            <td width="40%">*, +, ?, {n}, {n,}, {n,m}</td>
            <td width="60%">限定符</td>
        </tr>
        <tr vAlign=top>
            <td width="40%">^, $, \<em>anymetacharacter</em></td>
            <td width="60%">位置和顺序</td>
        </tr>
        <tr vAlign=top>
            <td width="40%">|</td>
            <td width="60%">&#8220;或&#8221;操作</td>
        </tr>
    </tbody>
</table>
<!--copyright_start--><br>
<img src ="http://www.blogjava.net/zhangheng/aggbug/124313.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-06-14 14:12 <a href="http://www.blogjava.net/zhangheng/archive/2007/06/14/124313.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>微软的正则表达式教程（三）：字符匹配 </title><link>http://www.blogjava.net/zhangheng/archive/2007/06/14/124311.html</link><dc:creator>siwei</dc:creator><author>siwei</author><pubDate>Thu, 14 Jun 2007 06:11:00 GMT</pubDate><guid>http://www.blogjava.net/zhangheng/archive/2007/06/14/124311.html</guid><wfw:comment>http://www.blogjava.net/zhangheng/comments/124311.html</wfw:comment><comments>http://www.blogjava.net/zhangheng/archive/2007/06/14/124311.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhangheng/comments/commentRss/124311.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhangheng/services/trackbacks/124311.html</trackback:ping><description><![CDATA[<p>普通字符由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符，所有数字，所有标点符号以及一些符号。 </p>
<p>最简单的正则表达式是一个单独的普通字符，可以匹配所搜索字符串中的该字符本身。例如，单字符模式 'A' 可以匹配所搜索字符串中任何位置出现的字母 'A'。这里有一些单字符正则表达式模式的示例：</p>
<pre><code>/a/ /7/ /M/</code></pre>
<p>等价的 VBScript 单字符正则表达式为：</p>
<pre><code>"a" "7" "M"</code></pre>
<p>可以将多个单字符组合在一起得到一个较大的表达式。例如，下面的 JScript 正则表达式不是别的，就是通过组合单字符表达式 'a'、'7'以及 'M' 所创建出来的一个表达式。 </p>
<pre><code>/a7M/</code></pre>
<p>等价的 VBScript 表达式为：</p>
<pre><code>"a7M"</code></pre>
<p>请注意这里没有连接操作符。所需要做的就是将一个字符放在了另一个字符后面。</p>
<h2><a name=reconspecialcharacters></a><font size=3>特殊字符</font></h2>
<p>有不少元字符在试图对其进行匹配时需要进行特殊的处理。要匹配这些特殊字符，必须首先将这些字符转义，也就是在前面使用一个反斜杠 (\)。下表给出了这些特殊字符及其含义：</p>
<p>
<table cellSpacing=0 cols=2 rules=all border=1 frame=box>
    <tbody>
        <tr vAlign=top>
            <th width="16%">特殊字符</th>
            <th width="84%">说明</th>
        </tr>
        <tr vAlign=top>
            <td width="16%">$</td>
            <td width="84%">匹配输入字符串的结尾位置。如果设置了 <strong>RegExp</strong> 对象的 <strong>Multiline</strong> 属性，则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身，请使用 \$。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">( )</td>
            <td width="84%">标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符，请使用 \( 和 \)。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%"><strong>*</strong></td>
            <td width="84%">匹配前面的子表达式零次或多次。要匹配 * 字符，请使用 \*。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%"><strong>+</strong></td>
            <td width="84%">匹配前面的子表达式一次或多次。要匹配 + 字符，请使用 \+。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%"><strong>.</strong></td>
            <td width="84%">匹配除换行符 \n之外的任何单字符。要匹配 .，请使用 \。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">[ </td>
            <td width="84%">标记一个中括号表达式的开始。要匹配 [，请使用 \[。 </td>
        </tr>
        <tr vAlign=top>
            <td width="16%">?</td>
            <td width="84%">匹配前面的子表达式零次或一次，或指明一个非贪婪限定符。要匹配 ? 字符，请使用 \?。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\</td>
            <td width="84%">将下一个字符标记为或特殊字符、或原义字符、或后向引用、或八进制转义符。例如， 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\"，而 '\(' 则匹配 "("。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">^</td>
            <td width="84%">匹配输入字符串的开始位置，除非在方括号表达式中使用，此时它表示不接受该字符集合。要匹配 ^ 字符本身，请使用 \^。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">{</td>
            <td width="84%">标记限定符表达式的开始。要匹配 {，请使用 \{。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">|</td>
            <td width="84%">指明两项之间的一个选择。要匹配 |，请使用 \|。</td>
        </tr>
    </tbody>
</table>
<br><!--copyright_start--></p>
<h2><a name=reconnon></a><font size=3>非打印字符</font></h2>
<p>有不少很有用的非打印字符，偶尔必须使用。下表显示了用来表示这些非打印字符的转义序列：</p>
<p>
<table cellSpacing=0 cols=2 rules=all border=1 frame=box>
    <tbody>
        <tr vAlign=top>
            <th width="16%">字符</th>
            <th width="84%">含义</th>
        </tr>
        <tr vAlign=top>
            <td width="16%">\c<em>x</em></td>
            <td width="84%">匹配由<em>x</em>指明的控制字符。例如， \cM 匹配一个 Control-M 或回车符。 <em>x</em> 的值必须为 A-Z 或 a-z 之一。否则，将 c 视为一个原义的 'c' 字符。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\f</td>
            <td width="84%">匹配一个换页符。等价于 \x0c 和 \cL。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\n</td>
            <td width="84%">匹配一个换行符。等价于 \x0a 和 \cJ。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\r</td>
            <td width="84%">匹配一个回车符。等价于 \x0d 和 \cM。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\s</td>
            <td width="84%">匹配任何空白字符，包括空格、制表符、换页符等等。等价于 [&nbsp;\f\n\r\t\v]。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\S</td>
            <td width="84%">匹配任何非空白字符。等价于 [^&nbsp;\f\n\r\t\v]。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\t</td>
            <td width="84%">匹配一个制表符。等价于 \x09 和 \cI。</td>
        </tr>
        <tr vAlign=top>
            <td width="16%">\v</td>
            <td width="84%">匹配一个垂直制表符。等价于 \x0b 和 \cK。</td>
        </tr>
    </tbody>
</table>
<br><!--copyright_start--></p>
<h2>字符匹配</h2>
<p>句点 (.) 匹配一个字符串中任何单个的打印或非打印字符，除了换行符 (\n) 之外。下面的 JScript 正则表达式可以匹配 'aac'、'abc'、'acc'、'adc'如此等等，同样也可以匹配 'a1c'、'a2c'、a-c'以及 a#c'： </p>
<pre><code>/a.c/</code></pre>
<p>等价的 VBScript 正则表达式为：</p>
<pre><code>"a.c"</code></pre>
<p>如果试图匹配一个包含文件名的字符串，其中句点 (.) 是输入字符串的一部分，则可以在正则表达式中的句点前面加上一个反斜杠 (\) 字符来实现这一要求。举例来说，下面的 JScript 正则表达式就能匹配 'filename.ext'：</p>
<pre><code>/filename\.ext/</code></pre>
<p>对 VBScript 而言，等价的表达式如下所示：</p>
<pre><code>"filename\.ext"</code></pre>
<p>这些表达式仍然是相当有限的。它们只允许匹配<em>任何</em>单字符。很多情况下，对从列表中匹配特殊字符十分有用。例如，如果输入文字中包含用数字表示为Chapter 1, Chapter 2诸如此类的章节标题，你可能需要找到这些章节标题。 </p>
<h3>括号表达式</h3>
<p>可以在一个方括号 ([ 和 ]) 中放入一个或多个单字符，来创建一个待匹配的列表。如果字符被放入括号中括起来，则该列表称为<em>括号表达式</em>。括号内和其他任何地方一样，普通字符代表其本身，也就是说，它们匹配输入文字中出现的一处自己。大多数特殊字符在位于括号表达式中时都将失去其含义。这里有一些例外：
<ul type=disc>
    <li>']' 字符如果不是第一项，则将结束一个列表。要在列表中匹配 ']' 字符，请将其放在第一项，紧跟在开始的 '[' 后面。
    <li>'\' 仍然作为转义符。要匹配 '\' 字符，请使用 '\\'。 </li>
</ul>
<p>括号表达式中所包含的字符只匹配该括号表达式在正则表达式中所处位置的一个单字符。下面的 JScript 正则表达式可以匹配 'Chapter 1'、'Chapter 2'、'Chapter 3'、'Chapter 4' 以及 'Chapter 5'：</p>
<pre><code>/Chapter [12345]/</code></pre>
<p>在 VBScript 中要匹配同样的章节标题，请使用下面的表达式：</p>
<pre><code>"Chapter [12345]"</code></pre>
<p>请注意单词 'Chapter' 及后面的空格与括号内的字符的位置关系是固定的。因此，括号表达式只用来指定满足紧跟在单词 'Chapter' 和一个空格之后的单字符位置的字符集合。这里是第九个字符位置。</p>
<p>如果希望使用范围而不是字符本身来表示待匹配的字符，则可以使用连字符将该范围的开始和结束字符分开。每个字符的字符值将决定其在一个范围内的相对顺序。下面的 JScript 正则表达式包含了一个等价于上面所示的括号列表的范围表达式。</p>
<pre><code>/Chapter [1-5]/</code></pre>
<p>VBScipt 中相同功能的表达式如下所示：</p>
<pre><code>"Chapter [1-5]"</code></pre>
<p>如果以这种方式指定范围，则开始和结束值都包括在该范围内。有一点特别需要注意的是，在 Unicode 排序中起始值一定要在结束值之前。</p>
<p>如果想在括号表达式中包括连字符，则必须使用下述方法之一：
<ul type=disc>
    <li>使用反斜杠将其转义：
    <pre><code>[\-]</code></pre>
    <li>将连字符放在括号列表的开始和结束位置。下面的表达式能匹配所有的小写字母和连字符：
    <pre><code>[-a-z] [a-z-]</code></pre>
    <li>创建一个范围，其中开始字符的值小于连字符，而结束字符的值等于或大于连字符。下面两个正则表达式都满足这一要求：
    <pre><code>[!--] [!-~]</code></pre>
    </li>
</ul>
<p>同样，通过在列表开始处放置一个插入符(^)，就可以查找所有不在列表或范围中的字符。如果该插入符出现在列表的其他位置，则匹配其本身，没有任何特殊含义。下面的 JScript 正则表达式匹配章节号大于 5 的章节标题：</p>
<pre><code>/Chapter [^12345]/</code></pre>
<p>对 VBScript 则使用：</p>
<pre><code>"Chapter [^12345]"</code></pre>
<p>在上面所示的示例中，表达式将匹配第九个位置处除1, 2, 3, 4, or 5 之外的任何数字字符。因此， 'Chapter 7' 为一个匹配，同样 'Chapter 9' 也是如此。 </p>
<p>上面的表达式可以使用连字符 (-) 表示。对 JScript 为：</p>
<pre><code>/Chapter [^1-5]/</code></pre>
<p>或者，对 VBScript 为：</p>
<pre><code>"Chapter [^1-5]"</code></pre>
<p>括号表达式的典型用法是指定对任何大写或小写字母字符或任何数字的匹配。下面的 JScript 表达式给出了这一匹配：</p>
<pre><code>/[A-Za-z0-9]/</code></pre>
<p>等价的 VBScript 表达式为：</p>
<pre><code>"[A-Za-z0-9]"</code></pre>
<!--copyright_start-->
<img src ="http://www.blogjava.net/zhangheng/aggbug/124311.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhangheng/" target="_blank">siwei</a> 2007-06-14 14:11 <a href="http://www.blogjava.net/zhangheng/archive/2007/06/14/124311.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>