关于作者

姓名:

性别:男

出生日期:--

地区:浙江-杭州

联系电话:

QQ:3636119婚否:保密
用户名:zhytblog
笔名:插队落户
地区: 浙江-杭州
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



访问统计:
文章个数:9
评论个数:18
留言条数:26




Powered by BlogDriver 2.1

插队落户的一亩三分地

 

欢迎访问插队落户的一亩三分地

文章

Net下Web开发的ViewState工作原理

如果您是个经验丰富的 ASP.NET 开发人员,一提起 ViewState ,您可能会不寒而栗,因为您想到的是大量通过“鸡尾酒吸管”吸入的 Base64 编码数据。除非采取步骤进行预防,否则大部分 ASP.NET 页面将有大量辅助数据被存储在一个名为 __VIEWSTATE 的隐藏字段中,多数情况下,甚至不需要这个字段。浏览用 ASP.NET 生成的您喜爱的站点,查看页面源代码,计算隐藏在 __VIEWSTATE 字段中的字符数。我尝试了一下,数量为 800 到 7,800 个字符。

当然, ViewState 在 ASP.NET 中有个重要的角色。如果使用恰当,它能够简化页面开发,改进用户与站点的交互。如果置之不理,它能够显著增加站点响应大小,在连接速度慢的情况下,使您的响应时间更加缓慢。ASP.NET 2.0 的发布带来了 ViewState 机制的一些改进,这使得 ViewState 使用更简单,又不会防碍站点性能。这些改进包括:减少编码数量,采用控件状态从内容中分离出行为状态,以及智能集成数据绑定控件。

ViewState 基本原理


在介绍 ASP.NET 2.0 ViewState 的改进之前,简要总结目前版本中 ViewState 的用途和实现是适宜的。 ViewState 为 ASP.NET 开发人员解决了一个特定问题 — 保留服务器端不形成元素的控件的状态。这很重要,因为 ASP.NET 中的大部分服务器端控件模型是根据这样一个假设生成的,那就是 — 如果用户回发到相同页面,所有控件保持其状态不变。也就是说,如果在处理请求期间修改任何控件的内容,任何后续 POST 请求回到相同页面时,您可以依赖于那些仍然存在的修改。作为一个活动的 ViewState 示例,尝试运行显示的页面。

每次按下 Submit 按钮时,_sum 范围值递增。因为 ViewState 在请求期间保持以前的值,因此它将从上一次显示的值开始,显示 1、2、3、4 等等。如果想知道 ViewState 不可用时发生的事情,尝试添加 enableviewstate='false' 作为范围元素的一个属性。因为以前的范围值在请求处理时没有传播,所以不论页面发布多少次,都将显示值为 1。

在 ASP.NET 中, ViewState 完成基于控件的编程模型。如果没有 ViewState ,一些控件(如文本框和下拉列表)在 POST 请求期间保持状态,而其他控件不保持,使用这些状态各异的控件记录一些特殊的情况是令人沮丧的体验。使用 ViewState ,开发人员能够专注于编程模型和用户界面,而不用担心状态保持。还能对 ViewState 进行哈希或加密,以防止用户篡改或解码。关于这个主题的更多信息,请参见在线书籍 Securing Your ASP.NET Application and Web Services 的第 19 章。

使用 ViewState 的另一个重要之处是在控件中发布服务器端更改事件。如果用户改变了文本框中的值或切换了下拉列表中的选定元素,您就能够注册一个事件处理程序,引发事件时,执行代码。这些控件比较其当前值与以前值,如果有任何过程预订更改事件,以前值隐式存储在 ViewState 中。如果禁用一个控件的 ViewState ,而您正在处理该控件的更改通知事件,因为总是假定以前值与窗体默认值相同,所以更改通知事件不会正确激发。

ViewState 问题


正如我早前指出的,在 ASP.NET 1.x 中, ViewState 有很多问题。默认情况下,它是启用的,除非您知道在不需要使用时找到并禁用它,否则它能显著增加页面呈现的数据量。当使用数据绑定控件时,所有控件都使用 ViewState 保存回发后的状态,这将变得异常痛苦。作为一个简单而生动的示例,

这个页面有一个 DataGrid 控件,该控件绑定对 pubs 数据库中 authors 表格进行简单查询的结果。如果运行这个页面(对连接字符串做出必要改正),查看 ViewState 字段,您可能吃惊地发现里面有超过 12,000 个字符。当查看页面内容,意识到显示浏览器中整个表格内容所需的实际字符数量大约是 1,600,您会更加吃惊。造成这个结果的原因之一是 ViewState 不仅编码数据,而且编码数据类型(元数据);同样,Base64 编码一般要增加大约 33 % 的空间系统开销。而且,大量的系统开销只是为了保留 POST 请求后的控件状态,这似乎与时间不成比例,必须竭尽全力避免发生。

很明显,如果您关心响应大小,那么决定何时禁用控件的 ViewState 是重要的。刚才的示例就是一个典型的情况,传播了 ViewState ,但是从未使用,这是优化站点的 ViewState 使用时建议采用的主要规则:如果每次请求页面时填充控件内容,禁用该控件的 ViewState 一般是安全(而明智)的。

另一方面,您可能决定利用 ViewState 保留控件状态这一实事,并且只在页面的首次 GET 请求时,填充控件内容(只是贯穿后续 POST 请求)。这省去了使用的任何后端数据源的往返行程。通过修改我的页面来利用这一结论,我更改了 OnLoad 方法,使它在填充 DataGrid 前检查 IsPostBack 标志,当然,有一些其他可能更好的选择,例如缓存服务器的查询结果,每次发出请求时重新绑定控件。由您权衡状态存储的位置和特定体系结构与应用程序的重要性。

您可能注意到我曾小心提过,如果每次请求页面时填充控件内容,那么禁用 ViewState “一般”是安全的。例外情况是,一些控件既使用 ViewState 保留行为,也保留一般状态。如前所述,下拉列表和文本框控件使用 ViewState 存储以前的值来正确发布服务器上的更改通知事件。同样地,DataGrid 类使用 ViewState 发布分页、编辑和排序事件。遗憾的是,如果您想要使用 DataGrid 中诸如排序、分页或编辑的功能,则不能禁用其 ViewState 。对于尝试生成快速有效站点的开发人员来说,服务器端控件 ViewState 的非全有即全无的状况,是 ASP.NET 1.x 的服务器端控件模型另人沮丧的一面。

ASP.NET 2.0 中的 ViewState 改进


既然我概述了这些问题,是时候讨论 ASP.NET 2.0 中的所有改进了。第一个是 ViewState 序列化时的总大小。在 ASP.NET 1.x 中,两个字符串进入 ViewState 缓冲区中的序列化如下所示:

<p<l<string1;>;l<string2;>>;>;

ASP.NET 1.x 中使用的 ViewState 序列化格式是元组格式,由三个一组的层次集合和使用大于号和小于号的序列对组成。大于号之前的字母代表存储对象的类型(t=triplet,p=pair,i=integer,l=ArrayList,等)。大于号和小于号内的每个子元素由分号分隔。这是有趣的序列化格式,有点像一个压缩的 XML。但是,如果您关心空间,那么它不是最有效的序列化格式(只是比 XML 稍好一点)。

ASP.NET 2.0 改变了这种序列化格式。在 ASP.NET 2.0 中,相同的两个字符串进入 ViewState 缓冲区的序列化如以下代码行所示:

[][]string1[]string2

至少,这很像浏览器呈现的格式。方括号实际上是非打印字符,如果我们使用 Unicode 字符引用来重写,则变成了以下显示的编码:

&#15;&#5;string1&#5;string2

ASP.NET 2.0 使用一些非打印字符标识对象的起始并表述对象的类型,而非使用可打印字符集(“<”、“>”、“;”、“l”、“i”、“p”等)来分割 ViewState 流中的对象。

使用非打印字符分割 ViewState 中存储的对象有两个目的。第一个目的是改进解析 ViewState 字符串时进行词法分析的效率,因为此时不再需要匹配字符或解析标记。第二个,也是更重要的目的是它减少了为 ViewState 中的对象编码所使用的字符数量。在为两个字符串编码的简单示例中,第一种编码方法使用了 16 个分割字符,而 2.0 格式使用了 3 个。这种作用很快地复合而产生了重大的影响。

例如,如果我们在窗体上放置一个 DataGrid,将其绑定到 pubs 数据库的 authors 表格,则 1.x 中的 ViewState 字符串大小是 12,648 个字符。如果在 2.0 中也这样做, ViewState 大小减少到 6,728 个字符,几乎减少了一半。图 4 显示 ASP.NET 1.x、ASP.NET 2.0 中 ViewState 所需的空间对比,以及不同输入大小在页面实际呈现的字符数。在这种情况下,authors 表格在 500 行处增加了额外的行,达到了 2,000 行。

随着 ASP.NET 2.0 版本的发布,您能够期望立即减小 ViewState 大小,而无需做任何特别的工作。这不意味着您应该停止考虑控件正常工作是否需要 ViewState ,这是因为它仍然对响应大小有很大影响,在图 4 中表现的很明显。然而, ViewState 的下一个改进能潜在地节省更多。

控件状态


我先前提过,ASP.NET 1.x 中使用服务器端控件最另人沮丧的一面是关于 ViewState 的非全有即全无的状态。控件的行为方面,如 DataGrid 中的分页或文本框中的选定更改通知,需要启用 ViewState 来正常运行。我确定在任何 DataGrid 中启用 ViewState 的前景令所有人十分沮丧。在 ASP.NET 2.0 中,Microsoft 通过将 ViewState 分割成两个独立不同的类别解决了这一特殊问题: ViewState 和控件状态。

控件状态是另一类隐藏的状态,专门为维护控件的核心行为功能而保留,而 ViewState 只包含维护控件内容 (UI) 的状态。从技术上来说,控件状态存储在与 ViewState 相同的隐藏字段中(只是在 ViewState 层次结构末端的另一个叶子节点),但是如果禁用一个特定控件或整个页面的 ViewState ,控件状态仍然传播。实现这一技术好的一面是,现在我们能够改进 ASP.NET 2.0 优化 ViewState 的原始原则,使之更强健:如果在每次请求页面时传播控件内容,您应该禁用该控件的 ViewState 。

只要控件生成器将其状态正确划分成行为状态(为了维护核心功能)和 UI 状态(为了保留内容),就应该坚定地遵守这个原则。我建议您现在使用 ASP.NET 2.0 时开始遵循这个原则,但是 DataGrid 还未重构以保留控件状态中的行为状态。幸运的是,新的 GridView 是 DataGrid 的逻辑继承者,它像 TextBox 和 DropDownList 控件的新版本一样正确使用控件状态。随着 ASP.NET 2.0 的最终发布,这个列表可能会作出变动。

如果您愿意进一步研究 ASP.NET 2.0 的新控件如何使用控件状态和 ViewState ,可以使用我编写的一个叫做 View State Decoder 的实用工具,它用来显示给定页面上所有的 ViewState 和控件状态。

对于您的那些生成控件,控件状态的使用模型不像 ViewState 一样方便。您必须重写 LoadControlState 和 SaveControlState 虚方法,手动管理您那部分映射到控件状态中的对象集合,而非提供一个带有索引的状态包用来插入和移除选项。另一件您必须做的事情是在初始化期间调用 Page.RegisterRequiresControlState 方法,使页面能够在正确的时间询问控件状态。在控件状态中存储选项比在 ViewState 中更难可能是一件好事,因为用户无法禁用它(控件状态有一个选择模型也有性能方面的好处)。开发人员在存储任何状态前应该认真考虑,因为它总是添加到呈现的页面中。当您决定控件状态和 ViewState 各存储什么内容时,请记住行为状态与内容或者 UI 状态应该有分别。诸如属性和数据集一样的内容一般应存储在 ViewState 中,而不是迁移到控件状态。触发服务器端事件的状态是存储在控件状态中最典型的一类状态。

返回页首声明性数据源和 ViewState


应用我们的 ViewState 优化原则有一个小问题 — 您经常不知道每次请求时是否正在填充控件内容。在 ASP.NET 2.0 中采用声明性数据源意味着将数据绑定到一个控件不再需要将数据源属性显式连接至 DataReader 或 DataSet,并调用 DataBind。而是在页面上声明性地放置一个数据源,并将控件指向该数据源。例如,使用绑定到 pubs 数据库中 authors 表格的新 GridView 类和与之相关的 SqlDataSource如果运行这个页面,您将发现它正好有效。GridView 及其相应的数据源能够了解何时进行交互。在页面呈现之前,GridView 充满数据源的数据,并且很可能在客户端呈现整个 authors 表格。

在这个简单的情况中,我们还未禁用 GridView 的 ViewState ,因此您可能会认为,我们再一次仅仅使用 ViewState 存储了从未使用的数据。幸运的是,ASP.NET 2.0 引擎作了正确的事情,当控件上的 ViewState 可用时,它将不厌其烦地返回到数据库。

同样地,如果禁用 GridView 上的 ViewState ,数据绑定到数据源将发生在每次请求发生的时候,包括 POST back 请求。DataBoundControl 基类中置入了这一功能,其中的 AdRotator、BulletedList、CheckBoxList、DropDownList、ListBox、RadioButtonList、GridView、DetailsView 和 FormView 控件继承了该功能。这些控件展示了 ViewState 在绑定到声明性数据源时表现的智能使用。

小结


ASP.NET 的下一个版本对 Web 开发人员承诺了许多改进,不只是更有可能晚上好好休息,不会做 Base64 编码数据淹没了 Web 窗体的噩梦。使用更紧凑的序列化格式、行为状态和 UI 状态的分离以及数据绑定控件和声明性数据源的智能交互,那些 ViewState 的噩梦仅可能变成美梦。

- 作者: 插队落户 2007年05月10日, 星期四 21:28  回复(5) |  引用(0) 加入博采

.net打包自动安装数据库
 一).创建部署项目
1. 在“文件”菜单上指向“添加项目”,然后选择“新建项目”。
2. 在“添加新项目”对话框中,选择“项目类型”窗格中的“安装和部署项目”,然后选择“模板”窗格中的“安装项目”。在“名称”框中键入 setup1。
3. 单击“确定”关闭对话框。
4. 项目被添加到解决方案资源管理器中,并且文件系统编辑器打开。
5. 在“属性”窗口中,选择 ProductName 属性,并键入 信息管理系统 。

二).将 主程序 项目的输出添加到部署项目中
1. 在“文件系统编辑器”中,选择“应用程序文件夹”。在“操作”菜单上,指向“添加”,然后选择“项目输出”。
2. 在“添加项目输出组”对话框中,选择“项目”下拉列表中的“你的程序”。
3. 单击“确定”关闭对话框。
4. 从列表中选择“主输出”和“内容文件”组,然后单击“确定”。

三).创建安装程序类
1. 在“文件”菜单上指向“新建”,然后选择“项目”。
2. 在“新建项目”对话框中,选择“项目类型”窗格中的“Visual Basic 项目”,然后选择“模板”窗格中的“类库”。在“名称”框中键入 installDB。
3. 单击“打开”关闭对话框。
4. 从“项目”菜单中选择“添加新项”。
5. 在“添加新项”对话框中选择“安装程序类”。在“名称”框中键入 installDB。
6. 单击“确定”关闭对话框。
7. 详细代码附后。

四).创建自定义安装对话框
1. 在解决方案资源管理器中选择“setup1”项目。在“视图”菜单上指向“编辑器”,然后选择“用户界面”。
2. 在用户界面编辑器中,选择“安装”下的“启动”节点。在“操作”菜单上,选择“添加对话框”。
3. 在“添加对话框”对话框中,选择“许可协议”对话框,然后单击“确定”关闭对话框。
4. 在“添加对话框”对话框中,选择“文本框 (A)”对话框,然后单击“确定”关闭对话框。
5. 在“操作”菜单上,选择“上移”。重复此步骤,直到“文本框 (A)”对话框位于“安装文件夹”节点之上。
6. 在“属性”窗口中,选择 BannerText 属性并键入:安装数据库.
7. 选择 BodyText 属性并键入:安装程序将在目标机器上安装数据库
8. 选择 Edit1Label 属性并键入:数据库名称:
9. 选择 Edit1Property 属性并键入 CUSTOMTEXTA1
10. 选择 Edit1Value 属性并键入:dbservers
11. 选择 Edit2Label 属性并键入:服务器名:
12. 选择 Edit2Property 属性并键入 CUSTOMTEXTA2
13. 选择 Edit2Value 属性并键入:(local)
14. 选择 Edit3Label 属性并键入:用户名:
15. 选择 Edit3Value 属性并键入:sa
16. 选择 Edit3Property 属性并键入 CUSTOMTEXTA3
17. 选择 Edit4Label 属性并键入:密码:
18. 选择 Edit4Property 属性并键入 CUSTOMTEXTA4
19. 选择 Edit2Visible、Edit3Visible 和 Edit4Visible 属性,并将它们设置为 true

五).创建自定义操作
1. 在解决方案资源管理器中选择“setup1”项目。在“视图”菜单上指向“编辑器”,然后选择“自定义操作”。
2. 在自定义操作编辑器中选择“安装”节点。在“操作”菜单上,选择“添加自定义操作”。
3. 在“选择项目中的项”对话框中,双击“应用程序文件夹”。
4. 选择“主输出来自 installDB(活动)”项,然后单击“确定”关闭对话框。
5. 在“属性”窗口中,选择 CustomActionData 属性并键入“/dbname=[CUSTOMTEXTA1] /server=[CUSTOMTEXTA2] /user=[CUSTOMTEXTA3] /pwd=[CUSTOMTEXTA4] /targetdir="[TARGETDIR]\"”。

附:/targetdir="[TARGETDIR]\"是安装后的目标路径,为了在installDB类中获得安装后的路径,我们设置此参数。
六).打包時加入卸载功能:
方法一:
1.在打包項目中添加文件msiexec.exe(一般可在c:\windows\system32\下找到)
2.在文件系統視圖中選擇應用程序文件夾,在msiexec.exe上按右鍵,選擇創建快捷方式,重命名快捷方式為"卸载".
3.更改此快捷方式的Arguments 为"/x {產品id}",產品id的值為打包項目的ProductCode屬性值.
方法二:(推荐)
1.先生成安装包,记下ProductCode(选择解决方案资源管理器根目录如setup1,再查看属性标签,不是右键中的属性),下面要用到
2.用VS.net建立一个新的控制台程序uninst.exe文件
'power by: landlordh
'for 2000,xp,2003
Module uninstall
Sub Main()
Dim myProcess As Process = New Process
If System.Environment.OSVersion.ToString.IndexOf("NT 5") Then
myProcess.Start("msiexec", "/X{2B65D4A9-C146-4808-AB4B-321FB0779559}") '改为自己的ProductCode
End If
myProcess.Close()
End Sub
End Module
3.将控制台程序BIN目录的exe文件加入到打包程序文件中,在程序组创建uninst.exe的快捷方式
installdb.vb类,要添加引用 system.configuration.install.dll :

using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.Reflection;
using System.IO;
using System.Data;
using System.Data.SqlClient;
namespace install
{
///


/// Installer1 的摘要说明。
///

[RunInstaller(true)]
public class Installer1 : System.Configuration.Install.Installer
{
///
/// 必需的设计器变量。
///

private System.ComponentModel.Container components = null;
public Installer1()
{
// 该调用是设计器所必需的。
InitializeComponent();
// TODO: 在 InitializeComponent 调用后添加任何初始化
}
///
/// 清理所有正在使用的资源。
///

protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region 组件设计器生成的代码
///


/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
///

private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
private string GetSql(string Name)
{
// //调用osql执行脚本
//
// System.Diagnostics.Process sqlProcess = new System.Diagnostics.Process();
//
// sqlProcess.StartInfo.FileName = "osql.exe";
//
// sqlProcess.StartInfo.Arguments = String.Format(" -U {0} -P {1} -d {2} -i {3}db.sql", this.Context.Parameters["user"], this.Context.Parameters["pwd"],"master", this.Context.Parameters["targetdir"]);
//
// sqlProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
//
// sqlProcess.Start();
//
// sqlProcess.WaitForExit() ;//等待执行
//
// sqlProcess.Close();
try
{
// Assembly Asm = Assembly.GetExecutingAssembly();
// System.IO.FileInfo FileInfo = new System.IO.FileInfo(Asm.Location);
// string path=FileInfo.DirectoryName @"\" Name;
string path=this.Context.Parameters["targetdir"] Name;
FileStream fs=new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.Read);
StreamReader reader = new StreamReader(fs,System.Text.Encoding.Default);
//System.Text.Encoding.ASCII;
return reader.ReadToEnd();
}
catch (Exception ex)
{
Console.Write("In GetSql:" ex.Message);
throw ex;
}
}
private void ExecuteSql(string DataBaseName,string Sql)
{
SqlConnection sqlConnection1=new SqlConnection();
sqlConnection1.ConnectionString =string.Format("server={0}; user id={1}; password={2}; Database=master",this.Context.Parameters["server"],this.Context.Parameters["user"],this.Context.Parameters["pwd"]);
System.Data.SqlClient.SqlCommand Command = new System.Data.SqlClient.SqlCommand(Sql,sqlConnection1);
try
{
Command.Connection.Open();
Command.Connection.ChangeDatabase(DataBaseName);

Command.ExecuteNonQuery();
}
catch(Exception ex)
{
Console.Write("In exception handler :" ex.Message);
}
finally
{
Command.Connection.Close();
}
}

protected void AddDBTable(string strDBName)
{
try
{
ExecuteSql("master","CREATE DATABASE " strDBName);
ExecuteSql(strDBName,GetSql("sql.txt"));
ExecuteSql("master","exec sp_addlogin 'myoamaster','myoamaster','" strDBName "',Null,Null");
ExecuteSql(strDBName,"EXEC sp_grantdbaccess 'myoamaster', 'myoamaster'");
ExecuteSql(strDBName,"exec sp_addrolemember 'db_owner','myoamaster'");
}
catch(Exception ex)
{
Console.Write("In exception handler :" ex.Message);
}
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
AddDBTable(this.Context.Parameters["dbname"]);
}
}
}
这里有个sql.txt是数据库的sql脚本,当然可以调用osql来执行sql脚本,其实是一样的。
打包的时候必须把sql.txt文件加进来,否则不会执行。
如果你想附加数据库的mdf文件和ldf文件,用下面这段程序:
private void CreateDataBase(string strSql,string DataName,string strMdf,string strLdf)
{
String str;
SqlConnection myConn = new SqlConnection (strSql);
//EXEC sp_detach_db @dbname = 'BX_FreightMileage_2'//需要先将数据库分离出来
str = "EXEC sp_attach_db @dbname = '" DataName "', @filename1 = '" strMdf "',@filename2='" strLdf "'";
SqlCommand myCommand = new SqlCommand(str, myConn);

myConn.Open();
myCommand.ExecuteNonQuery();
myConn.Close();

}
当然打包的时候也要把这两个数据库文件也加进来。

- 作者: 插队落户 2007年05月9日, 星期三 19:39  回复(1) |  引用(0) 加入博采

ASP.NET系统用户权限设计与实现

http://www.webjx.com  更新日期:2005-03-07 23:23  出处:网页教学网  作者:

  引言

    电子商务系统对安全问题有较高的要求,传统的访问控制方法DAC(Discretionary Access Control,自主访问控制模型)、MAC(Mandatory Access Control,强制访问控制模型)难以满足复杂的企业环境需求。因此,NIST(National Institute of Standards and Technology,美国国家标准化和技术委员会)于90年代初提出了基于角色的访问控制方法,实现了用户与访问权限的逻辑分离,更符合企业的用户、组织、数据和应用特征。ASP.NET是微软为了抗衡JSP而推出的新一代ASP(Active Server Pages)脚本语言,它借鉴了JSP的优点,同时它又具有自身的一些新特点。

    本文将首先介绍ASP.NET的基本情况和RBAC(Role Based Access Control)的基本思想,在此基础上,给出电子商务系统中实现用户权限控制的一种具体方法。

    ASP.NET概述

    1、ASP.NET

    ASP.NET是微软流行的动态WEB编程技术活动服务器网页(ASP)的最新版本,但它远不是传统ASP简单升级。ASP.NET和ASP的最大区别在于编程思维的转换,ASP.NET是真正的面向对象(Object-oriented),而不仅仅在于功能的增强。

    在ASP.NET中,Web 窗体页由两部分组成:视觉元素(HTML、服务器控件和静态文本)和该页的编程逻辑。其中每一部分都存储在一个单独的文件中。可视元素在一个扩展名为 .aspx 文件中创建,而代码位于一个单独的类文件中,该文件称作代码隐藏类文件扩展名为.aspx.vb 或 .aspx.cs。这样,.aspx文件中存放所有要显示的元素,aspx.vb或.aspx.cs文件中存放逻辑。

    2、用户控件(UserControl)

    为了使用户能够根据需要方便地定义控件,ASP.NET引入了 Web 窗体用户控件的概念。实际上,只要将.aspx稍作修改即可转换为 Web 用户控件,扩展名为 .ascx,.ascx和.aspx文件一样也有一个存放逻辑的代码隐藏类文件,扩展名为.ascx.vb或.ascx.cs,只是它不能作为独立 Web 窗体页来运行,只有当被包含在 .aspx文件中时,用户控件才能工作。

    通过以下两个步骤在WEB窗体页中设置用户控件:

    (1)使用@ Register指令在.aspx文件中注册用户控件。如要注册在放在相对路径“../UserControl/”下的头文件headinner.ascx的方法为:

<%@ Register TagPrefix="Acme" TagName="Head" Src="../UserControl/headinner.ascx" %>

    (2)在服务器控件的开始标记和结束标记之间(<form runat=server> </form>) 声明该用户控件元素。例如要声明上面所导入的控件的语法为:

<Acme: Head runat="server"/>

    这样,该控件就成为页的一部分,并将在处理该页时呈现出来。并且,该控件的公共属性、事件和方法将向 Web 窗体页公开并且可以通过编程来使用。根据这个原理,就可以将每个页面初始化时所要执行的操作(如登录验证,角色验证)封装在用户控件当中。

  RBAC的基本思想

    RBAC(角色访问控制)的基本思想可简单地用图1来表示,即把整个访问控制过程分成两步:访问权限与角色相关联,角色再与用户关联,从而实现了用户与访问权限的逻辑分离。

    由于RBAC实现了用户与访问权限的逻辑分离,因此它极大的方便了权限管理。例如,如果一个用户的职位发生变化,只要将用户当前的角色去掉,加入代表新职务或新任务的角色即可,角色/权限之间的变化比角色/用户关系之间的变化相对要慢得多,并且委派用户到角色不需要很多技术,可以由行政管理人员来执行,而配置权限到角色的工作比较复杂,需要一定的技术,可以由专门的技术人员来承担,但是不给他们委派用户的权限,这与现实中情况正好一致。

    用户权限在.NET中的设计与实现

    利用.NET中的用户控件实现权限控制的基本思想是:根据角色访问控制(RBAC)的基本原理,给用户分配一个角色,每个角色对应一些权限,然后利用ASP.NET中的用户控件(UserControl)来判断该用户对应的角色是否对访问页面有访问的权力。

    下面将从数据库设计、添加角色和用户控件的使用等三方面来阐述具体实现过程。

    1、数据库中表的设计

    首先,在数据库中设计功能模块表、功能表和角色表等三个表。

    (1) 功能模块表

    为了管理好用户的权限,首先要组织好系统的模块,为此设计了一个功能模块表。见表1。

    (2) 功能表

    每个功能模块所具有的子功能称为功能,如商品管理模块goods(属于功能模块的范畴)包含商品信息查询、商品信息更新、商品信息删除、商品定价信息查询以及商品定价信息更新五种功能,功能表的设计见表2。

    上面提到的例子可以作为这样几条记录分别插入功能模块表和功能表。

 

insert into TModule values(0,\\\\\\\\\\\\\\\'商品管理模块\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\',\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'goods\\\\\\\\\\\\\\',5);
insert into Tfunction values(0,\\\\\\\\\\\\\\'商品信息查询\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'selectgoods\\\\\\\\\\\\\\\\',0);
insert into Tfunction values(1,\\\\\\\\\\\\\\\'商品信息更新\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\',\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'updategoods\\\\\\\\\\\\\\',0);
insert into Tfunction values(2,\\\\\\\\\\\\\\\\\'商品信息删除\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\',\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'deletegoods\\\\\\\\\\\\',0);
insert into Tfunction values(3,\\\\\\\\\\\\\\\\\\'商品定价信息查询\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\',\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'selectgoodsprice\\\\\\\',0);
insert into Tfunction values(4,\\\\\\\\\\\\\\\\\'商品定价信息更新\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\',\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'updategoodsprice\\\\\\\\\\\',0);

    (3) 角色表

    角色表的设计关键在于角色值的定义,它是一个由0和1组成的类似二进制数的字符串。而功能表中的funcNo (功能编号)字段表示该功能在角色表的roleValue (角色值)字段中的位置,如果该位置对应的数值是0,表示该角色无此权限,如果值为1,则表示该角色拥有此权限。如角色普通会员的角色值为100100…00(共100位),如上所示,商品信息查询的功能编号为0,角色值100100…00的第0位为1,所以该普通会员角色拥有商品信息查询的功能;相反,该角色值的第1位为0,而功能编号为1 的功能为商品信息更新,所以该普通会员角色没有商品信息更新的权限。它们的关系可由图2来表示。

    2、角色的添加

    有了上面几个表,角色页面的功能模块以及其对应的功能都可以从功能模块表和功能表中读出,如图3所示。

 

    在将新角色普通会员插入数据库时,先将角色值的所有位都置为0,然后利用.NET Framework 类库中的Replace函数将角色值中的打上勾的功能相应的功能编号位的值改为1。

    例如,新添加一个角色名为普通会员的角色,它拥有的功能为商品信息查询(功能编号0)和商品定价信息查询(功能编号3)两项,则角色值应为1001000……00(100位),即角色值中第0位和第3位的值为1,其余为0。

    3、利用用户控件实现访问权限

    在定义好用户控件.ascx文件(head.ascx)及.ascx.cs(head.ascx,cs)文件时,接下去只要在.aspx文件中注册和声明它就可以了。

    (1) 注册

<%@ Register TagPrefix="Acme" TagName="Head" Src="../UserControl/headinner.ascx" %>

    (2) 声明

    经过实践,在.aspx文件中声明.ascx文件可分为几种情况:

  第一种情况:<Acme:Head runat="server" />

  第二种情况:<Acme:Head runat="server" flag=0 funcname1=selectgoods funcname2=updategoods />

  第三种情况:<Acme: Head runat="server" flag=1 funcname1= selectgoods funcname2=updategoods />

    字段flag是用来控制怎样进行权限检查的标志,funcname指功能表中的功能英文名。如果flag为空,则不执行权限检查(第一种情况);否则如果flag=="0",则表示同时具有selectgoods(商品信息查询)和 updategoods(商品信息更新)这两种权限的角色所对应的用户才有权利查看该页(第二种情况);否则,如果flag=="1",则认为,具有selectgoods(商品信息查询)或 updategoods(商品信息更新)这两种权限中任意一种权限的用户就有权利查看该页(第三种情况)。

    上面进行权限检查的过程全部由用户控件来实现,其全部方法都封装在.ascx.cs文件中,其中最主要的一个方法是检查某一角色是否拥有某一确定权限的checkAuth(string roleId,string funcEName)方法。这个方法的思想如图4所示。

    图4中roleValue(角色值)的第0位(selectgoods的功能编号)值为1,表示该角色拥有selectgoods(商品信息查询)的权限。这样,我们把对权限检查的所有逻辑都封装在了用户控件中,因此,对WEB窗体页.aspx文件而言,只需在导入.ascx文件时确定用户在访问该页面时所应拥有的权限,而不需对aspx.cs进行任何改动。

    由上所述,可以很清楚地看出,只要在用户控件中对用户权限进行控制,再把它包括在.aspx文件中(这件事作者本来就是要做的),那么在编程的时候就不必考虑复杂的权限问题了。

- 作者: 插队落户 2007年03月13日, 星期二 22:38  回复(0) |  引用(0) 加入博采

(转)为什么VB.net的Shared(共享)方法在C#中叫Static(静态)?

文中所有内容均代表本人对问题的理解,可能与实际有所差别!文中C语言代码的调试环境为MyTc 5.4.1,C#代码调试环境为VS.NET 2003。

为什么VB.net的Shared(共享)方法在C#中叫Static(静态)? 这个问题看起来很愚蠢,但是透彻的了解它确需要掌握面向对象程序设计语言中深层次、本质上的内容。本文将通过以下几个层面的分析深入剖析隐藏在Shared与Static背后的究竟是什么。

另外本题目仅仅是个引子,本文除了讨论静态外,同时还要更多的讨论"动态"方法(Object Method或Instance Method),并试图揭示面向对象的本质。

一、C#中的Static方法为什么在VB.net中叫Shared方法

1、封装

下面的案例摘自微软课程《Course 2124C: Programming with C#》。

继承、封装、多态性可以说是面向对象程序设计的三大特征。我们先来看看封装这个概念。在传统的C语言程序设计中是没有封装一说的(注意:这里暂不谈C语言中的指针使用),如果两个函数需要对同一个变量进行操作的话,不得不将该变量声明为公有,但这一来就不能排除其它人不通过正当渠道而直接修改公有数据,如下图左边所示:

我们通过Deposit方法与Withdraw方法完成存钱与取钱的动作,进而修改帐户余额balance。然而公有的balance变量允许用户不经存、取钱动作就可以直接修改帐户余额, 给程序带来了很大的风险。如果用C语言实现的话,代码可以表示为:

#include 
double balance = 0.0;
main()
{
   Deposit(10000.0);
   Withdraw(2000.0);
   ShowBalance();
   balance = 999999999.9;
   ShowBalance();
   printf("Oh! my God!\n");
}
Withdraw(double amount)
{
   balance = balance - amount;
}
Deposit(double amount)
{
   balance = balance + amount;
}
ShowBalance()
{
   printf("%12.2f\n", balance);
}

面向对象语言的出现引入了封装的概念,通过构筑一个“胶囊”,胶囊的边界将空间划分为“内”和“外”,将数据放到里面作为私有数据,而将方法提供给外面以供调用,这样就有效保护了数据,防止非法访问。这个“胶囊”就是class。如上图右边所示。用C#实现的代码如下:

using System ;
public class Account
{
   private double balance = 0;
   public void Withdraw(double amount)
   {
      balance -= amount;
   }
   public void Deposit(double amount)
   {
      balance += amount;
   }
   public void ShowBalance()
   {
      Console.WriteLine("Current balance is {0}", balance);
   }
}
public class Client
{
   public static void Main()
   {
      Account account1 = new Account();
      Account account2 = new Account();
      account1.Deposit(10000);
      account2.Deposit(7000);
      account1.ShowBalance();
      account2.ShowBalance();
   }
}

2、实例(Instance)

帐户类仅仅定义了一个模子,不同人应当拥有自身的帐户以及各自的存取记录和帐户余额。在这里每个人的帐户就是帐户类的一个实例。实例和实例间的数据是不同的,如果两个人的帐户余额相同纯属偶然。如上面代码所示,我们通过new命令创建一个帐户类的实例。不同实例间(account1与account2)可以拥有不同的帐户余额。

3、Shared的起源

实例和实例之间往往需要共享一些数据(例如存款利率),将这些共享数据存放在每一个实例中显然不是什么好的办法,我们需要将它们抽取出来单独存放并被所有实例所“共享”,这就是Shared的来源,在C#语言中叫做Static。如下图所示:

图中的Shared与非Shared的代码和数据是分开的,然而在编写程序时,我们需要将它们写在同一个类里面,但它们在运行时却有着不同的表现,初学者一定要注意这一点。C#代码实现如下:

using System ;
public class Account
{
   private double balance = 0;
   private static double interest = 0.07;
   public void Withdraw(double amount)
   {
      balance -= amount;
   }
   public void Deposit(double amount)
   {
      balance += amount;
   }
   public static double InterestRate()
   {
      return interest;
   }
   public void ShowBalance()
   {
      Console.WriteLine("Current balance is {0}", balance);
   }
}
public class Client
{
   public static void Main()
   {
      Account account1 = new Account();
      Account account2 = new Account();
      account1.Deposit(10000);
      account2.Deposit(7000);
      account1.ShowBalance();
      account2.ShowBalance();
      Console.WriteLine("InterestRate is: {0}", Account.InterestRate());
   }
}

对于这类方法和属性我们在前面标示上Shared(VB.net)或static(C#),当看到这些标示后,我们就知道这些内容是用来被所有实例所共享的。这就算是Shared的起源吧。

4、另外一个案例

让我们再来举另外一个例子。假如有一学生类。有两个方法:自报姓名以及火车票打折。我们可以看到报姓名的方法是实例方法,因为不同人有不同的姓名,而火车票打折是对所有学生而言的,因此应当属于共享的方法。代码如下:

using System ;
public class Student
{
   private string name;
   public Student(string name)
   {
      this.name = name;
   }
   public void TellName()
   {
      Console.WriteLine("My name is: {0}", name);
   }
   public static double CalcTicketDiscount(double price)
   {
      return price/2;
   }
}
public class Client
{
   public static void Main()
   {
      Student stu1 = new Student("Tom");
      Student stu2 = new Student("Jim");
      
      stu1.TellName();
      stu2.TellName();
      
      Console.WriteLine("Discount: {0:C2}",Student.CalcTicketDiscount(100));
   }
}

5、问题

从上面的分析可以看出,Shared在描述问题上似乎更有道理、更容易理解,那么为什么在C#语言中没有使用shared一词,反而使用了static(静态)呢? 这里的静态又是指什么?我们将在下一部分内容中加以分析。

二、VB.net中的Shared方法为什么在C#中叫Static方法

1、"静态"是什么?

我们在C#中所说的静态和传统C语言中的静态其实不是一个概念。C语言中的静态指当多次进入某一方法时,该方法中的静态变量能够保持上次调用时的值。举例来说,下面的C语言程序的打印结果是1、2。当第一次调用sub后,静态变量i中的值被保留了下来。

#include 
main()
{
   sub();
   sub();
}
sub()
{
   static int i = 0;
   i++;
   printf("%d\n", i);
}

而C#中的静态则不是这个概念。它往往指“方法”或“属性”是固定的、不会发生变化的、编译时就能确定下来的。这似乎很难懂,我们还是看看以下两个例子。

第一个例子是C语言的例子,在C语言中,函数与函数不能出现重名,因此当告知调用的函数名时,这个函数是什么,代码在什么地方就可以唯一的确定下来,不会有任何的歧义。C#中管这种唯一能够确定代码位置的,不会产生任何歧义的方法叫做静态方法。

#include 
main()
{
   sub1();
}
sub1()
{
   printf("This is sub1");
}
sub2()
{
   printf("This is sub2");
}

上面的例子中,当主程序发出sub1方法的调用命令后,我们可以唯一的确定该方法在什么地方,去哪里执行代码,因此我们说sub1方法是“静态”的。简直是废话!不过不要着急,在C#中情况就不同了,当你调用某一方法时,你可能根本就不知到该方法在什么地方,会执行什么样的代码。

2、“多态”中所体现出来的动态

我们看下面的案例:

using System;
public abstract class Light
{
   public abstract void TurnOn();
}
public class BulbLight : Light
{
   public override void TurnOn()
   { Console.WriteLine("Bulb Light is turned on!"); }
}
public class TubeLight : Light
{
   public override void TurnOn()
   { Console.WriteLine("Tube Light is turned on!"); }
}
public class Client
{
   public static void Main()
   {
      Light light;
      light = new BulbLight();
      TurnOnLight(light);
      
      light = new TubeLight();
      TurnOnLight(light);
   }
   public static void TurnOnLight(Light light)
   { light.TurnOn(); }
}

不知各位能否说出最后一行代码light.TurnOn()方法在什么地方吗?恐怕不能。因为主程序中先后两次调用TurnOnLight方法,然而所运行的代码位置不同,产生的结果也各不相同。因此,我们说TurnOn方法是非静态的,究竟调用哪段代码要在运行时决定,而不能在编译时决定。其实这就是C#语言中的实例方法(Instance Method),也叫做对象方法(Object Method)。不过我个人更喜欢叫它动态方法(Dynamic Method),因为TurnOn方法的代码位置是在运行时动态决定的。(注意:“动态方法”这个名称是错误的,因为动态方法与实例方法是两个孑然不同的概念!本文最后要比较实例方法和动态方法的区别。在不是很严格的情况下,我总是喜欢将它们混为一谈

3、编译时与运行时

简单说来,C#中的静态是指在编译时就能够唯一确定其代码位置的,调用时目的地静止不动、不会发生变化的方法。而非静态方法往往指在运行时动态决定代码的调用位置、能够呈现出多态特征的方法,其调用往往绑定到某一对象的具体实例上。

关于实例方法为什么不能叫做“动态方法”以及实例方法究竟是怎么一回事,我们将在下一部分内容中加以详细介绍。

4、为什么Main必须是静态的?

现在我们可以思考一下为什么Main方法必须是静态的?从一个角度上说,Main是程序的入口点,它在编译时必须是能够唯一确定其位置的,不能发生变化的方法,其调用不能产生任何的二意性,因此必须是静态的。当然这里的分析还比较片面,当我们深入学习了后面的实例方法后,对Main方法为什么是静态的会有更深入的了解。

5、再次分析银行帐户的案例

对于第一部分中银行帐户案例里银行利率必须是Shared属性的解释,我们现在可以站在static的角度加以分析了。因为银行利率的存储地址必须是唯一的,当你访问银行利率时必须没有二意性。从这个角度上说,银行利率必须是“静态”的。

三、实例方法及其实现

在前面分析了静态方法后,我们在这部分内容中将重点说说什么是实例方法、实例方法的威力以及它是如何在内部实现的。另外我们还要讨论为什么实例方法不能叫做动态方法。

1、值类型与引用类型

关于这个问题我不准备多说什么了,有太多的资料介绍这个问题。如果现在还不知道什么是值类型和引用类型的话,我想下面的那部分内容也就可以不用读了。

2、什么是this

首先看看下面这段程序:

using System ;
public class Car
{
   public int speed = 0;
   public void SetSpeed(int speed)
   {
      this.speed = speed;
   }
}
public class Client
{
   public static void Main()
   {
      Car car1 = new Car();
      Car car2 = new Car();
      car1.SetSpeed(60);
      car2.SetSpeed(100);
   }
}

大家注意,我们在主程序中声明了两个Car的实例,分别是car1与car2,然后分别调用其SetSpeed方法,于是两个car实例被赋予了不同的速度值。然而SetSpeed方法是如何知道究竟是修改car1的speed还是car2的呢?这两个speed又在什么地方呢?SetSpeed方法中的this.speed是谁,speed又是谁呢?

其实实例方法在调用时隐含的传递了一个this指针,使得我们的SetSpeed知道到哪里去修改相应的数据。上面的程序运行时内存布局如下图所示:

当创建了car1对象与car2对象后,实际上在堆中给这两个对象分配了相应的内存并初始化了字段的取值。由于对象是引用类型,因此变量car1与car2存储的是内存地址。紧接着我们要调用SetSpeed方法,上面说过,实例方法在调用时实际上隐含的传递了一个this指针。因此程序实际执行时,SetSpeed方法实际上形式如下:

SetSpeed(Car this, int speed)

注意:上面的形式并不完全正确,仅仅说明了非virtual的Instance方法的调用方式,在后面说完VMT的概念后再说virtual方法的调用方式。

在上面的SetSpeed方法调用中,第一个参数是一个Car类型的参数,参数名是this,第二个参数才是代码中书写的int speed参数。为什么会是这样呢?我们看看SetSpeed方法实际被调用时的情形:

当调用car1.SetSpeed(60);时,实际上后台执行的代码是SetSpeed(car1, 60);。 将car1的引用地址传了进去并赋值给this参数。这样一来,“this.speed = speed;”就不难理解了,this.speed就是顺着地址00A0找到的speed属性(即car1.speed),而第二个speed是SetSpeed方法中的第二个参数。这句代码的意思就很明显了。

3、型与值的概念

型与值在编程语言中通常是统一的。例如代码“int i = 10;”, 变量i的型是整型,而变量i的值是10,10是整型,因此我们说i的型与值是一致的。这种例子多得是,我就不再多说了。然而在面向对象程序设计中,还存在一种型与值不一致的情形(注意:严格的说,并不存在型与值不一致的情形,型只是编译时而已,而值才是运行时,在运行时,型和值其实是一致的。关于这点我们将在后面再探讨)。请看下面的代码:

using System;
public abstract class Light
{
   public abstract void TurnOn();
}
public class BulbLight : Light
{
   public override void TurnOn()
   { Console.WriteLine("Bulb Light is turned on!"); }
}
public class TubeLight : Light
{
   public override void TurnOn()
   { Console.WriteLine("Tube Light is turned on!"); }
}
public class Client
{
   public static void Main()
   {
      Light light = new BulbLight();
      light.TurnOn();
   }
}

在上面的案例中,变量light的值是灯泡(BulbLight),然而它的型确是灯(Light),值是型的子类,因此程序在执行时会呈现出多态的行为:当我们调用灯的开灯方法时,会调用灯泡的开灯方法。这又是如何实现的呢?其实一切秘密都隐藏在虚拟方法表中(VMT, Virtual Method Table)。

4、Virtual Method Table

实例方法之所以在运行时能够产生“动态”的行为,其秘密就在于虚拟方法表(VMT)。其实,使用虚拟方法表的目的就是让子类与父类中相同的方法与属性在各自的虚拟方法表中具有相同的偏移量(当然也有不同的实现,比如后面说的Dynamic方法,实现手段就不是这样)。用一个图来描述的话就是:

该图对于属性和方法是同样适用的,只是属性并不存放在虚拟方法表中。从图中可以看出,子类包含了父类的所有方法和属性(包括私有成员),并且它们的相对位置(偏移量)是完全相同的。

另外,每个对象实例都会有一个指向自己虚拟方法表的指针(如下图左侧的 VMT ptr),实例属性就存储在堆中,每个实例拥有自己的属性值,而虚拟方法表中记录下了各个实例方法的具体代码位置(指向函数的指针)。值得注意的是,虚拟方法表除了存储实例方法指针外,还会存储一些其它信息,例如类的类名(下图中间最上面的name指针),并且是负偏移量。不同语言的实现手段也各不相同。如果对此感兴趣的话,可以分析一下Delphi的代码。网上也有很多文章介绍Delphi中虚拟方法表是什么样子的,在这里就不再重复。感兴趣的人也可以读一读台湾李维的《Inside VCL(深入核心——VCL架构剖析)》一书。

下图演示了同一个对象的多个实例共享一个VMT,但各自存储自己的属性。

5、"多态"是怎么实现的

面向对象程序设计的魅力所在就是“针对抽象编程”,而没有多态,针对抽象编程就会成为空谈。关于多态是什么不是本文要论述的问题,我在这里重点说说多态是如何通过虚拟方法表实现的。假设有如下图所示的类继承关系:

Motorcycle与Car继承自Vehicle,Mortorcycle覆写了Drive方法与Stop方法;Car覆写了Drive方法并提供了一个新属性maxSpeed和一个新方法SetMaxSpeed;PassengerCar与Truck继承自Car,并分别提供了各自的新属性与方法,另外Truck覆写了Stop方法。如果用C#代码书写下来的话,可以表示成:

using System ;
public class Vehicle
{
   public int wheels;
   public int speed;
   public virtual void Drive()
   {
      Console.WriteLine("Drive Vehicle.");
   }
   public virtual void Stop()
   {
      Console.WriteLine("Stop Vehicle.");
   }
}
public class Motorcycle : Vehicle
{
   public override void Drive()
   {
      Console.WriteLine("Drive Motorcycle.");
   }
   public override void Stop()
   {
      Console.WriteLine("Stop Motorcycle.");
   }
}
public class Car : Vehicle
{
   public int maxSpeed;
   public override void Drive()
   {
      Console.WriteLine("Drive Car.");
   }
   public virtual void SetMaxSpeed(int maxSpeed)
   {
      this.maxSpeed = maxSpeed;
      Console.WriteLine("Set car max speed to {0}.", maxSpeed);
   }
}
public class PassengerCar : Car
{
   public int maxPassengers;
   public void SetMaxPassengers(int maxPassengers)
   {
      this.maxPassengers = maxPassengers;
      Console.WriteLine("Set max passengers to {0}.", maxPassengers);
   }
}
public class Truck : Car
{
   public int carryingCapacity;
   public override void Stop()
   {
      Console.WriteLine("Stop Truck.");
   }
   public void SetCarryingCapacity(int carryingCapacity)
   {
      this.carryingCapacity = carryingCapacity;
      Console.WriteLine("Set carrying capacity to {0}t.", carryingCapacity);
   }
}
public class Client
{
   public static void Main()
   {
      Vehicle v = new Truck();
      v.Drive();
      v.Stop();
      Car c = (Car)v;
      c.SetMaxSpeed(90);
      Truck t = (Truck)c;
      t.SetCarryingCapacity(10);
      Car car = new PassengerCar();
      car.Drive();
      PassengerCar p = (PassengerCar)car;
      p.SetMaxPassengers(21);
      Truck truck = new Truck();
      truck.SetCarryingCapacity(20);
   }
}

注意客户端调用代码中使用了型与值不一致的表示方法,将子类的实例赋值给了父类,并进行了多次强制类型转换。那么上面那段代码在执行时内存的布局是什么样子的呢?我们可以使用下图来描述:

从上图中我们可以得出以下结论:

1)当子类覆写了父类的方法时,相当于将VMT中对应方法指针指向了新的代码位置。

子类与父类中相同的方法具有相同的偏移量(相对位置),如果子类override了父类的方法,将使得该位置上的方法指针指向新的位置。上图中,PassengerCar通过Car继承自Vehicle,PassengerCar与Car均没有覆写Vehicle的Stop方法,因此,PassengerCar的VMT中Stop方法仍然指向Vehicle.Stop法方法。而Car覆写了Vehicle的Drive方法,PassengerCar没有覆写Car的Drive方法,导致PassengerCar的Drive方法指向了Car的Drive方法。依此类推……

2)非静态方法在编译时能够确定的仅仅是虚拟方法表中的偏移量

如上图,由于子类与父类中相同的方法具有相同的偏移量(相对位置),因此当程序对虚拟方法进行调用时,实际上是对VMT中指定位置索引的方法进行调用,于是产生了多态。在上面的例子中,当执行Vehicle v = new Truck();后,系统在堆中创建了一个值为Truck的对象(虚拟方法表的指针指向了Truck的VMT),并将该对象的地址赋值给了一个型为Vehicle的变量v。当我们调用v.Drive()时,其实在后台执行的代码是(* v.VMT[idx])(v),在这里idx是Drive方法在VMT中的偏移量,即0(该偏移量可以在编译时就确定下来)。整个命令可以表示成:调用v所指向的VMT中偏移量为0处的方法,并将v的引用传递给该方法(这就是前面所说的隐含传递了一个this指针)。

因此,我们说非静态方法的调用地址是不固定的,唯一能够在编译时确定下来的就是方法在虚拟方法表中的相对偏移位置。

3)型是编译时、值是运行时

通过上面的分析我们还可以得到一个结论:型是编译时,值是运行时(尽管在IL代码中仍然保留了型的成分,但是我始终坚信在最终的机器代码中是没有型这个概念的)。关于这个结论我们可以从程序运行时内存布局上看出。注意在前面的内存布局图中,t、c、v分别表示了型为Truck、Car与Vehcile的三个对象,然而在内存中它们是完全一样的。型的存在是为了提供多态、提供“针对抽象编程”生存的环境,同时帮助编译器确认方法偏移量,而真正程序执行时只有值,没有型。我们也可以从下面这段代码中看出型仅仅是编译时:

using System ;
public class Light
{}
public class BulbLight : Light
{}
public class Client
{
   public static void Main()
   {
      Light l = new BulbLight();
      Console.WriteLine(l.GetType().ToString());
   }
}

注意变量l的型是Light,而它的值是BulbLight,当我们调用l的GetType方法时,它返回的不是Light型,而是BulbLight型,究其原因,其实这个Type是根据对象的虚拟方法表(及相关因素)产生的,而l的虚拟方法表就是指向BulbLight的,因此程序执行的结果是BulbLight而不是Light。这也从一个侧面说明了“型是编译时、值是运行时”,程序一旦运行起来就没有型的概念了。

有人可以会提及down cast或者up cast的概念以及is运算符与as运算符,这些不都是和型相关吗?我个人认为这些东西在运行时所做工作仅仅是根据虚拟方法表(很多虚拟方法表中都提供了一个指向父类VMT的指针,于是便串成了一个链表)判断类型是否匹配而已,如果类型匹配,则无需再做任何工作。

也许有人会提及接口的cast是如何进行的,其实接口的内存实现与VMT不一样,应当单独讨论,我个人理解应当有接口表,每个接口又对应VMT中的不同方法,不过这也仅仅是根据Assembly内部结构作的推断而已,就不再加以讨论。

四、为什么实例方法不能叫动态方法?

与静态方法相对应的应当叫做“动态方法”,然而我们可以在C#教材上看到“实例方法”、“对象方法”的称呼,唯独没有叫“动态方法”,尽管这种叫法显得比较自然,这是为什么呢?其原因就在于“动态方法”其实另有它指。在Delphi语言中动态方法有自己专门的实现机制。“动态方法”的英文名字叫做“Dynamic Method”,尽管从功能上和我们的实例方法没有任何区别,然而在实现技术上确相差很大,因为动态方法使用的不是VMT,而是DMT(Dynamic Method Table)。DMT是什么样子?又有什么好处呢?针对前面的Vehicle案例,我们如果用动态方法表表述的话可以描述成下图:

可以看到相对于VMT,每个DMT的子类中不再包含父类中所有项目,相同方法也不再拥有相同的相对位置,取而代之的是每个DMT都有一个指向父类DMT的指针,并且每个DMT中只包含有自己覆写或实现的方法。这样一来节省了内存的占用,然而确影响了调用效率。

由于DMT在父类和子类中的大小不一定相同,同一个方法在DMT中的位置也不一定相同,所以动态方法在调用时必须从当前类的DMT向上动态查找,调用效率就低一些。而VMT能够在编译时就确定方法偏移量,因此调用很快,但使用的内存要比DMT多。

因此,从运行时的表现上,“动态方法”和“实例方法”是完全相同的,但从实现机制上说又各不相同,在不是很严格的情况下可以混为一谈。如果深入实现机理,我们还要搞清楚“动态”的真正含义。

五、补充

看到装配脑袋双鱼座的回复后,发现文中确实有几处漏洞,在这里补充一下:

我对static的看法仅仅是本文的一个引子,观点似乎不完全正确(地址固定的、不会发生变化的方法),如果按照我的观点,非virtual的实例方法也有某种静态成分在里面,这显然有点离谱,所以我还是赞同双鱼座所说,纯粹为了尊重群体习惯,我有点偷换概念了。

另外对static、非virtual的Instance方法以及virtual的Instance方法在这里再作个归纳,希望批评指正:

1)静态方法:MethodName(parameters);其实在调用时也应当传递了一个this指针,只不过该指针指向的是类信息地址,该地址其实也是一个实例,只不过是一个更低层次上的、且结构完全固定的实例(即所谓元数据的元数据)。

2)非virtual的Instance方法:MethodName(this, parameters);

3)virtual的Instance方法:(* obj.VMT[idx])(this, parameters);

注:文中有关面向对象实现方面资料部分参考了《Introduction to Object Oriented Programming, 3rd Ed》一书第27章,可以从:

http://undergraduate.csse.uwa.edu.au/units/230.224/timBudd/chap27/chap27.html 处找到相关信息。

- 作者: 插队落户 2007年03月11日, 星期日 21:16  回复(6) |  引用(0) 加入博采

常见的一些javascript小技巧
//事件源对象
event.srcElement.tagName event.srcElement.type

//捕获释放
event.srcElement.setCapture();  event.srcElement.releaseCapture();

//事件按键
event.keyCode
event.shiftKey
event.altKey
event.ctrlKey

//事件返回值
event.returnvalue

//鼠标位置
event.x
event.y

//窗体活动元素
document.activeElement

//绑定事件
document.captureEvents(Event.KEYDOWN);

//访问窗体元素
document.all("txt").focus(); document.all("txt").select();

//窗体命令
document.execCommand

//窗体COOKIE
documents.cookie

//菜单事件
document.oncontextmenu

//创建元素
document.createElement("SPAN");

//根据鼠标获得元素:
document.elementFromPoint(event.x,event.y).tagName=="TD document.elementFromPoint(event.x,event.y).appendChild(ms)

//窗体图片
document.images[索引]

//窗体事件绑定
document.onmousedown=scrollwindow;

//元素
document.窗体.elements[索引]

//对象绑定事件
document.all.xxx.detachEvent('onclick',a);

//插件数目
navigator.plugins

//取变量类型
typeof($js_libpath) == "undefined"

//下拉框
下拉框.options[索引]
下拉框.options.length

//查找对象
document.getElementsByName("r1"); document.getElementById(id);

//定时
timer=setInterval('scrollwindow()',delay); clearInterval(timer);

//UNCODE编码
escape() ,unescape

//父对象
obj.parentElement(dhtml) obj.parentNode(dom)

//交换表的行
TableID.moveRow(2,1)

//替换CSS
document.all.csss.href = "a.css";

//并排显示
display:inline

//隐藏焦点
hidefocus=true

//根据宽度换行
style="word-break:break-all"

//自动刷新
<meta HTTP-EQUIV="refresh" CONTENT="8;URL=http://c98.yeah.net">

//简单邮件
<a href="mailto:aaa@bbb.com?subject=ccc&body=xxxyyy">

//快速转到位置
obj.scrollIntoView(true)

//锚
<a name="first">
<a href="#first">anchors</a>

//网页传递参数
location.search();

//可编辑
obj.contenteditable=true

//执行菜单命令
obj.execCommand

//双字节字符
/[^\x00-\xff]/

//汉字
/[\u4e00-\u9fa5]/

//让英文字符串超出表格宽度自动换行
word-wrap: break-word; word-break: break-all;

//透明背景
<IFRAME src="1.htm" width=300 height=180 allowtransparency></iframe>

//获得style内容
obj.style.cssText

//HTML标签
document.documentElement.innerHTML

//第一个style标签
document.styleSheets[0]

//style标签里的第一个样式
document.styleSheets[0].rules[0]

//防止点击空链接时,页面往往重置到页首端。
<a href="javascript:function()">word</a> 上一网页源 asp: request.servervariables("HTTP_REFERER") javascript: document.referrer

//释放内存
CollectGarbage();

//禁止右键
document.oncontextmenu = function() { return false;}

//禁止保存
<noscript><iframe src="*.htm"></iframe></noscript>

//禁止选取
<body oncontextmenu="return false" ondragstart="return false" onselectstart ="return false" onselect="document.selection.empty()" oncopy="document.selection.empty()" onbeforecopy="return false"onmouseup="document.selection.empty()>

//禁止粘贴
<input type=text onpaste="return false">

//地址栏图标
<link rel="Shortcut Icon" href="favicon.ico">
favicon.ico 名字最好不变16*16的16色,放虚拟目录根目录下

//收藏栏图标
<link rel="Bookmark" href="favicon.ico">

//查看源码
<input type=button value=查看网页源代码 onclick="window.location = 'view-source:'+ 'http://www.csdn.net/'">

//关闭输入法
<input style="ime-mode:disabled">

//自动全选
<input type=text name=text1 value="123" onfocus="this.select()">

//ENTER键可以让光标移到下一个输入框
<input onkeydown="if(event.keyCode==13)event.keyCode=9">

//文本框的默认值
<input type=text value="123" onfocus="alert(this.defaultvalue)">

//title换行
obj.title = "123&#13sdfs&#32"

//获得时间所代表的微秒
var n1 = new Date("2004-10-10".replace(/-/g, "\/")).getTime()

//窗口是否关闭
win.closed

//checkbox扁平
<input type=checkbox style="position: absolute; clip:rect(5px 15px 15px 5px)">

//获取选中内容
document.selection.createRange().duplicate().text

//自动完成功能
<input  type=text  autocomplete=on>打开该功能  <input  type=text  autocomplete=off>关闭该功能

//窗口最大化
<body onload="window.resizeTo(window.screen.width - 4,window.screen.height-50);window.moveTo(-4,-4)">

//无关闭按钮IE
window.open("aa.htm", "meizz", "fullscreen=7");

//统一编码/解码
alert(decodeURIComponent(encodeURIComponent("http://你好.com?as= hehe"))) encodeURIComponent对":"、"/"、";" 和 "?"也编码

//表格行指示
<tr onmouseover="this.bgColor='#f0f0f0'" onmouseout="this.bgColor='#ffffff'">

//各种尺寸
s  +=  "\r\n网页可见区域宽:"+  document.body.clientWidth;   s  +=  "\r\n网页可见区域高:"+  document.body.clientHeight;   s  +=  "\r\n网页可见区域高:"+  document.body.offsetWeight  +"  (包括边线的宽)";   s  +=  "\r\n网页可见区域高:"+  document.body.offsetHeight  +"  (包括边线的宽)";   s  +=  "\r\n网页正文全文宽:"+  document.body.scrollWidth;   s  +=  "\r\n网页正文全文高:"+  document.body.scrollHeight;   s  +=  "\r\n网页被卷去的高:"+  document.body.scrollTop;   s  +=  "\r\n网页被卷去的左:"+  document.body.scrollLeft;   s  +=  "\r\n网页正文部分上:"+  window.screenTop;   s  +=  "\r\n网页正文部分左:"+  window.screenLeft;   s  +=  "\r\n屏幕分辨率的高:"+  window.screen.height;   s  +=  "\r\n屏幕分辨率的宽:"+  window.screen.width;   s  +=  "\r\n屏幕可用工作区高度:"+  window.screen.availHeight;   s  +=  "\r\n屏幕可用工作区宽度:"+  window.screen.availWidth;

//过滤数字
<input type=text onkeypress="return event.keyCode>=48&&event.keyCode<=57||(this.value.indexOf('.')<0?event.keyCode==46:false)" onpaste="return !clipboardData.getData('text').match(/\D/)" ondragenter="return false">

//特殊用途
<input type=button value=导出收藏夹 onclick="window.external.ImportExportFavorites(false,'http://localhost');">
<input type=button value=整理收藏夹 onclick="window.external.ShowBrowserUI('OrganizeFavorites', null)">
<input type=button value=语言设置   onclick="window.external.ShowBrowserUI('LanguageDialog', null)">
<input type=button value=加入收藏夹 onclick="window.external.AddFavorite('http://www.google.com/', 'google')">
<input type=button value=加入到频道 onclick="window.external.addChannel('http://www.google.com/')">
<input type=button value=加入到频道 onclick="window.external.showBrowserUI('PrivacySettings',null)">

//不缓存
<META HTTP-EQUIV="pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> <META HTTP-EQUIV="expires" CONTENT="0">

//正则匹配
匹配中文字符的正则表达式: [\u4e00-\u9fa5] 匹配双字节字符(包括汉字在内):[^\x00-\xff] 匹配空行的正则表达式:\n[\s| ]*\r 匹配HTML标记的正则表达式:/<(.*)>.*<\/\1>|<(.*) \/>/  匹配首尾空格的正则表达式:(^\s*)|(\s*$)(像vbscript那样的trim函数) 匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* 匹配网址URL的正则表达式:http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

以下是例子:
利用正则表达式限制网页表单里的文本框输入内容:

用正则表达式限制只能输入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))"
1.用正则表达式限制只能输入全角字符: onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\uFF00-\uFFFF]/g,''))"
2.用正则表达式限制只能输入数字:onkeyup="value=value.replace(/[^\d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"
3.用正则表达式限制只能输入数字和英文:onkeyup="value=value.replace(/[\W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"

//消除图像工具栏
<IMG SRC="mypicture.jpg" HEIGHT="100px" WIDTH="100px" GALLERYIMG="false">  
or
<head>
<meta http-equiv="imagetoolbar" content="no">
</head>

//无提示关闭
function Close()
{  var ua=navigator.userAgent  var ie=navigator.appName=="Microsoft Internet Explorer"?true:false  
if(ie)  {       
var IEversion=parseFloat(ua.substring(ua.indexOf("MSIE ")+5,ua.indexOf(";",ua.indexOf("MSIE "))))   
if(IEversion< 5.5)   {    
var str  = '<object id=noTipClose classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">'       
str += '<param name="Command" value="Close"></object>';       
document.body.insertAdjacentHTML("beforeEnd", str);       
document.all.noTipClose.Click();   }      
else   {        window.opener =null;       
window.close();       }    }  
else  {   window.close()    } }

//取得控件得绝对位置(1)
function getoffset(e)
{  
var t=e.offsetTop;  
var l=e.offsetLeft;  
while(e=e.offsetParent)
{  
  t+=e.offsetTop;  
  l+=e.offsetLeft;  
}  
var rec = new Array(1);
rec[0]  = t;
rec[1] = l;
return rec
}  
</script>

//获得控件的绝对位置(2)
oRect = obj.getBoundingClientRect(); oRect.left oRect.

//最小化,最大化,关闭
<object id=min classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">  
<param name="Command" value="Minimize"></object>  
<object id=max classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">  
<param name="Command" value="Maximize"></object>  
<OBJECT id=close classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">  
<PARAM NAME="Command" value="Close"></OBJECT>  
<input type=button value=最小化 onclick=min.Click()>  
<input type=button value=最大化 onclick=max.Click()>  
<input type=button value=关闭 onclick=close.Click()>  

//光标停在文字最后
<script language="javascript">
function cc()
{
var e = event.srcElement;
var r =e.createTextRange();
r.moveStart('character',e.value.length);
r.collapse(true);
r.select();
}
</script>
<input type=text name=text1 value="123" onfocus="cc()">

//页面进入和退出的特效
进入页面<meta http-equiv="Page-Enter" content="revealTrans(duration=x, transition=y)">
推出页面<meta http-equiv="Page-Exit" content="revealTrans(duration=x, transition=y)">
这个是页面被载入和调出时的一些特效。duration表示特效的持续时间,以秒为单位。transition表示使
用哪种特效,取值为1-23:
  0 矩形缩小
  1 矩形扩大
  2 圆形缩小
  3 圆形扩大
  4 下到上刷新
  5 上到下刷新
  6 左到右刷新
  7 右到左刷新
  8 竖百叶窗
  9 横百叶窗
  10 错位横百叶窗
  11 错位竖百叶窗
  12 点扩散
  13 左右到中间刷新
  14 中间到左右刷新
  15 中间到上下
  16 上下到中间
  17 右下到左上
  18 右上到左下
  19 左上到右下
  20 左下到右上
  21 横条
  22 竖条
  23

//网页是否被检索
<meta name="ROBOTS" content="属性值">
  其中属性值有以下一些:
  属性值为"all": 文件将被检索,且页上链接可被查询;
属性值为"none": 文件不被检索,而且不查询页上的链接;
属性值为"index": 文件将被检索;
属性值为"follow": 查询页上的链接;
属性值为"noindex": 文件不检索,但可被查询链接;
属性值为"nofollow":

//打印分页
<p  style="page-break-after:always">page1</p>   
<p  style="page-break-after:always">page2</p>   

//设置打印
<object id="factory" style="display:none" viewastext
  classid="clsid:1663ed61-23eb-11d2-b92f-008048fdd814"
  codebase="http://www.meadroid.com/scriptx/ScriptX.cab#Version=5,60,0,360"
></object>
<input type=button value=页面设置 onclick="factory.printing.PageSetup()">
<input type=button value=打印预览 onclick="factory.printing.Preview()">
  
<script language=javascript>
function window.onload()
{
   // -- advanced features
   factory.printing.SetMarginMeasure(2) // measure margins in inches
   factory.printing.SetPageRange(false, 1, 3) // need pages from 1 to 3
   factory.printing.printer = "HP DeskJet 870C"
   factory.printing.copies = 2
   factory.printing.collate = true
   factory.printing.paperSize = "A4"
   factory.printing.paperSource = "Manual feed"
   // -- basic features
   factory.printing.header = "居左显示&b居中显示&b居右显示页码,第&p页/共&P页"
   factory.printing.footer = "(自定义页脚)"
   factory.printing.portrait = false
   factory.printing.leftMargin = 0.75
   factory.printing.topMargin = 1.5
   factory.printing.rightMargin = 0.75
   factory.printing.bottomMargin = 1.5
}
function Print(frame) {
  factory.printing.Print(true, frame) // print with prompt
}
</script>
<input type=button value="打印本页" onclick="factory.printing.Print(false)">
<input type=button value="页面设置" onclick="factory.printing.PageSetup()">
<input type=button value="打印预览" onclick="factory.printing.Preview()"><br>
<a href="http://www.meadroid.com/scriptx/docs/printdoc.htm?static"  target=_blank>具体使用手册,更多信息,点这里</a>

//自带的打印预览
WebBrowser.ExecWB(1,1) 打开 Web.ExecWB(2,1) 关闭现在所有的IE窗口,并打开一个新窗口 Web.ExecWB(4,1) 保存网页
Web.ExecWB(6,1) 打印 Web.ExecWB(7,1) 打印预览 Web.ExecWB(8,1) 打印页面设置 Web.ExecWB(10,1)
查看页面属性 Web.ExecWB(15,1) 好像是撤销,有待确认 Web.ExecWB(17,1) 全选 Web.ExecWB(22,1) 刷新 Web.ExecWB(45,1)
关闭窗体无提示
<style media="print">  .Noprint{display:none;}<!--用本样式在打印时隐藏非打印项目-->  .PageNext{page-break-after: always;}<!--控制分页-->  </style>
<object id="WebBrowser" width="0" height="0" classid="CLSID:8856F961-340A-11D0-A96B-00C04FD705A2">
</object>
<center class="Noprint">
<input type="button" value="打印" onclick="document.all.WebBrowser.ExecWB(6,1)">
<input type="button" value="直接打印" onclick="document.all.WebBrowser.ExecWB(6,6)">
<input type="button" value="页面设置" onclick="document.all.WebBrowser.ExecWB(8,1)">
<p></p>
<p>
<input type="button" value="打印预览" onclick="document.all.WebBrowser.ExecWB(7,1)">
</p>
</center>

//去掉打印时的页眉页脚
<script  language="javascript">   
var HKEY_Root,HKEY_Path,HKEY_Key;
HKEY_Root="HKEY_CURRENT_USER";
HKEY_Path="\\Software\\Microsoft\\Internet Explorer\\PageSetup\\";

//设置网页打印的页眉页脚为空
function PageSetup_Null()
{
try
{
         var Wsh=new ActiveXObject("WScript.Shell");
  HKEY_Key="header";
  Wsh.RegWrite(HKEY_Root+HKEY_Path+HKEY_Key,"");
  HKEY_Key="footer";
  Wsh.RegWrite(HKEY_Root+HKEY_Path+HKEY_Key,"");
}
catch(e){}
}

//设置网页打印的页眉页脚为默认值
function  PageSetup_Default()
{   
try
{
  var Wsh=new ActiveXObject("WScript.Shell");
  HKEY_Key="header";
  Wsh.RegWrite(HKEY_Root+HKEY_Path+HKEY_Key,"&w&b页码,&p/&P");
  HKEY_Key="footer";
  Wsh.RegWrite(HKEY_Root+HKEY_Path+HKEY_Key,"&u&b&d");
}
catch(e){}
}
</script>
<input type="button" value="清空页码" onclick=PageSetup_Null()>
<input type="button" value="恢复页码" onclick=PageSetup_Default()>
//浏览器验证


function checkBrowser() {     this.ver=navigator.appVersion     this.dom=document.getElementById?1:0     this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom)?1:0;     this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom)?1:0;     this.ie4=(document.all && !this.dom)?1:0;     this.ns5=(this.dom && parseInt(this.ver) >= 5) ?1:0;     this.ns4=(document.layers && !this.dom)?1:0;     this.mac=(this.ver.indexOf('Mac') > -1) ?1:0;     this.ope=(navigator.userAgent.indexOf('Opera')>-1);     this.ie=(this.ie6 || this.ie5 || this.ie4)     this.ns=(this.ns4 || this.ns5)     this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns5 || this.ns4 || this.mac || this.ope)     this.nbw=(!this.bw)     return this; }
//计算内容宽和高
<SCRIPT  language="javascript">   
function  test(obj)   
{   
       var  range  =  obj.createTextRange();   
       alert("内容区宽度:  "  +  range.boundingWidth   +  "px\r\n内容区高度:  "  +  range.boundingHeight  +  "px");   
              
}   
</SCRIPT>   
<BODY>   
<Textarea id="txt" height="150">sdf</text_area><INPUT  type="button"  value="计算内容宽度"  onClick="test(txt)">   
</BODY>

//无模式的提示框
function modelessAlert(Msg) {    window.showModelessDialog("javascript:alert(\""+escape(Msg)+"\");window.close();","","status:no;resizable:no;help:no;dialogHeight:height:30px;dialogHeight:40px;"); }

//屏蔽按键
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
  <noscript><meta http-equiv="refresh" content="0;url=about:noscript"></noscript>
  <title>屏蔽鼠标右键、Ctrl+N、Shift+F10、Alt+F4、F11、F5刷新、退格键</title>
</head>
<body>
<script language="javascript"><!--
   //屏蔽鼠标右键、Ctrl+N、Shift+F10、F11、F5刷新、退格键
  //Author: meizz(梅花雨) 2002-6-18
function document.oncontextmenu(){event.returnvalue=false;}//屏蔽鼠标右键
function window.onhelp(){return false} //屏蔽F1帮助
function document.onkeydown()
{
  if ((window.event.altKey)&&
      ((window.event.keyCode==37)||   //屏蔽 Alt+ 方向键 ←
       (window.event.keyCode==39)))   //屏蔽 Alt+ 方向键 →
  {
     alert("不准你使用ALT+方向键前进或后退网页!");
     event.returnvalue=false;
  }
     /* 注:这还不是真正地屏蔽 Alt+ 方向键,
     因为 Alt+ 方向键弹出警告框时,按住 Alt 键不放,
     用鼠标点掉警告框,这种屏蔽方法就失效了。以后若
     有哪位高手有真正屏蔽 Alt 键的方法,请告知。*/
  if ((event.keyCode==8)  ||                 //屏蔽退格删除键
      (event.keyCode==116)||                 //屏蔽 F5 刷新键
      (event.ctrlKey && event.keyCode==82)){ //Ctrl + R
     event.keyCode=0;
     event.returnvalue=false;
     }
  if (event.keyCode==122){event.keyCode=0;event.returnvalue=false;}  //屏蔽F11
  if (event.ctrlKey && event.keyCode==78) event.returnvalue=false;   //屏蔽 Ctrl+n
  if (event.shiftKey && event.keyCode==121)event.returnvalue=false;  //屏蔽 shift+F10
  if (window.event.srcElement.tagName == "A" && window.event.shiftKey)  
      window.event.returnvalue = false;             //屏蔽 shift 加鼠标左键新开一网页
  if ((window.event.altKey)&&(window.event.keyCode==115))             //屏蔽Alt+F4
  {
      window.showModelessDialog("about:blank","","dialogWidth:1px;dialogheight:1px");
      return false;
  }
}
</script>
屏蔽鼠标右键、Ctrl+N、Shift+F10、Alt+F4、F11、F5刷新、退格键
</body>
</html>
//屏蔽打印
<style>
@media print{
* {display:none}
}
</style>

//移动的图层,拖动
1.
<span style='position:absolute;width:200;height:200;background:red' onmousedown=MouseDown(this) onmousemove=MouseMove() onmouseup=MouseUp()>meizz</span>
<script language=javascript>
var Obj;
function MouseDown(obj)
{
  Obj=obj;
  Obj.setCapture();
  Obj.l=event.x-Obj.style.pixelLeft;
  Obj.t=event.y-Obj.style.pixelTop;
}
function MouseMove()
{
  if(Obj!=null)
  {
    Obj.style.left = event.x-Obj.l;
    Obj.style.top = event.y-Obj.t;
  }
}
function MouseUp()
{
  if(Obj!=null)
  {
    Obj.releaseCapture();
    Obj=null;
  }
}
</script>
2.
<div id="myDiv" src="logo.gif" ondrag="doDrag();" onmouseover="this.style.cursor='hand'" style="position:absolute;left=100;top=100;" onmousedown="doMouseDown();">
<a href="#" onclick="return false"><h1>wlecome</h1></a>
</div>
<script language="javascript" type="text/javascript">
var orgMouseX;
var orgMouseY;
var orgObjX;
var orgObjY;
function doDrag()
{
var myObject=document.all.myDiv;
var x=event.clientX;
var y=event.clientY;
myObject.style.left=x-(orgMouseX-orgObjX);
myObject.style.top=y-(orgMouseY-orgObjY);
  
}
function doMouseDown()
{
orgMouseX=event.clientX;
orgMouseY=event.clientY;
orgObjX=parseInt(document.all.myDiv.style.left);
orgObjY=parseInt(document.all.myDiv.style.top);
}
</script>

//文档状态改变
<iframe src="a.html" id="f" name="f" scrolling="no" frameborder=0 marginwidth=0 marginheight=0></iframe>
<script>
var doc=window.frames["f"].document;
function s(){
if (doc.readyState=="complete"){
  document.all.f.style.height=doc.body.scrollHeight
  document.all.f.style.width=doc.body.scrollWidth
}
}
doc.onreadystatechange=s
</script>

//刷新后不变的文本框
<HTML>
<HEAD>
<META NAME="save" CONTENT="history">
<STYLE>
   .sHistory {behavior:url(#default#savehistory);}
</STYLE>
</HEAD>
<BODY>
<INPUT class=sHistory type=text id=oPersistInput>
</BODY>
</HTML>

//访问剪贴板
(1)拖拽访问
event.dataTransfer.setData("URL", oImage.src); sImageURL = event.dataTransfer.getData("URL");
(2)普通访问
window.clipboardData.setData("Text",oSource.innerText); window.clipboardData.getData("Text");

//操作COOKIE
function SetCookie(sName, svalue)
{  
documents.cookie = sName + "=" + escape(svalue) + "; ";
}
function GetCookie(sName)
{  
var aCookie = documents.cookie.split("; ");  
for (var i=0; i < aCookie.length; i++)  {      
var aCrumb = aCookie[i].split("=");   
if (sName == aCrumb[0])    
return unescape(aCrumb[1]);  
}   }
function DelCookie(sName) {
documents.cookie = sName + "=" + escape(svalue) + "; expires=Fri, 31 Dec 1999 23:59:59 GMT;";
}

//setTimeout增加参数
<script>
var _st = window.setTimeout;
window.setTimeout = function(fRef, mDelay) {
if(typeof fRef == 'function'){
  var argu = Array.prototype.slice.call(arguments,2);
  var f = (function(){ fRef.apply(null, argu); });
  return _st(f, mDelay);
}
return _st(fRef,mDelay);
}
function test(x){
alert(x);
}
window.setTimeout(test,1000,'fason');
</script>

//自定义的apply,call
Function.prototype.apply = function (obj, argu) {  
if (obj) obj.constructor.prototype._caller = this;   
var argus = new Array();  
for (var i=0;i<argu.length;i++)   
argus[i] = "argu[" + i + "]";  
var r;  
eval("r = " + (obj ? ("obj._caller(" + argus.join(",") + ");") : ("this(" + argus.join(",") + ");")));  
return r;
};
Function.prototype.call = function (obj) {  
var argu = new Array();  
for (var i=1;i<arguments.length;i++)   
argu[i-1] = arguments[i];  
return this.apply(obj, argu);
};       

//下载文件
function DownURL(strRemoteURL,strLocalURL) {  try  {   var xmlHTTP=new ActiveXObject("Microsoft.XMLHTTP");   xmlHTTP.open("Get",strRemoteURL,false);   xmlHTTP.send();   var adodbStream=new ActiveXObject("ADODB.Stream");   adodbStream.Type=1;//1=adTypeBinary   adodbStream.Open();   adodbStream.write(xmlHTTP.responseBody);   adodbStream.SaveToFile(strLocalURL,2);   adodbStream.Close();   adodbStream=null;   xmlHTTP=null;     }  catch(e)  {   window.confirm("下载URL出错!");  }  //window.confirm("下载完成."); }

//检验连接是否有效
function getXML(URL)  {  var xmlhttp = new ActiveXObject("microsoft.xmlhttp");  xmlhttp.Open("GET",URL, false);   try  {    xmlhttp.Send();  }  catch(e){}  finally   {   var result = xmlhttp.responseText;   if(result)    {    if(xmlhttp.Status==200)    {     return(true);    }    else     {     return(false);    }   }   else    {    return(false);   }  } }
//POST代替FORM
<SCRIPT language="VBScript">
Function URLEncoding(vstrIn)
    strReturn = ""
    For i = 1 To Len(vstrIn)
        ThisChr = Mid(vStrIn,i,1)
        If Abs(Asc(ThisChr)) < &HFF Then
            strReturn = strReturn & ThisChr
        Else
            innerCode = Asc(ThisChr)
            If innerCode < 0 Then
                innerCode = innerCode + &H10000
            End If
            Hight8 = (innerCode  And &HFF00)\ &HFF
            Low8 = innerCode And &HFF
            strReturn = strReturn & "%" & Hex(Hight8) &  "%" & Hex(Low8)
        End If
    Next
    URLEncoding = strReturn
End Function
Function bytes2BSTR(vIn)
    strReturn = ""
    For i = 1 To LenB(vIn)
        ThisCharCode = AscB(MidB(vIn,i,1))
        If ThisCharCode < &H80 Then
            strReturn = strReturn & Chr(ThisCharCode)
        Else
            NextCharCode = AscB(MidB(vIn,i+1,1))
            strReturn = strReturn & Chr(CLng(ThisCharCode) * &H100 + CInt(NextCharCode))
            i = i + 1
        End If
    Next
    bytes2BSTR = strReturn
End Function
dim strA,oReq
strA = URLEncoding("submit1=Submit&text1=中文")
set oReq = CreateObject("MSXML2.XMLHTTP")
oReq.open "POST","http://ServerName/VDir/TstResult.asp",false
oReq.setRequestHeader "Content-Length",Len(strA)
oReq.setRequestHeader "CONTENT-TYPE","application/x-www-form-urlencoded"
oReq.send strA
msgbox bytes2BSTR(oReq.responseBody)
</SCRIPT>
//readyState是xmlhttp返回数据的进度,0=载入中,1=未初始化,2=已载入,3=运行中,4=完成

//组件是否安装
isComponentInstalled("{6B053A4B-A7EC-4D3D-4567-B8FF8A1A5739}", "componentID"))

//检查网页是否存在
function CheckURL(URL) {   var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");   xmlhttp.Open("GET",URL, false);   try   {      xmlhttp.Send();      var result = xmlhttp.status;   }   catch(e) {return(false); }   if(result==200)   {      return true;   }   xmlhttp = null;   return false; }

//连接数据库
<script language="javascript">
  //用 javascript 写服务器端连接数据库的代码示例
  var conn = new ActiveXObject("ADODB.Connection");
  conn.Open("Provider=SQLOLEDB.1; Data Source=localhost; User ID=sa; "
    +"Password=; Initial Catalog=pubs");
  var rs = new ActiveXObject("ADODB.Recordset");
  var sql="select * from authors";
  rs.open(sql, conn);
shtml = "<table width='100%' border=1>";
shtml +="<tr bgcolor='#f4f4f4'><td>au_id</td><td>au_lname</td><td>au_fname</td><td>phone</td><td>address</td><td> city</td><td>state</td><td>zip</td></tr>";
  while(!rs.EOF)
  {
shtml += "<tr><td>" + rs("au_id") + "</td><td>" + rs("au_lname") + "</td><td>" + rs("au_fname") + "</td><td>" + rs("phone") + "</td><td>" + rs("address") + "</td><td>" + rs("city") + "</td><td>" + rs("state") + "</td><td>" + rs("zip") + "</td></tr>";
rs.moveNext;
  }
  shtml += "</table>";
  document.write(shtml);
  rs.close();  
  rs = null;  
  conn.close();  
  conn = null;
</script>

//使用数据岛
<html>
<body>
srno:<input type=text datasrc=#xmldate DataFLD=srno size="76"><BR>
times:<input type=text datasrc=#xmldate DataFLD=times size="76"><BR>
<input id="first" TYPE=button value="<< 第一条记录" onclick="xmldate.recordset.moveFirst()">
<input id="prev" TYPE=button value="<上一条记录" onclick="xmldate.recordset.movePrevious()">   
<input id="next" TYPE=button value="下一条记录>" onclick="xmldate.recordset.moveNext()">   
<input id="last" TYPE=button value="最后一条记录>>" onclick="xmldate.recordset.moveLast()">    
<input id="Add" TYPE=button value="添加新记录" onclick="xmldate.recordset.addNew()">   
<XML ID="xmldate">
<infolist>
<info ><srno>20041025-01</srno><times>null</times></info>
<info ><srno>20041101-09</srno><times>2004年10月1日2点22分0秒</times></info>
</infolist>
</XML>
</body>
</html>

//获得参数
<body>
<a href="javascript:location.href=location.href + '?a=1&b=2'">search</a>
<script language="javascript">
<!--
var a = location.search.substr(1);
if(a.length>0)
{
var re = /([^&]*?)\=([^&]*)/g
var s = a.match(re);
for(var i= 0;i<s.length;i++)
{
  alert(s[i]);
  alert(s[i].split("=")[1]);
}
}
//-->
</script>
</body>

//可编辑SELECT
<input type=text name=re_name style="width:100px;height:21px;font-size:10pt;"><span style="width:18px;border:0px solid red;"><select name="r00" style="margin-left:-100px;width:118px; background-color:#FFEEEE;" onChange="document.all.re_name.value=this.value;">
                <option value="1">11111111<option>
                <option value="2">222222</option>
                <option value="3">333333</option>
              </select>
              </span>

//操作EXECL
<script language="javascript">
function jStartExcel() {
var xls = new ActiveXObject ( "Excel.Application" );
xls.visible = true;
var newBook = xls.Workbooks.Add;
newBook.Worksheets.Add;
newBook.Worksheets(1).Activate;
xls.ActiveWorkBook.ActiveSheet.PageSetup.Orientation = 2;
xls.ActiveWorkBook.ActiveSheet.PageSetup.PaperSize = 5;
newBook.Worksheets(1).Columns("A").columnwidth=50;
newBook.Worksheets(1).Columns("A").WrapText = true;
newBook.Worksheets(1).Columns("B").columnwidth=50;
newBook.Worksheets(1).Columns("B").WrapText = true;
newBook.Worksheets(1).Range("A1:B1000").NumberFormat = "0";
newBook.Worksheets(1).Range("A1:B1000").HorizontalAlignment = -4131;
newBook.Worksheets(1).Cells(1,1).Interior.ColorIndex="15";
newBook.Worksheets(1).Cells(1,1).value="First Column, First Cell";
newBook.Worksheets(1).Cells(2,1).value="First Column, Second Cell";
newBook.Worksheets(1).Cells(1,2).value="Second Column, First Cell";
newBook.Worksheets(1).Cells(2,2).value="Second Column, Second Cell";
newBook.Worksheets(1).Name="My First WorkSheet";
}
</script>

//自定义提示条
<a href="#" title="这是提示">tip</a>
<script Language="javascript">
//***********默认设置定义.*********************
tPopWait=50;//停留tWait豪秒后显示提示。
tPopShow=5000;//显示tShow豪秒后关闭提示
showPopStep=20;
popOpacity=99;
//***************内部变量定义*****************
sPop=null;
curShow=null;
tFadeOut=null;
tFadeIn=null;
tFadeWaiting=null;
document.write("<style type='text/css'id='defaultPopStyle'>");
document.write(".cPopText {  background-color: #F8F8F5;color:#000000; border: 1px #000000 solid;font-color: font-size: 12px; padding-right: 4px; padding-left: 4px; height: 20px; padding-top: 2px; padding-bottom: 2px; filter: Alpha(Opacity=0)}");
document.write("</style>");
document.write("<div id='dypopLayer' style='position:absolute;z-index:1000;' class='cPopText'></div>");
function showPopupText(){
var o=event.srcElement;
MouseX=event.x;
MouseY=event.y;
if(o.alt!=null && o.alt!=""){o.dypop=o.alt;o.alt=""};
        if(o.title!=null && o.title!=""){o.dypop=o.title;o.title=""};
if(o.dypop!=sPop) {
sPop=o.dypop;
clearTimeout(curShow);
clearTimeout(tFadeOut);
clearTimeout(tFadeIn);
clearTimeout(tFadeWaiting);
if(sPop==null || sPop=="") {
dypopLayer.innerHTML="";
dypopLayer.style.filter="Alpha()";
dypopLayer.filters.Alpha.opacity=0;
}
else {
if(o.dyclass!=null) popStyle=o.dyclass  
else popStyle="cPopText";
curShow=setTimeout("showIt()",tPopWait);
}
}
}
function showIt(){
dypopLayer.className=popStyle;
dypopLayer.innerHTML=sPop;
popWidth=dypopLayer.clientWidth;
popHeight=dypopLayer.clientHeight;
if(MouseX+12+popWidth>document.body.clientWidth) popLeftAdjust=-popWidth-24
else popLeftAdjust=0;
if(MouseY+12+popHeight>document.body.clientHeight) popTopAdjust=-popHeight-24
else popTopAdjust=0;
dypopLayer.style.left=MouseX+12+document.body.scrollLeft+popLeftAdjust;
dypopLayer.style.top=MouseY+12+document.body.scrollTop+popTopAdjust;
dypopLayer.style.filter="Alpha(Opacity=0)";
fadeOut();
}
function fadeOut(){
if(dypopLayer.filters.Alpha.opacity<popOpacity) {
dypopLayer.filters.Alpha.opacity+=showPopStep;
tFadeOut=setTimeout("fadeOut()",1);
}
else {
dypopLayer.filters.Alpha.opacity=popOpacity;
tFadeWaiting=setTimeout("fadeIn()",tPopShow);
}
}
function fadeIn(){
if(dypopLayer.filters.Alpha.opacity>0) {
dypopLayer.filters.Alpha.opacity-=1;
tFadeIn=setTimeout("fadeIn()",1);
}
}
document.onmouseover=showPopupText;
</script>

//插入文字
document.onclick =function(){  
var oSource = window.event.srcElement;  
if(oSource.tagName!="DIV")  
return false;  
var sel = document.selection;  
if (sel!=null) {  
var rng = sel.createRange();  
if (rng!=null)  
rng.pasteHTML("<font color=red>插入文字</font>");
}  }  
//netscapte下操作xml doc = new ActiveXObject("Msxml2.DOMDocument"); doc = new ActiveXObject("Microsoft.XMLDOM") ->> doc = (new DOMParser()).parseFromString(sXML,'text/xml')

//判断键值
<html>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<head>
<script language="javascript">
var ie  =navigator.appName=="Microsoft Internet Explorer"?true:false;
   
function keyDown(e)
{
if(!ie)
{
  var nkey=e.which;
  var iekey='现在是ns浏览器';
  var realkey=String.fromCharCode(e.which);
}
if(ie)
{
  var iekey=event.keyCode;
  var nkey='现在是ie浏览器';
  var realkey=String.fromCharCode(event.keyCode);
  if(event.keyCode==32){realkey='\' 空格\''}
  if(event.keyCode==13){realkey='\' 回车\''}
  if(event.keyCode==27){realkey='\' Esc\''}
  if(event.keyCode==16){realkey='\' Shift\''}
  if(event.keyCode==17){realkey='\' Ctrl\''}
  if(event.keyCode==18){realkey='\' Alt\''}
}
alert('ns浏览器中键值:'+nkey+'\n'+'ie浏览器中键值:'+iekey+'\n'+'实际键为'+realkey);
}
document.onkeydown = keyDown;
</script>
</head>
<body>
//javascript Document.
<hr>
<center>
<h3>请按任意一个键。。。。</h3>
</center>
</body>
</html>

//禁止FSO
1.注销组件
regsvr32 /u scrrun.dll
2.修改PROGID
HKEY_CLASSES_ROOT\Scripting.FileSystemObject
Scripting.FileSystemObject
3.对于使用object的用户,修改HKEY_CLASSES_ROOT\Scripting.

//省略号
<DIV STYLE="width: 120px; height: 50px; border: 1px solid blue;
            overflow: hidden; text-overflow:ellipsis">
<NOBR>就是比如有一行文字,很长,表格内一行显示不下.</NOBR>
</DIV>

//检测media play版本
<IE:clientCaps ID="oClientCaps" style="{behavior:url(#default#clientcaps)}" />
<SCRIPT>
var flash="";
    WMPVersion= oClientCaps.getComponentVersion("{22D6F312-B0F6-11D0-94AB-0080C74C7E95}","ComponentID");  
    if (WMPVersion != "") {
    flash = "";
    var version = WMPVersion.split(",");
    var i;
    for (i = 0; i < version.length; i++) {
      if (i != 0)
    flash += ".";
      flash += version[i];
    }
     document.write("您的Windows Media Player 版本是:"+flash+"<p>");
  }
</SCRIPT>

//图象按比例
<script language="javascript">
<!--
//图片按比例缩放
var flag=false;
function DrawImage(ImgD){
var image=new Image();
var iwidth = 80;  //定义允许图片宽度
var iheight = 80;  //定义允许图片高度
image.src=ImgD.src;
if(image.width>0 && image.height>0){
flag=true;
if(image.width/image.height>= iwidth/iheight){
  if(image.width>iwidth){   
  ImgD.width=iwidth;
  ImgD.height=(image.height*iwidth)/image.width;
  }else{
  ImgD.width=image.width;   
  ImgD.height=image.height;
  }
  ImgD.alt=image.width+"×"+image.height;
  }
else{
  if(image.height>iheight){   
  ImgD.height=iheight;
  ImgD.width=(image.width*iheight)/image.height;   
  }else{
  ImgD.width=image.width;   
  ImgD.height=image.height;
  }
  ImgD.alt=image.width+"×"+image.height;
  }
}
}  
//-->
</script>
<img src=".." onload = "DrawImage(this)">

//细线SELECT
<span style="border:1px solid #000000; position:absolute; overflow:hidden;" >
<select style="margin:-2px;">
<option>1111</option>
<option>11111111111111</option>
<option>111111111</option>
</select></span>
<span style="border:1px solid #000000; position:absolute; overflow:hidden;" >
<select style="margin:-2px;">
<option>1111</option>
<option>11111111111111</option>
<option>111111111</option>
</select></span>
//Import

function Import() {  for( var i=0; i<arguments.length; i++ ) {   var file = arguments[i];   if ( file.match(/\.js$/i))     document.write('<script type=\"text/javascript\" src=\"' + file + '\"></sc' + 'ript>');   else    document.write('<style type=\"text/css\">@import \"' + file + '\" ;</style>');  } };

//js枚举
function getComputerName() {  var objWMIService = GetObject("Winmgmts:root\cimv2");  for(e = new Enumerator(objWMIService) ; !e.atEnd() ; e.moveNext())  {     var getComputer = e.item();     return getComputer.Name;  } }

//条件编译
script language=javascript>
/*@cc_on @*/
/*@if (@_win32 && @_jscript_version>5)
function window.confirm(str)
{
    execScript("n = msgbox('"+ str +"', 257)", "vbscript");
    return(n == 1);
}
@end @*/
</script>

//取得innerText
<SCRIPT LANGUAGE="javascript">
<!--
var xmlDoc = new ActiveXObject("Msxml2.DOMDocument.4.0");
var currNode;
xmlDoc.async = false;
xmlDoc.async = false;
xmlDoc.loadXML("<TABLENAME>      你好你阿三    大法     司法等四              </TABLENAME>");
currNode = xmlDoc.documentElement;
    
  var s = currNode.xml;
  var r = /\<([^\>\s]*?)[^\>]*?\>([^\<]*?)\<\/\1\>/
  var b = s.replace(r,"$2");
  alert(b);
//-->
</SCRIPT>

//mergeAttributes 复制所有读/写标签属性到指定元素。
<SCRIPT>
function fnMerge(){
oSource.children[1].mergeAttributes(oSource.children[0]);
}
</SCRIPT>
<SPAN ID=oSource>
<DIV
ID="oDiv"
ATTRIBUTE1="true"
ATTRIBUTE2="true"
onclick="alert('click');"
onmouseover="this.style.color='#0000FF';"
onmouseout="this.style.color='#000000';"
>
This is a sample <B>DIV</B> element.
</DIV>
<DIV ID="oDiv2">
This is another sample <B>DIV</B> element.
</DIV>
</SPAN>
<INPUT
TYPE="button"
value="Merge Attributes"
onclick="fnMerge()"
>

//检查链接是否有效
<a href="http://www.pconline.com.cn/welcome.asp" onclick="mm(this, 'http://www.pconline.com.cn/')">test</a>
<script language="javascript">
function getURL(url)
{
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET",url,false);
xmlhttp.send();
if (xmlhttp.readyState==4) return xmlhttp.Status==200;
return false;
}
function mm(e, url)
{
if(!getURL(e.href)) e.href = url;
}
</script>

//判断是否输入值再激活按钮
<script>
function check(){
s.disabled = (t1.value == '' || t2.value == '' || t3.value == '')
}
</script>
<input name="t1" onpropertychange="check();">
<input name="t2" onpropertychange="check();">
<input name="t3" onpropertychange="check();">
<input type="submit" name="s" disabled>

//屏蔽鼠标右键、Ctrl+N、Shift+F10、Alt+F4、F11、F5刷新、退格键
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
  <noscript><meta http-equiv="refresh" content="0;url=about:noscript"></noscript>
  <title>屏蔽鼠标右键、Ctrl+N、Shift+F10、Alt+F4、F11、F5刷新、退格键</title>
</head>
<body>
<script language="javascript"><!--

//屏蔽鼠标右键、Ctrl+N、Shift+F10、F11、F5刷新、退格键
//Author: meizz(梅花雨) 2002-6-18
function document.oncontextmenu(){event.returnvalue=false;}//屏蔽鼠标右键
function window.onhelp(){return false} //屏蔽F1帮助
function document.onkeydown()
{
  if ((window.event.altKey)&&
      ((window.event.keyCode==37)||   //屏蔽 Alt+ 方向键 ←
       (window.event.keyCode==39)))   //屏蔽 Alt+ 方向键 →
  {
     alert("不准你使用ALT+方向键前进或后退网页!");
     event.returnvalue=false;
  }
     /* 注:这还不是真正地屏蔽 Alt+ 方向键,
     因为 Alt+ 方向键弹出警告框时,按住 Alt 键不放,
     用鼠标点掉警告框,这种屏蔽方法就失效了。以后若
     有哪位高手有真正屏蔽 Alt 键的方法,请告知。*/
  if ((event.keyCode==8)  ||                 //屏蔽退格删除键
      (event.keyCode==116)||                 //屏蔽 F5 刷新键
      (event.ctrlKey && event.keyCode==82)){ //Ctrl + R
     event.keyCode=0;
     event.returnvalue=false;
     }
  if (event.keyCode==122){event.keyCode=0;event.returnvalue=false;}  //屏蔽F11
  if (event.ctrlKey && event.keyCode==78) event.returnvalue=false;   //屏蔽 Ctrl+n
  if (event.shiftKey && event.keyCode==121)event.returnvalue=false;  //屏蔽 shift+F10
  if (window.event.srcElement.tagName == "A" && window.event.shiftKey)  
      window.event.returnvalue = false;             //屏蔽 shift 加鼠标左键新开一网页
  if ((window.event.altKey)&&(window.event.keyCode==115))             //屏蔽Alt+F4
  {
      window.showModelessDialog("about:blank","","dialogWidth:1px;dialogheight:1px");
      return false;
  }
}
</script>
屏蔽鼠标右键、Ct, rl+N、Shift+F10、Alt+F4、F11、F5刷新、退格键
</body>
</html>

//自定义热键(请按a)
<SCRIPT language="javascript">
<!--
var hotkey=97
var destination="http://www.wyev.com"
if (document.layers)
document.captureEvents(Event.KEYPRESS)
function backhome(e){
if (document.layers){
if (e.which==hotkey)
window.location=destination
}
else if (document.all){
if (event.keyCode==hotkey)
window.location=destination
}
}
document.onkeypress=backhome
//-->

</SCRIPT>

//取得字符串实际长度(汉字算两个字节,英文字母算一个字节):
<script>
var s='中文,English';
alert("["+s+"]的长度:"+s.replace(/[^\x00-\xff]/gi,'xx').length)
</script>

//最好的简繁转换
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>无标题文档</title>
</head>
<body>
<a id="StranLink">繁体</a>
网页代码检测 中国DotNet俱乐部——首页点乃特学习.z6ionestabw3cn蓝色ideaMSDN 雷神竞技场中文站论坛 z987论坛 VeryCD游戏专区 Google
<script type="text/javascript">
//模仿語言包式的簡繁轉換功能插件!
//Edited by Stardy --2005-04-16 , Web :http://www.stardy.com , QQ:2885465
//Re-Edited by Stud --2005-10-10 , Web :http://www.metro.com.tw
var Default_isFT = 0  //默認是否繁體,0-簡體,1-繁體
var StranIt_Delay = 30 //翻譯延時毫秒(設這個的目的是讓網頁先流暢的顯現出來)
//-------代碼開始,以下別改-------
//轉換文本
function StranText(txt,toFT,chgTxt)
{
if(txt==""||txt==null)return ""
toFT=toFT==null?BodyIsFt:toFT
if(chgTxt)txt=txt.replace((toFT?"簡":"繁"),(toFT?"繁":"簡"))
if(toFT){return Simplized(txt)}
else {return Traditionalized(txt)}
}
//轉換對象,使用遞歸,逐層剝到文本
function StranBody(fobj)
{
if(typeof(fobj)=="object"){var obj=fobj.childNodes}
else
{
  var tmptxt=StranLink_Obj.innerHTML.toString()
  if(tmptxt.indexOf("簡")<0)
  {
   BodyIsFt=0
   StranLink_Obj.innerHTML=StranText(tmptxt,0,1)
   StranLink_Obj.title=StranText(StranLink_Obj.title,0,1)
  }
  else
  {
   BodyIsFt=1
   StranLink_Obj.innerHTML=StranText(tmptxt,1,1)
   StranLink_Obj.title=StranText(StranLink_Obj.title,1,1)
  }
  setCookie(JF_cn,BodyIsFt,7)
  var obj=document.body.childNodes
}
for(var i=0;i<obj.length;i++)
{
  var OO=obj.item(i)
  if("||BR|HR|TEXTAREA|".indexOf("|"+OO.tagName+"|")>0||OO==StranLink_Obj)continue;
  if(OO.title!=""&&OO.title!=null)OO.title=StranText(OO.title);
  if(OO.alt!=""&&OO.alt!=null)OO.alt=StranText(OO.alt);
  if(OO.tagName=="INPUT"&&OO.value!=""&&OO.type!="text"&&OO.type!="hidden")OO.value=StranText(OO.value);
  if(OO.nodeType==3){OO.data=StranText(OO.data)}
  else StranBody(OO)
}
}
function JTPYStr()
{
     return '皑蔼碍爱翱袄奥坝罢摆败颁办绊帮绑镑谤剥饱宝报鲍辈贝钡狈备惫绷笔毕毙闭边编贬变辩辫鳖瘪濒滨宾摈饼拨钵铂驳卜补参蚕残惭惨灿苍舱仓沧厕侧册测层诧搀掺蝉馋谗缠铲产阐颤场尝长偿肠厂畅钞车彻尘陈衬撑称惩诚骋痴迟驰耻齿炽冲虫宠畴踌筹绸丑橱厨锄雏础储触处传疮闯创锤纯绰辞词赐聪葱囱从丛凑窜错达带贷担单郸掸胆惮诞弹当挡党荡档捣岛祷导盗灯邓敌涤递缔点垫电淀钓调迭谍叠钉顶锭订东动栋冻斗犊独读赌镀锻断缎兑队对吨顿钝夺鹅额讹恶饿儿尔饵贰发罚阀珐矾钒烦范贩饭访纺飞废费纷坟奋愤粪丰枫锋风疯冯缝讽凤肤辐抚辅赋复负讣妇缚该钙盖干赶秆赣冈刚钢纲岗皋镐搁鸽阁铬个给龚宫巩贡钩沟构购够蛊顾剐关观馆惯贯广规硅归龟闺轨诡柜贵刽辊滚锅国过骇韩汉阂鹤贺横轰鸿红后壶护沪户哗华画划话怀坏欢环还缓换唤痪焕涣黄谎挥辉毁贿秽会烩汇讳诲绘荤浑伙获货祸击机积饥讥鸡绩缉极辑级挤几蓟剂济计记际继纪夹荚颊贾钾价驾歼监坚笺间艰缄茧检碱硷拣捡简俭减荐槛鉴践贱见键舰剑饯渐溅涧浆蒋桨奖讲酱胶浇骄娇搅铰矫侥脚饺缴绞轿较秸阶节茎惊经颈静镜径痉竞净纠厩旧驹举据锯惧剧鹃绢杰洁结诫届紧锦仅谨进晋烬尽劲荆觉决诀绝钧军骏开凯颗壳课垦恳抠库裤夸块侩宽矿旷况亏岿窥馈溃扩阔蜡腊莱来赖蓝栏拦篮阑兰澜谰揽览懒缆烂滥捞劳涝乐镭垒类泪篱离里鲤礼丽厉励砾历沥隶俩联莲连镰怜涟帘敛脸链恋炼练粮凉两辆谅疗辽镣猎临邻鳞凛赁龄铃凌灵岭领馏刘龙聋咙笼垄拢陇楼娄搂篓芦卢颅庐炉掳卤虏鲁赂禄录陆驴吕铝侣屡缕虑滤绿峦挛孪滦乱抡轮伦仑沦纶论萝罗逻锣箩骡骆络妈玛码蚂马骂吗买麦卖迈脉瞒馒蛮满谩猫锚铆贸么霉没镁门闷们锰梦谜弥觅绵缅庙灭悯闽鸣铭谬谋亩钠纳难挠脑恼闹馁腻撵捻酿鸟聂啮镊镍柠狞宁拧泞钮纽脓浓农疟诺欧鸥殴呕沤盘庞国爱赔喷鹏骗飘频贫苹凭评泼颇扑铺朴谱脐齐骑岂启气弃讫牵扦钎铅迁签谦钱钳潜浅谴堑枪呛墙蔷强抢锹桥乔侨翘窍窃钦亲轻氢倾顷请庆琼穷趋区躯驱龋颧权劝却鹊让饶扰绕热韧认纫荣绒软锐闰润洒萨鳃赛伞丧骚扫涩杀纱筛晒闪陕赡缮伤赏烧绍赊摄慑设绅审婶肾渗声绳胜圣师狮湿诗尸时蚀实识驶势释饰视试寿兽枢输书赎属术树竖数帅双谁税顺说硕烁丝饲耸怂颂讼诵擞苏诉肃虽绥岁孙损笋缩琐锁獭挞抬摊贪瘫滩坛谭谈叹汤烫涛绦腾誊锑题体屉条贴铁厅听烃铜统头图涂团颓蜕脱鸵驮驼椭洼袜弯湾顽万网韦违围为潍维苇伟伪纬谓卫温闻纹稳问瓮挝蜗涡窝呜钨乌诬无芜吴坞雾务误锡牺袭习铣戏细虾辖峡侠狭厦锨鲜纤咸贤衔闲显险现献县馅羡宪线厢镶乡详响项萧销晓啸蝎协挟携胁谐写泻谢锌衅兴汹锈绣虚嘘须许绪续轩悬选癣绚学勋询寻驯训讯逊压鸦鸭哑亚讶阉烟盐严颜阎艳厌砚彦谚验鸯杨扬疡阳痒养样瑶摇尧遥窑谣药爷页业叶医铱颐遗仪彝蚁艺亿忆义诣议谊译异绎荫阴银饮樱婴鹰应缨莹萤营荧蝇颖哟拥佣痈踊咏涌优忧邮铀犹游诱舆鱼渔娱与屿语吁御狱誉预驭鸳渊辕园员圆缘远愿约跃钥岳粤悦阅云郧匀陨运蕴酝晕韵杂灾载攒暂赞赃脏凿枣灶责择则泽贼赠扎札轧铡闸诈斋债毡盏斩辗崭栈战绽张涨帐账胀赵蛰辙锗这贞针侦诊镇阵挣睁狰帧郑证织职执纸挚掷帜质钟终种肿众诌轴皱昼骤猪诸诛烛瞩嘱贮铸筑驻专砖转赚桩庄装妆壮状锥赘坠缀谆浊兹资渍踪综总纵邹诅组钻致钟么为只凶准启板里雳余链泄';
}
function FTPYStr()
{
return '皚藹礙愛翱見W壩罷擺敗頒辦絆幫綁鎊謗剝飽寶報鮑輩貝鋇狽備憊繃筆畢斃閉邊編貶變辯辮鱉癟瀕濱賓擯餅撥缽鉑駁蔔補參蠶殘慚慘燦蒼艙倉滄廁側冊測層詫攙摻蟬饞讒纏鏟產闡顫場嘗長償腸廠暢鈔車徹塵陳襯撐稱懲镇G癡遲馳恥齒熾沖蟲寵疇躊籌綢醜櫥廚鋤雛礎儲觸處傳瘡闖創錘純綽辭詞賜聰蔥囪從叢湊竄錯達帶貸擔單鄲撣膽憚誕彈當擋黨蕩檔搗島秾ПI燈鄧敵滌遞締點墊電澱釣調疊諜疊釘頂錠訂東動棟凍鬥犢獨讀賭鍍鍛斷緞兌隊對噸頓鈍奪鵝額訛惡餓兒爾餌貳發罰閥琺碘C煩範販飯訪紡飛廢費紛墳奮憤糞豐楓鋒風瘋馮縫諷鳳膚輻撫輔賦複負訃婦縛該鈣蓋幹趕稈贛岡剛鋼綱崗皋鎬擱鴿閣鉻個給龔宮鞏貢鉤溝構購夠蠱顧剮關觀館慣貫廣規矽歸龜閨軌詭櫃貴劊輥滾鍋國過駭韓漢閡鶴賀橫轟鴻紅後壺護滬戶嘩華畫劃話懷壞歡環還緩換喚瘓煥渙黃謊揮輝毀賄穢會燴彙諱誨繪葷渾夥獲貨禍擊機積饑譏雞績緝極輯級擠幾薊劑濟計記際繼紀夾莢頰賈鉀價駕殲監堅箋間艱緘繭檢堿鹼揀撿簡儉減薦檻鑒踐賤見鍵艦劍餞漸濺澗漿蔣槳獎講醬膠澆驕嬌攪鉸矯僥腳餃繳絞轎較秸階節莖驚經頸靜鏡徑痙競淨糾廄舊駒舉據鋸懼劇鵑絹傑潔結誡屆緊鍍H謹進晉燼盡勁荊覺決訣絕鈞軍駿開凱顆殼課墾懇摳庫褲誇塊儈寬礦曠況虧巋窺饋潰擴闊蠟臘萊來賴藍欄攔籃闌蘭瀾讕攬覽懶纜爛濫撈勞澇樂鐳壘類淚籬離裏鯉禮麗厲勵礫曆瀝隸倆聯蓮連鐮憐漣簾斂臉鏈戀煉練糧涼兩輛諒療遼鐐獵臨鄰鱗凜賃齡鈴淩靈嶺領餾劉龍聾嚨粔艛n隴樓婁摟簍蘆盧顱廬爐擄鹵虜魯賂祿錄陸驢呂鋁侶屢縷慮濾綠巒攣尀磥y掄輪倫侖淪綸論蘿羅邏鑼籮騾駱絡媽瑪碼螞馬罵嗎買麥賣邁脈瞞饅蠻滿謾貓錨鉚貿麼黴沒鎂門悶們錳夢謎彌覓綿緬廟滅憫閩鳴銘謬之&#65533;鈉納難撓腦惱鬧餒膩攆撚釀鳥聶齧鑷鎳檸獰甯擰濘鈕紐膿濃農瘧諾歐鷗毆嘔漚盤龐國愛賠噴鵬騙飄頻貧蘋憑評潑頗撲鋪樸譜臍齊騎豈啟氣棄訖牽扡□鉛遷簽謙錢鉗潛溩l塹槍嗆牆薔強搶鍬橋喬僑翹竅竊欽親輕氫傾頃請慶瓊窮趨區軀驅齲顴權勸卻鵲讓饒擾繞熱韌認紉榮絨軟銳閏潤灑薩鰓賽傘喪騷掃澀殺紗篩曬閃陝贍繕傷賞燒紹賒攝懾設紳審嬸腎滲聲繩勝聖師獅濕詩屍時蝕實識駛勢釋飾視試壽獸樞輸書贖屬術樹豎數帥雙誰稅順說碩爍絲飼聳慫頌訟誦斕K訴肅雖綏歲孫損筍縮瑣鎖獺撻抬攤貪癱灘壇譚談歎湯燙濤滌騰謄銻題體屜條貼鐵廳聽烴銅統頭圖塗團頹蛻脫鴕馱駝橢窪襪彎灣頑萬網韋違圍為濰維葦偉偽緯謂衛溫聞紋穩問甕撾蝸渦窩嗚鎢烏誣無蕪吳塢霧務誤錫犧襲習銑戲細蝦轄峽俠狹廈掀鮮纖鹹賢銜閑顯險現獻縣餡羨憲線廂鑲鄉詳響項蕭銷曉嘯蠍協挾攜脅諧寫瀉謝溽吪d洶鏽繡虛噓須許緒續軒懸選癬絢學勳詢尋馴訓訊遜壓鴉鴨啞亞訝閹煙鹽嚴顏閻豔厭硯彥諺驗鴦楊揚瘍陽癢養樣瑤搖堯遙窯謠藥爺頁業葉醫銥頤遺儀彝蟻藝億憶義詣議誼譯異繹蔭陰銀飲櫻嬰鷹應纓瑩螢營熒蠅穎喲擁傭癰踴詠湧優憂郵鈾猶遊誘輿魚漁娛與嶼語籲禦獄譽預馭鴛淵轅園員圓緣遠願約躍鑰嶽粵悅閱雲鄖勻隕咛N醞暈韻雜災載攢暫贊贓髒鑿棗灶責擇則澤儋浖檮炣堝庨l詐齋債氈盞斬輾嶄棧戰綻張漲帳賬脹趙蟄轍鍺這貞針偵枣傟噿瓯牚b幀鄭證織職執紙摯擲幟質鍾終種腫眾謅軸皺晝驟豬諸誅燭矚囑貯鑄築駐專磚轉賺樁莊裝妝壯狀錐贅墜綴諄濁茲資漬蹤綜總縱鄒詛組鑽緻鐘麼為隻兇準啟闆裡靂餘鍊洩';
}
function Traditionalized(cc){
var str='',ss=JTPYStr(),tt=FTPYStr();
for(var i=0;i<cc.length;i++)
{
  if(cc.charCodeAt(i)>10000&&ss.indexOf(cc.charAt(i))!=-1)str+=tt.charAt(ss.indexOf(cc.charAt(i)));
    else str+=cc.charAt(i);
}
return str;
}
function Simplized(cc){
var str='',ss=JTPYStr(),tt=FTPYStr();
for(var i=0;i<cc.length;i++)
{
  if(cc.charCodeAt(i)>10000&&tt.indexOf(cc.charAt(i))!=-1)str+=ss.charAt(tt.indexOf(cc.charAt(i)));
    else str+=cc.charAt(i);
}
return str;
}
function setCookie(name, value)  //cookies設置
{
var argv = setCookie.arguments;
var argc = setCookie.arguments.length;
var expires = (argc > 2) ? argv[2] : null;
if(expires!=null)
{
  var LargeExpDate = new Date ();
  LargeExpDate.setTime(LargeExpDate.getTime() + (expires*1000*3600*24));
}
documents.cookie = name + "=" + escape (value)+((expires == null) ? "" : ("; expires=" +LargeExpDate.toGMTString()));
}
function getCookie(Name)   //cookies讀取
{
var search = Name + "="
if(documents.cookie.length > 0)
{
  offset = documents.cookie.indexOf(search)
  if(offset != -1)
  {
   offset += search.length
   end = documents.cookie.indexOf(";", offset)
   if(end == -1) end = documents.cookie.length
   return unescape(documents.cookie.substring(offset, end))
   }
else return ""
   }
}
var StranLink_Obj=document.getElementById("StranLink")
if (StranLink_Obj)
{
var JF_cn="ft"+self.location.hostname.toString().replace(/\./g,"")
var BodyIsFt=getCookie(JF_cn)
if(BodyIsFt!="1")BodyIsFt=Default_isFT
with(StranLink_Obj)
{
  if(typeof(document.all)!="object")  //非IE瀏覽器
  {
   href="javascript:StranBody()"
  }
  else
  {
   href="#";
   onclick= new Function("StranBody();return false")
  }
  title=StranText("以繁體中文瀏覽",1,0)
  innerHTML=StranText(innerHTML,1,0)
}
if(BodyIsFt=="1"){setTimeout("StranBody()",StranIt_Delay)}
}
</script>
</body>
</html>

- 作者: 插队落户 2006年11月12日, 星期日 18:59  回复(1) |  引用(0) 加入博采

HTML 服务器控件

默认情况下,ASP.NET 文件中的 HTML 元素被视为文本,并且页面开发人员不能以编程方式访问。若要使这些元素能够以编程方式访问,可以通过添加 runat="server" 属性,指示应将 HTML 元素作为服务器控件分析和处理。

唯一的 id 属性允许以编程方式引用该控件。HTML 服务器控件必须驻留在具有 runat="server" 属性的包含 <form> 标记中

web服务器控件定义,如:

<asp:Button id="MyButton"
 Text="label"
 CommandName="command"
 CommandArgument="commandargument"
 CausesValidation="true | false"
 OnClick="OnClickMethod"
 runat="server"/>
 
验证服务器控件
   下面列出的属性适用于所有验证控件
      ControlToValidate 验证控件将计算的输入控件的编程 ID
      Display 指定的验证控件的显示行为。此属性可以为下列值之一
                None,Static,Dynamic(常用)
      Enabled
      ErrorMessage 当验证失败时在 ValidationSummary 控件中显示的错误信息。
                     常用于为验证控件和 ValidationSummary 控件提供各种消息
      IsValid 指示 ControlToValidate 属性所指定的输入控件是否被确定为有效
      Text 验证失败时会在验证控件中显示此消息。如果未设置此属性,则在控件中
            显示 ErrorMessage 属性中指定的文本
 

CompareValidator 控件(下面都省略上面的公共属性)

   
<asp:CompareValidator
 id="ProgrammaticID"
 ValueToCompare="value"
 Type="DataType" 
 Operator="Operator Value" 
 Text="Message to display in control" 
 ForeColor="value"
 BackColor="value" ... 
 runat="server" > 
</asp:CompareValidator>
说明:比较ControlToValidate和ValueToCompare(可以是指定值也可以是控件ID)的大小。
      Operator:ValidationCompareOperator枚举(DataTypeCheck,Equal,GreaterThan,
      GreaterThanEqual,LessThan,LessThanEqual,NotEqual),其中DataTypeCheck只验证
      ControlToValidate的数据类型是否和Type指定的相同(如string)。
 
RangeValidator 控件 
<asp:RangeValidator 
 id="ProgrammaticID" 
 MinimumValue="value"
 MaximumValue="value" 
 Type="DataType" 
 runat="server" >
</asp:RangeValidator>
 

RegularExpressionValidator 控件

<asp:RegularExpressionValidator 
 id="ProgrammaticID" 
 ValidationExpression="expression" 
 runat="server" >
</asp: RegularExpressionValidator>
说明:确定值是否与某个正则表达式所定义的模式相匹配
 

RequiredFieldValidator 控件

<asp:RequiredFieldValidator 
 id="ProgrammaticID"
 InitialValue="value"//关联控件的初始值
 runat="server" >
</asp:RequiredFieldValidator>
 

ValidationSummary 控件

<asp:ValidationSummary 
 id="programmaticID" 
 DisplayMode="BulletList | List | SingleParagraph" 
 EnableClientScript="true | false"
 ShowSummary="true | false"
 ShowMessageBox="true | false" 
 HeaderText="TextToDisplayAsSummaryTitle"
 runat="server"/>
说明:允许在单个位置概述 Web 页上所有验证控件的错误信
   息(每个验证控件的 ErrorMessage 属性),基于 DisplayMode属性的值,
   该摘要可显示为列表、项目符号列表或单个段落
 

CustomValidator 控件

<asp:CustomValidator

id="ProgrammaticID"

ClientValidationFunction="ClientValidateID"//设置客户端验证

OnServerValidate="ServerValidateID"//设置服务断验证

runat="server" >

</asp:CustomValidator>

说明:创建服务器端验证函数,请为执行验证的 ServerValidate 事件提供处理程序。

   可使用作为参数传入事件处理程序的 ServerValidateEventArgs 对象的 Value 属性

   访问来自要验证的输入控件的字符串。验证结果随即存储在 ServerValidateEventArgs

    对象的 IsValid 属性中。

   创建一个客户端验证函数,首先添加服务器端验证函数。然后,将客户端验证脚本函数添

   加到 .aspx 页中,如:
   Function ValidationFunctionName (source, arguments)

   使用 arguments 参数的 Value 属性访问要验证的值。通过设置 arguments 参数的

   IsValid 属性返回验证结果。

例子:

void ServerValidation (object source, ServerValidateEventArgs args)
{    
   args.IsValid = (CheckBox1.Checked == true);
}

- 作者: 插队落户 2006年10月25日, 星期三 18:35  回复(0) |  引用(0) 加入博采

经典正则表达式

正则表达式用于字符串处理,表单验证等场合,实用高效,但用到时总是不太把握,以致往往要上网查一番。我将一些常用的表达式收藏在这里,作备忘之用。本贴随时会更新。

匹配中文字符的正则表达式: [\u4e00-\u9fa5]

匹配双字节字符(包括汉字在内):[^\x00-\xff]

应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

String.prototype.len=function(){return this.replace([^\x00-\xff]/g,"aa").length;}

匹配空行的正则表达式:\n[\s| ]*\r

匹配HTML标记的正则表达式:/<(.*)>.*<\/\1>|<(.*) \/>/

匹配首尾空格的正则表达式:(^\s*)|(\s*$)

应用:javascript中没有像vbscript那样的trim函数,我们就可以利用这个表达式来实现,如下:

String.prototype.trim = function()
{
    return this.replace(/(^\s*)|(\s*$)/g, "");
}

利用正则表达式分解和转换IP地址:

下面是利用正则表达式匹配IP地址,并将IP地址转换成对应数值的Javascript程序:

function IP2V(ip)
{
 re=/(\d+)\.(\d+)\.(\d+)\.(\d+)/g  //匹配IP地址的正则表达式
if(re.test(ip))
{
return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
}
else
{
 throw new Error("Not a valid IP address!")
}
}

不过上面的程序如果不用正则表达式,而直接用split函数来分解可能更简单,程序如下:

var ip="10.100.20.168"
ip=ip.split(".")
alert("IP值是:"+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))

匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

匹配网址URL的正则表达式:http://([/w-]+/.)+[/w-]+(/[/w- ./?%&=]*)?

利用正则表达式去除字串中重复的字符的算法程序:[注:此程序不正确,原因见本贴回复]

var s="abacabefgeeii"
var s1=s.replace(/(.).*\1/g,"$1")
var re=new RegExp("["+s1+"]","g")
var s2=s.replace(re,"")
alert(s1+s2)  //结果为:abcefgi

我原来在CSDN上发贴寻求一个表达式来实现去除重复字符的方法,最终没有找到,这是我能想到的最简单的实现方法。思路是使用后向引用取出包括重复的字符,再以重复的字符建立第二个表达式,取到不重复的字符,两者串连。这个方法对于字符顺序有要求的字符串可能不适用。

得用正则表达式从URL地址中提取文件名的javascript程序,如下结果为page1

s="http://www.9499.net/page1.htm"
s=s.replace(/(.*\/){0,}([^\.]+).*/ig,"$2")
alert(s)

利用正则表达式限制网页表单里的文本框输入内容:

用正则表达式限制只能输入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))"

用正则表达式限制只能输入全角字符: onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\uFF00-\uFFFF]/g,''))"

用正则表达式限制只能输入数字:onkeyup="value=value.replace(/[^\d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"

用正则表达式限制只能输入数字和英文:onkeyup="value=value.replace(/[\W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"

- 作者: 插队落户 2006年09月11日, 星期一 18:16  回复(4) |  引用(0) 加入博采

ASP.NET中如何防范SQL注入式攻击

一、什么是SQL注入式攻击?

  所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击。常见的SQL注入式攻击过程类如:

  ⑴ 某个ASP.NET Web应用有一个登录页面,这个登录页面控制着用户是否有权访问应用,它要求用户输入一个名称和密码。

  ⑵ 登录页面中输入的内容将直接用来构造动态的SQL命令,或者直接用作存储过程的参数。下面是ASP.NET应用构造查询的一个例子:

  System.Text.StringBuilder query = new System.Text.StringBuilder(
  "SELECT * from Users WHERE login = '")
  .Append(txtLogin.Text).Append("' AND password='")
  .Append(txtPassword.Text).Append("'");

  ⑶ 攻击者在用户名字和密码输入框中输入"'或'1'='1"之类的内容。

  ⑷ 用户输入的内容提交给服务器之后,服务器运行上面的ASP.NET代码构造出查询用户的SQL命令,但由于攻击者输入的内容非常特殊,所以最后得到的SQL命令变成:SELECT * from Users WHERE login = '' or '1'='1' AND password = '' or '1'='1'。

  ⑸ 服务器执行查询或存储过程,将用户输入的身份信息和服务器中保存的身份信息进行对比。

  ⑹ 由于SQL命令实际上已被注入式攻击修改,已经不能真正验证用户身份,所以系统会错误地授权给攻击者。

  如果攻击者知道应用会将表单中输入的内容直接用于验证身份的查询,他就会尝试输入某些特殊的SQL字符串篡改查询改变其原来的功能,欺骗系统授予访问权限。

  系统环境不同,攻击者可能造成的损害也不同,这主要由应用访问数据库的安全权限决定。如果用户的帐户具有管理员或其他比较高级的权限,攻击者就可能对数据库的表执行各种他想要做的操作,包括添加、删除或更新数据,甚至可能直接删除表。
 
二、如何防范?

  好在要防止ASP.NET应用被SQL注入式攻击闯入并不是一件特别困难的事情,只要在利用表单输入的内容构造SQL命令之前,把所有输入内容过滤一番就可以了。过滤输入内容可以按多种方式进行。

  ⑴ 对于动态构造SQL查询的场合,可以使用下面的技术:

  第一:替换单引号,即把所有单独出现的单引号改成两个单引号,防止攻击者修改SQL命令的含义。再来看前面的例子,“SELECT * from Users WHERE login = ''' or ''1''=''1' AND password = ''' or ''1''=''1'”显然会得到与“SELECT * from Users WHERE login = '' or '1'='1' AND password = '' or '1'='1'”不同的结果。

  第二:删除用户输入内容中的所有连字符,防止攻击者构造出类如“SELECT * from Users WHERE login = 'mas' -- AND password =''”之类的查询,因为这类查询的后半部分已经被注释掉,不再有效,攻击者只要知道一个合法的用户登录名称,根本不需要知道用户的密码就可以顺利获得访问权限。

  第三:对于用来执行查询的数据库帐户,限制其权限。用不同的用户帐户执行查询、插入、更新、删除操作。由于隔离了不同帐户可执行的操作,因而也就防止了原本用于执行SELECT命令的地方却被用于执行INSERT、UPDATE或DELETE命令。

  ⑵ 用存储过程来执行所有的查询。SQL参数的传递方式将防止攻击者利用单引号和连字符实施攻击。此外,它还使得数据库权限可以限制到只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。

  ⑶ 限制表单或查询字符串输入的长度。如果用户的登录名字最多只有10个字符,那么不要认可表单中输入的10个以上的字符,这将大大增加攻击者在SQL命令中插入有害代码的难度。

  ⑷ 检查用户输入的合法性,确信输入的内容只包含合法的数据。数据检查应当在客户端和服务器端都执行——之所以要执行服务器端验证,是为了弥补客户端验证机制脆弱的安全性。

  在客户端,攻击者完全有可能获得网页的源代码,修改验证合法性的脚本(或者直接删除脚本),然后将非法内容通过修改后的表单提交给服务器。因此,要保证验证操作确实已经执行,唯一的办法就是在服务器端也执行验证。你可以使用许多内建的验证对象,例如RegularExpressionValidator,它们能够自动生成验证用的客户端脚本,当然你也可以插入服务器端的方法调用。如果找不到现成的验证对象,你可以通过CustomValidator自己创建一个。

  ⑸ 将用户登录名称、密码等数据加密保存。加密用户输入的数据,然后再将它与数据库中保存的数据比较,这相当于对用户输入的数据进行了“消毒”处理,用户输入的数据不再对数据库有任何特殊的意义,从而也就防止了攻击者注入SQL命令。System.Web.Security.FormsAuthentication类有一个HashPasswordForStoringInConfigFile,非常适合于对输入数据进行消毒处理。

  ⑹ 检查提取数据的查询所返回的记录数量。如果程序只要求返回一个记录,但实际返回的记录却超过一行,那就当作出错处理。

- 作者: 插队落户 2006年09月11日, 星期一 18:06  回复(0) |  引用(0) 加入博采

ASP.NET中的正则表达式
     Microsoft®.NET Framework 对正则表达式的支持是一流的,甚至在 Microsoft® ASP.NET 中也有依赖正则表达式语言的控件。本文介绍了深入学习正则表达式的基础知识和推荐内容。

    本文主要面向对正则表达式知之甚少或没有使用经验,但却熟悉 ASP.NET、可借助 .NET 编程的初学者。此外,希望本文连同 regular expression cheat sheet 成为有正则表达式使用经验的开发者的手头参考资料或进修资料。本文讨论内容如下:

1.

正则表达式使用历史简介

2.

简单表达式

3.

限定符

4.

元字符

5.

字符类

6.

预定义的集合元字符

7.

表达式示例详细内容

8.

ASP.NET 中的验证

9.

正则表达式 API

10.

免费工具

11.

高级主题概述

12.

小结和其他资源

通常,如果对本文或对正则表达式有疑问,请访问 http://www.aspadvice.com/,通过 regex mailing list 提出问题。编写此文时其中已有 350 多个订户参与。

正则表达式使用历史简介

    正则表达式设计于五十年代,存在至今。正则表达式最初用于描述“正则集”,它们是一些神经生理学家研究的模式。正则表达式最早由数学家 Stephen Kleene 提出,最终由 Ken Thompson 在两种非常流行的文本实用程序 qed 和 grep 中使用。Jeffrey Friedl 在其著作“Mastering Regular Expressions (2nd edition)”中对此作了进一步阐述。建议那些希望更多了解正则表达式理论和历史的人看看这本书。

    在最近的五十年中,正则表达式逐渐从模糊深奥的数学概念发展为在各类工具和软件包中应用的主要功能。尽管数十年来很多 UNIX 工具都支持正则表达式,但仅仅是近十年来,它才在大部分 Windows 开发者工具包中得到体现。在 Microsoft® Visual Basic® 6 或 Microsoft® VBScript 中,即使情况理想,正则表达式仍难以使用。但随着.NET Framework 的推行,正则表达式的支持发展到极点,所有 Microsoft 开发者和所有 .NET 语言都可以使用正则表达式。

    那么,正则表达式究竟是什么呢?正则表达式是一种语言,它可以明确描述文本字符串中的模式。除了简单描述这些模式之外,正则表达式引擎通常可用于遍历匹配,并使用模式作为分隔符来将字符串解析为子字符串,或以智能方式替换文本或重新设置文本格式。正则表达式为解决与文本处理有关的许多常见任务提供了有效而简捷的方式。

    在讨论正则表达式时,通常以正则表达式匹配(或不匹配)的文本为基础分析正则表达式。本文(以及 System.Text.RegularExpressions 类)将在正则表达式交互操作中引用 3 个参与对象:正则表达式的“模式”、“输入”字符串和字符串内的所有模式的“匹配”。

简单表达式

    最简单的正则表达式大家都已熟悉,即文字字符串。特定的字符串可通过文字本身加以描述;像 foo 这样的正则表达式模式可精确匹配输入的字符串 foo。在本例中,也将匹配如下输入:The food was quite tasty,如果希望精确匹配,这可能不是预期结果。

    当然,使用正则表达式匹配等于它自身的精确字符串是没有价值的实现,不能体现正则表达式的真正作用。假如不查找 foo,而是查找以字母 f 开头的所有单词,或所有 3 个字母的单词,那该怎么办?目前,这超出了文字字符串的合理范围。我们需要更加深入地研究正则表达式。下面是一个文字表达式示例及一些匹配的输入。

模式输入(匹配)

foo

foofood、foot、“There's evil afoot.”

限定符

    限定符提供了一种简单方法,用于指定在模式中允许特定字符或字符集自身重复出现的次数。有 3 个非显式限定符:

1.

*,描述“出现 0 或多次”。

2.

+,描述“出现 1 或多次”。

3.

?,描述“出现 0 或 1 次”。

    限定符始终引用限定符前(左边)的模式,通常是单个字符,除非使用括号创建模式组。下面是一些模式示例及匹配的输入。

模式输入(匹配)

fo*

foofoe、food、fooot、“forget it”、funny、 puffy

fo+

foofoe、food、foot、“forget it”

fo?

foo、foe、food、foot、“forget it”、funny、puffy

    除了指定给定模式准确出现 0 或 1 次之外,? 字符还可强制模式或子模式匹配数目最少的字符(如果匹配输入字符串中的多个字符)。

   除了非显式限定符(一般叫做限定符,但为区别于下一组,故称非显式限定符)之外,还有显式限定符。在模式出现次数方面,限定符的概念非常模糊。使用显式限定符则可准确指定数字、范围或数字集。显式限定符位于所应用的模式的后边,这一点与正则限定符一样。显式限定符使用花括号 {} 及其中的数字值表示模式出现次数的上下限。例如,x{5} 将准确匹配 5 个 x 字符 (xxxxx)。如果仅指定一个数字,则表示次数上限;如果数字后跟一个逗号,如 x{5,},表示匹配任何出现次数大于 4 的 x 字符。下面是一些模式示例及匹配的输入。

模式输入(匹配)

ab{2}c

abbc、aaabbccc

ab{,2}c

acabcabbc、aabbcc

ab{2,3}c

abbcabbbc、aabbcc、aabbbcc

元字符

    在正则表达式中,有一种意义特殊的构造,即元字符。目前已知的元字符有很多,如 *?+{} 字符。其他字符在正则表达式语言中都有特殊的含义。这些字符包括:$ ^ . [ ( | ) ] \

     .(句点或点)元字符是最简单但最常用的一个字符。它可匹配任何单字符。如果要指定某些模式可包含任意组合的字符,使用句点非常有用,但一定要在特定长度范围内。此外,我们知道表达式将对包含在较长字符串中的所有模式进行匹配,假如只需要精确匹配模式,又该怎么办?这在验证方案中经常出现,例如,要确保用户输入的邮政编码或电话号码的格式正确。使用 ^ 元字符可指定字符串(或行)的开始,使用 $ 元字符可指定字符串(或行)的结束。通过将这些字符添加到模式的开始和结束处,可强制模式仅匹配精确匹配的输入字符串。如果 ^ 元字符用在方括号 [ ] 指定的字符类的开头,也有特殊的含义。具体内容见下。

    \ (反斜杠)元字符既可根据特殊含义“转义”字符,也可指定预定义集合元字符的实例。同样,具体内容见下。为了在正则表达式中包括文字样式的元字符,必须使用反斜杠进行“转义”。例如,如果要匹配以“c:\”开始的字符串,可使用:^c:\\。注意,要使用 ^ 元字符指出字符串必须以此模式作为开始,然后用反斜杠元字符转义文字反斜杠。

    |(管道)元字符用于交替指定,特别用于在模式中指定“此或彼”。例如,a|b 将匹配包含“a”或“b”的任何输入内容,这与字符类 [ab] 非常类似。

    最后,括号 ( ) 用于给模式分组。它允许使用限定符让一个完整模式出现多次。为了便于阅读,或分开匹配特定的输入部分,可能允许分析或重新设置格式。

下面列出元字符的一些使用示例。

模式输入(匹配)

.

abc123

.*

Abc, 123, 任意字符串, 无字符时也匹配

^c:\\

c:\windowsc:\\\\\c:\foo.txtc:\ 后跟任何其他内容

abc$

abc123abc abc 结束的任意字符串

(abc){2,3}

abcabcabcabcabc

字符类

    字符类是正则表达式中的“迷你”语言,在方括号 [ ] 中定义。最简单的字符类只不过是括号中的一个字符表,如 [aeiou]。在表达式中使用字符类时,可在模式的此位置使用其中任何一个字符(但只能使用一个字符,除非使用了限定符)。请注意,不能使用字符类定义单词或模式,只能定义单个字符。

    要指定任何数值数字,可以使用字符类 [0123456789]。但是,由于这样使用字符不大方便,所以要通过在括号中使用连字符 - 来定义字符的范围。连字符在字符类中有特殊的含义(不是在正则表达式中,因此,准确地说它不能叫正则表达式元字符),且仅在连字符不是第一个字符时,连字符才在字符类中有特殊含义。要使用连字符指定任何数值数字,可以使用 [0-9]。小写字母也一样,可以使用 [a-z],大写字母可以使用 [A-Z]。连字符定义的范围取决于使用的字符集。因此,字符在(例如)ASCII 或 Unicode 表中出现的顺序确定了在范围中包括的字符。如果需要在范围中包括连字符,将它指定为第一个字符。例如:[-.?] 将匹配 4 个字符中任何一个字符(注意,最后的字符是个空格)。另请注意,正则表达式元字符在字符类中不做特殊处理,所以这些元字符不需要转义。考虑到字符类是与其他正则表达式语言分开的一种语言,因此字符类有自己的规则和语法。

    如果使用字符 ^ 作为字符类的第一个字符来否定此类,也可以匹配字符类成员以外的任何字符。因此,要匹配任何非元音字符,可以使用字符类 [^aAeEiIoOuU]。注意,如果要否定连字符,应将连字符作为字符类的第二个字符,如 [^-]。记住,^ 在字符类中的作用与它在正则表达式模式中的作用完全不同。

下面列出操作中使用的一些字符类。

模式输入(匹配)

^b[aeiou]t$

Batbetbitbotbut

^[0-9]{5}$

11111, 12345, 99999

^c:\\

c:\windowsc:\\\\\c:\foo.txtc:\ 后跟任何其他内容

abc$

abc123abc abc 结束的任意字符串

(abc){2,3}

abcabcabcabcabc

^[^-][0-9]$

012、... (不匹配 -0、-1、 -2 等)

    在 .NET Framework 的下一版中,代码名“Whidbey”作为一种新功能被添加到字符类中,称作字符类差 (character class subtraction)。它的主要作用是,允许从一个字符类中减去另一个字符类,可提供更可读的方式描述某些模式。该规范可通过以下地址访问:http://www.gotdotnet.com/team/clr/bcl/TechArticles/techarticles/Specs/Regex/CharacterClassSubtraction.doc。它的语法类似 [a-z-[aeiou]],匹配所有的小写辅音字母。

预定义的集合元字符

    使用目前提供的工具可以完成很多工作。但是,要使用 [0-9] 表示模式中的每个数值数字,或(更糟)使用 [0-9a-zA-Z]表示任何字母数字字符,还有一段相当漫长的过程。为了减轻处理这些常用但冗长模式的痛苦,事先定义了预定义元字符集合。正则表达式的不同实现定义了不同的预定义元字符集合,下面描述的预定义元字符集合在 .NET Framework 中得到 System.Text.RegularExpressions API 的支持。这些预定义元字符的标准语法是,在反斜杠 \ 后跟一个或多个字符。多数预定义元字符只有一个字符,它们的使用很容易,是冗长字符类的理想替代字符。以下是两个示例:\d 匹配所有数值数字,\w 匹配所有单词字符(字母数字加下划线)。例外情况是一些特定字符代码匹配,此时必须指定所匹配字符的地址,如 \u000D 将匹配 Unicode 回车符。下面列出一些最常用的字符类及其等效的元字符。

元字符等效字符类

\a

匹配铃声(警报);\u0007

\b

匹配字符类外的字边界,它匹配退格字符,\u0008

\t

匹配制表符,\u0009

\r

匹配回车符,\u000D

\w

匹配垂直制表符,\u000B

\f

匹配换页符,\u000C

\n

匹配新行,\u000A

\e

匹配转义符,\u001B

\040

匹配 3 位 8 进制 ASCII 字符。\040 表示空格(十进制数 32)。

\x20

使用 2 位 16 进制数匹配 ASCII 字符。此例中,\x2- 表示空格。

\cC

匹配 ASCII 控制字符,此例中是 ctrl-C。

\u0020

使用 4 位 16 进制数匹配 Unicode 字符。此例中 \u0020 是空格。

\*

不代表预定义字符类的任意字符都只作为该字符本身对待。因此,\* 等同于 \x2A(是文字 *,不是 * 元字符)。

\p{name}

匹配已命名字符类“name”中的任意字符。支持名称是 Unicode 组和块范围。例如,Ll、Nd、Z、IsGreek、IsBoxDrawing 和 Sc(货币)。

\p{name}

匹配已命名字符类“name”中不包括的文本。

\w

匹配任意单词字符。对于非 Unicode 和 ECMAScript 实现,这等同于 [a-zA-Z_0-9]。在 Unicode 类别中,这等同于 [\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}]

\W

\w 的否定,等效于 ECMAScript 兼容集合 [^a-zA-Z_0-9] 或 Unicode 字符类别 [^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}]

\s

匹配任意空白区域字符。等效于 Unicode 字符类 [\f\n\r\t\v\x85\p{Z}]。如果使用 ECMAScript 选项指定 ECMAScript 兼容方式,\s 等效于 [ \f\n\r\t\v] (请注意前导空格)。

\S

匹配任意非空白区域字符。等效于 Unicode 字符类别 [^\f\n\r\t\v\x85\p{Z}]。如果使用 ECMAScript 选项指定 ECMAScript 兼容方式,\S 等效于 [^ \f\n\r\t\v] (请注意 ^ 后的空格)。

\d

匹配任意十进制数字。在 ECMAScript 方式下,等效于 Unicode 的 [\p{Nd}]、非 Unicode 的 [0-9]

\D

匹配任意非十进制数字。在 ECMAScript 方式下,等效于 Unicode 的 [\p{Nd}]、非 Unicode 的 [^0-9]

表达式示例

    很多人都喜欢通过示例学习,下面即提供一些表达式示例。要获取更多示例,请访问以下地址中的正则表达式联机数据库:http://www.regexlib.com/

模式说明

^\d{5}$

5 个数值数字,如美国邮政编码。

^(\d{5})|(\d{5}-\d{4}$

5 个数值数字或 5 个数字-短划线-4 个数字。匹配 5 位数字格式的美国邮政编码,或 5 位数字 + 4 位数字格式的美国邮政编码。

^(\d{5}(-\d{4})?$

与前一个相同,但更有效。使用 ? 可使模式中的 4 位数字成为可选部分,而不是要求分别比较不同的两个模式(通过另一种方式)。

^[+-]?\d+(\.\d+)?$

匹配任意有可选符号的实数。

^[+-]?\d*\.?\d*$

与上一个相同,但也匹配空字符串。

^(20|21|22|23|[01]\d)[0-5]\d$

匹配 24 小时制时间值。

/\*.*\*/

匹配 C 语言风格的注释 /* ... */

ASP.NET 中的验证

    ASP.NET 提供了一套验证控件,与使用旧的(或愿意的话使用传统的) ASP 处理任务相比,验证控件使在 Web 窗体上验证输入变得非常容易。其中一个非常有效的验证器是 RegularExpressionValidator,如您所料,它允许您提供必须匹配输入的正则表达式来验证输入。设置控件的 ValidationExpression 属性可指定正则表达式的模式。下面显示了验证邮政代码字段的验证程序:


    使用 RegularExpressionValidator 要注意几个问题:

决不要使用验证程序要验证的控件中的空字符串来激活验证器。只有 RequiredFieldValidator 才可以捕获空字符串。

您无需指定匹配字符的开始与结尾(^$)- 它们是事先假设的。如果添加了开始与结尾,也没有任何影响,不需要这样做。

对于所有验证控件来说,必须在客户端以及服务器端进行验证。如果正则表达式不是 ECMAScript 兼容方式,客户端验证将失败。为了避免这种情况,确保表达式是 ECMAScript 兼容方式,否则只在服务器端进行控件验证。

正则表达式 API

    除了 ASP.NET 验证控件,在.NET 中使用正则表达式的大多数情况都要使用 System.Text.RegularExpressions 命名空间中发现的类。特别是那些您希望熟悉的主类 RegexMatchMatchCollection

    顺便说一下,正则表达式缩写样式 regex 的发音究竟是 /reg-eks/ 还是 /rej-eks/,还有一些争议。本人倾向于后者,但两种发音都有专家赞同,所以选择哪个发音由您自己决定。

    Regex 类有大量的方法和属性,如果您以前没有用过它,可能会感到无所适从。下面汇总了一些最常用的方法:

方法说明

Escape / Unescape

字符串中的转义元字符,用作表达式中的文字。

IsMatch

如果正则表达式在输入字符串中发现匹配,返回“Ture”。

Match

如果在输入字符串中发现匹配,则返回匹配对象。

Matches

如果在输入字符串中发现包含任何或全部匹配,则返回匹配集合对象。

Replace

用给定的替换字符串替换输入字符串中的匹配。

Split

将输入字符串拆分成用正则表达式匹配分开的数组元素时,返回数组字符串。

    除了指定很多方法外,还有一些选项可以指定,通常在 Regex 对象构造函数中。由于这些选项是位屏蔽的一部分,或许可以同时指定这些选项(如,可以同时指定 Multiline 和 Singleline)。

方法说明

Compiled

当在循环中执行许多匹配操作时使用此选项。这可以节省每一循环的分析表达式步骤。

Multiline

它与输入字符串中的行数没有关系。确切地说,它只修改 ^$ 的方式,以便匹配行开始 (BOL) 和行结尾 (EOL),而不是匹配整个输入字符串的开始和结尾。

IgnoreCase

使模式在匹配搜索字符串时忽略大小写。

IgnorePatternWhitespace

允许根据需要在模式中包括任意数量的空白区域,也支持使用 (?# 注释 #) 语法在模式中加入注释。

SingleLine

它与输入字符串中的行数没有关系。更确切地说,它将导致 .(句点)元字符匹配任意字符,而不是除 \n 之外的任意字符(默认情况)。

    使用正则表达式常执行的操作包括:验证、匹配和替换。大多数情况下,可以使用 Regex 类的静态方法完成这些操作,不需要实例化 Regex 类本身。要执行验证,全部要做的就是必建或找到正确的表达式,然后使用 Regex 类的 IsMatch() 方法将表达式应用到输入字符串中。例如,下面的函数演示了如何使用正则表达式验证邮政编码:

private void ValidateZipButton_Click(object sender, System.EventArgs e)
{
String ZipRegex = @"^\d{5}$";
if(Regex.IsMatch(ZipTextBox.Text, ZipRegex))
{
ResultLabel.Text = "ZIP is valid!";
}
else
{
ResultLabel.Text = "ZIP is invalid!";
}
}

    类似的,可以使用静态 Replace() 方法将匹配替换为特定字符串,如下所示:

String newText = Regex.Replace(inputString, pattern, replacementText);

    最后,可以使用如下代码遍历输入字符串的匹配集合:

private void MatchButton_Click(object sender, System.EventArgs e)
{
MatchCollection matches = Regex.Matches(SearchStringTextBox.Text,
MatchExpressionTextBox.Text);
MatchCountLabel.Text = matches.Count.ToString();
MatchesLabel.Text = "";
foreach(Match match in matches)
{
MatchesLabel.Text += "Found" + match.ToString() + " at
position " + match.Index + ".
"; } }

    通常,在您需要指定默认方式以外的方式时,需要实例化 Regex 类的实例。特别是在设置选项时。例如,要创建忽略大小写和模式空白区域的 Regex 实例,然后检索与该表达式匹配的集合,则应使用如下代码:

Regex re = new Regex(pattern,
   RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
MatchCollection mc = re.Matches(inputString);

    本文的下载文件中包括这些示例的完整使用版本,与简单 ASP.NET 页中的一样。

免费工具

    Regulator (http://royo.is-a-geek.com/iserializable/regulator/) - 一种在客户端运行的正则表达式测试工具,通过 Web 服务与 RegexLib 紧密集成,提供对“匹配”、“拆分”和“替换”等的支持。包括性能分析和语法突出显示功能。

    RegexDesigner.NET (http://www.sellsbrothers.com/tools/) - 一种功能强大的可视工具,可帮助构造并测试正则表达式。它可生成 C# 和/或 VB.NET 代码和已编译汇编代码,帮助您将表达式集成到应用程序中。

    Regular Expression Workbench (v2.0) (http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=C712F2DF-B026-4D58-8961-4EE2729D7322) - Eric Gunnerson 开发的工具,用于创建、测试和研究正则表达式。具有“Examine-o-matic”功能,允许将鼠标悬停在正则表达式的上方,对其含义进行解码。

高级主题

    正则表达式有两个不得不说的功能,一个是“命名组”,另一个是“四向处理”(lookaround processing)。由于这些功能很少使用,此处只简单阐述一下。

    使用命名组,您可单独命名匹配组,然后在表达式中使用程序语言引用这些组。如果结合 Replace 方法重新设置输入字符串的格式(通过重新排列顺序、替换输入字符串中的元素),这个功能特别有效。例如,假设日期使用 MM/DD/YYYY 格式的字符串,而您希望日期格式是 DD-MM-YYYY。此时,可编写一个表达式捕获第一种格式,遍历它的匹配集合,并分析每个字符串,然后使用字符串操作建立替换字符串。这需要大量的代码和大量的处理。如果使用命名组,您可完成同样的任务,具体见下:

String MDYToDMY(String input)
{
return Regex.Replace(intput, @"\b(?\d{1,2})/(?\d{1,2}
/(?\d{4})\b", "${day}-
${month}-${year}");
}

    您还可以按编号或按名称引用组。在任何情况下,这种引用通称作“反向引用”。另一个经常使用反向引用的场合在匹配表达式本身,如下这个表达式用于查找重复的字母:[a-z]\1。它将匹配“aa”、“bb”、“cc”,但它不同于 [a-z]{2}[a-z][a-z],后两者是等效的,后两者允许匹配“ab”或“ac”或任何其他两个字母的组合。反向引用允许表达式记住表达式已经分析并匹配过的输入字符串中的部分字符。

   “四向处理”指很多正则表达式引擎所支持的正负 Lookahead 和 Lookbehind 功能。并不是所有的正则表达式引擎都支持验证四向处理。这些构造不使用字符,即使它们可以匹配字符。有些模式可能在不使用四向处理的情况下无法描述。特别是当模式中存在的一部分依赖于另一部分,如果不使用四向处理,则不能描述这样的模式。下面介绍了每个四向处理的语法。

语法说明

(?=...)

正 Lookahead

(?!...)

负 Lookahead

(?<=...)

正 Lookbehind

(?

负 Lookbehind

    密码验证是必需四向处理的一个示例。假定在密码限制中,密码必须介于 4 到 8 个字符,且必须至少包含一个数字。为此,您可以仅在匹配中测试 \d,然后使用字符串操作来测试长度。但要在正则表达式中实现这一切,必须使用 Lookahead。特别是正 lookahead,如下所示:^(?=.*\d).{4,8}$

结论

    正则表达式是一种描述文本模式的极有效方法,它使文本模式成为字符串验证和操作的极好资源。.NET Framework 通过 System.Text.RegularExpressions 命名空间(特别是其中的 Regex 类)提供了对正则表达式的强大支持。使用 API 很简单,但编写正确的正则表达式通常比较费力。但幸运的是,正则表达式可以重复使用,您可以通过网络中的各种联机资源,从中找出其他人设计的表达式,或者在创建表达式遇到困难时得到帮助。

资源

正则表达式库 http://www.regexlib.com/

正则表达式讨论列表 http://aspadvice.com/login.aspx?ReturnUrl=%2fSignUp%2flist.aspx%3fl%3d68%26c%3d16&l=68&c=16

正则表达式论坛 http://forums.regexadvice.com/

正则表达式 Web 日志 http://blogs.regexadvice.com/

Mastering Regular Expressions (O'Reilly),作者 Jeffrey Friedl http://www.regex.info/

.NET 正则表达式参考 http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemTextRegularExpressions.asp

Jscript 正则表达式语法 http://www.msdn.microsoft.com/library/en-us/script56/html/js56jsgrpRegExpSyntax.asp

正则表达式信息 http://www.regular-expressions.info/

作者简介

    Steven A. Smith 是 Microsoft 在 ASP.NET 技术方面的最有价值专家 (MVP),是 ASPAlliance.com 和 DevAdvice.com 的总裁和所有者。此外,他也是 ASPSmith Ltd(一家重点提供 .NET 培训的公司)的所有者和首席教师。他撰写了两本著作:“ASP.NET Developer's Cookbook”和“ASP.NET By Example”,并在 MSDN 和 AspNetPRO 杂志上发表了一些文章。Steve 每年都在各种会议上发表演讲,是 INETA 联络处的成员。Steve 拥有企业管理硕士学位及计算机科学工程理学士学位。

- 作者: 插队落户 2006年09月11日, 星期一 17:54  回复(1) |  引用(0) 加入博采