我們知道,現在網絡上一般的網站,稍微完善一點的,往往都需要用戶先注冊,提供諸如電子郵件、賬號、密碼等信息以后,成為網站欄目的注冊用戶,才可以享受網站一些特殊欄目提供的信息或者服務,比如免費電子郵件、論壇、聊天等,都需要用戶注冊。而對于電子商務網站,比如igo5等大型電子商務網站,用戶需要購買商品,就一定需要詳細而準確的注冊,而這些信息,往往是用戶很隱秘的信息,比如電話、電子郵件、地址等,所以,注冊信息對于用戶和網站都是很重要的資源,不能隨意透露,更加不能存在安全上的隱患。
如果我們也設計一個需要用戶注冊的網站,根據現在的常用技術實現方法,可以在數據庫中建立一個用于存放用戶信息的表,這個表中至少包括用戶賬號字段:UserAccount和用戶密碼字段:Password,當然,實際應用中一個用戶信息表不可能就只有這些信息,往往根據網站服務要求,會適當增加一些其它的信息,以方便網站提供更加完善的服務。一般的,一個用戶信息占用這個用戶信息表的一行也就是一個數據記錄,當用戶登錄或者提交資料的時候,程序將用戶填寫的信息與表中的信息對照,如果用戶賬號和密碼都準確無誤,那幺說明這個用戶是合法用戶,通過注冊;反之,則是非法用戶,不許通過。
然而,是不是這樣就安全了了?是不是這樣就能滿足網站的注冊要求了呢?仔細想想,我們一般將用戶資料直接保存在數據庫中,并沒有進行任何的保密措施,對于一些文件型數據庫比如Access等,如果有人得到這個文件,豈不是所有的資料都泄露無疑?更加重要的是,如果一個不負責任的網管,不需要任何技術手段,就可以查看網站中的任何資料,如果我們的用戶信息在數據庫中沒有加密,對于網管而言,查看這些信息是太簡單了。所以,為了增加安全性,我們有必要對數據庫中的資料進行加密,這樣,即使有人得到了整個數據庫,如果沒有解密算法,也一樣不能查看到數據庫中的用戶信息。但是,在考慮數據庫是否安全之前,我們有必要對我們的數據是否真的那幺重要進行考慮,如果數據只是簡單的一些文件資料,沒有保密的必要,顯然,沒有必要對這些數據進行加密而浪費系統資源、加重程序負擔,如果這些數據具有一定的隱私性,當然就有必要進行加密。所以,在考慮加密以前,我們可以對需要加密的數據做適當的選擇,以免浪費系統資源。
MD5加密算法簡單介紹
在現階段,我們一般認為存在兩種加密方式,單向加密和雙向加密。雙向加密是加密算法中最常用的,它將我們可以直接理解的明文數據加密為我們不可直接理解的密文數據,然后,在需要的時候,可以使用一定的算法將這些加密以后的密文解密為原來可以理解的明文。雙向加密適合于隱秘通訊,比如,我們在網上購物的時候,需要向網站提交信用卡密碼,我們當然不希望我們的數據直接在網上明文傳送,因為這樣很可能被別的用戶“偷聽”,我們希望我們的信用卡密碼是通過加密以后,再在網絡傳送,這樣,網站接受到我們的數據以后,通過解密算法就可以得到準確的信用卡賬號。
單向加密剛好相反,只能對數據進行加密,也就是說,沒有辦法對加密以后的數據進行解密。可能我們立即就會想,這樣的加密有什幺用處?不能解密的加密算法有什幺作用呢?在實際中的一個應用就是數據庫中的用戶信息加密,當用戶創建一個新的賬號或者密碼,他的信息不是直接保存到數據庫,而是經過一次加密以后再保存,這樣,即使這些信息被泄露,也不能立即理解這些信息的真正含義。
MD5就是采用單向加密的加密算法,對于MD5而言,有兩個特性是很重要的,第一是任意兩段明文數據,加密以后的密文不能是相同的;第二是任意一段明文數據,經過加密以后,其結果必須永遠是不變的。前者的意思是不可能有任意兩段明文加密以后得到相同的密文,后者的意思是如果我們加密特定的數據,得到的密文一定是相同的。
MD5CyptoServiceProvider類是.NET中System.Security.Cryptography名字空間的一個類,提供專門用于MD5單向數據加密的解決方法,也是本文中我們用來加密數據庫中密碼的類。在真正進行數據加密之前,我們首先來了解MD5CyptoServiceProvider類中的主要方法:ComputeHash,它將輸入的明文數據數組使用MD5加密以后輸出加密后的密文數據數組。現在,我們就來看一個具體的實例:
'要加密的明文字符串
Dim strPlainText as String = "Encrypt me!"
'用于存放明文字符串的數組
Dim hashedDataBytes as Byte()
Dim encoder as New UTF8Encoding()
'建立MD5CryptoService實例
Dim md5Hasher as New MD5CryptoServiceProvider()
'加密運算
hashedDataBytes = md5Hasher.ComputeHash(encoder.GetBytes(strPlainText))
看完以上的具體實例以后,我們知道,ComputeHash方法只能接受數組作為加密對象,輸出的密文也是數組,因此,在對字符串加密之前,我們必須首先將這些字符串轉化為數組,這就要用到UTF8Encoding類的GetBytes方法,將字符串轉化為數組,而加密以后的結果也是使用數組輸出。
以上我們大致了解了MD5的具體加密實現方法,下面,我們結合數據庫來看看MD5的實際使用。
使用MD5存儲密碼
在前面的介紹中,我們提到網站往往將用戶的賬號、密碼等信息使用非加密的方式保存到數據庫,比如賬號使用類型為VarChar的UserCount字段,同樣,密碼也是采用類型為VarChar的Password字段。但是,如果我們打算采用MD5加密方式存儲密碼信息,就必須改變密碼字段PassWord的類型為16為二進制方式,這個其實我們也不難理解,因為在前面的介紹中,我們知道加密以后的輸出,是使用二進制數組的,所以,這里必須做相應的改變。
當用戶注冊成功,正式建立一個賬號的時候,數據庫中就必須為這個用戶增加一條記錄。以下的程序代碼實現了建立一個賬號的功能,在頁面中,程序要求用戶輸入賬號、密碼等信息,然后,將這些信息作為賬號信息存入名為UserCount的數據表,在這個表中,用戶密碼是使用MD5加密保存的。下面就是實現以上頁面的具體代碼:
<%@ Import Namespace="System.Security.Cryptography" %>
<%@ Import Namespace="System.Text" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<Script runat="server" language="VB">
Sub CreateAccount(sender as Object, e as EventArgs)
'1. 建立數據庫連接
Const strConnString as String = "connection string"
Dim objConn as New SqlConnection(strConnString)
'2. 建立Command對象
Dim strSQL as String = _
"INSERT INTO UserAccount(Username,Password) " & _
"VALUES(@Username, @Password)"
Dim objCmd as New SqlCommand(strSQL, objConn)
'3. SQL參數
Dim paramUsername as SqlParameter
paramUsername= New SqlParameter("@Username", SqlDbType.VarChar, 25)
paramUsername.Value = txtUsername.Text
objCmd.Parameters.Add(paramUsername)
'加密用戶密碼
Dim md5Hasher as New MD5CryptoServiceProvider()
Dim hashedBytes as Byte()
Dim encoder as New UTF8Encoding()
hashedBytes=md5Hasher.ComputeHash(encoder.GetBytes(txtPwd.Text))
Dim paramPwd as SqlParameter
paramPwd = New SqlParameter("@Password", SqlDbType.Binary, 16)
paramPwd.Value = hashedBytes
objCmd.Parameters.Add(paramPwd)
'加入數據庫
objConn.Open()
objCmd.ExecuteNonQuery()
objConn.Close()
End Sub
</script>
<Form runat="server">
<h1>建立一個賬號</h1>
用戶名:<asp:TextBox runat="server" id="txtUsername" />
<br />密碼:
<asp:TextBox runat="server" id="txtPwd" TextMode="Password" />
<p><asp:Button runat="server" Text="建立用戶賬號" onClick="CreateAccount" /></p>
</form>
在以上程序實現的頁面中,“用戶名”和“密碼”輸入框要求用戶輸入自己的賬號和密碼,用戶輸入自己的信息以后,按“建立用戶賬號”按鈕,就可以建立一個賬號并且存入數據庫。我們同時需要特別注意,因為以上的程序使用到了MD5加密和數據庫等功能,所以,在代碼最開頭,我們引入了幾個稍微特別一點的名字空間,這是不可缺少的。
我們可以看到,PassWord字段的信息是二進制方式保存的,即使數據庫被人取得,也不可能知道密碼具體是什幺意思。當然,密碼也就不會泄露
使用MD5鑒別是否合法用戶
既然用戶密碼是按照MD5加密以后保存在數據庫中的,我們知道,MD5是單次加密算法,所以,不可能將加密以后的信息轉為明文,也就是說,已經沒有辦法知道。這就出現一個問題,如果用戶使用賬號、密碼登錄,怎么知道用戶提供的密碼是否準確呢?
這就不得不提到我們前文說到的MD5的特征,我們知道,任意一段明文數據,經過加密以后,其結果必須永遠是不變的,也就是說,如果需要驗證用戶密碼是否正確,只需要將用戶當前提供的密碼使用MD5加密,然后和數據庫中保存的密碼字段比較就可以了。以下代碼就可以實現這個功能:
<%@ Import Namespace="System.Security.Cryptography" %>
<%@ Import Namespace="System.Text" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<Script runat="server" language="VB">
Sub Login(sender as Object, e as EventArgs)
'1. 建立數據庫連接
Const strConnString as String = "connection string"
Dim objConn as New SqlConnection(strConnString)
'2. 建立Command對象
Dim strSQL as String = "SELECT COUNT(*) FROM UserAccount " & _
"WHERE Username=@Username AND Password=@Password"
Dim objCmd as New SqlCommand(strSQL, objConn)
'3. SQL參數
Dim paramUsername as SqlParameter
paramUsername = New SqlParameter("@Username", SqlDbType.VarChar, 25)
paramUsername.Value = txtUsername.Text
objCmd.Parameters.Add(paramUsername)
'加密密碼信息
Dim md5Hasher as New MD5CryptoServiceProvider()
Dim hashedDataBytes as Byte()
Dim encoder as New UTF8Encoding()
hashedDataBytes = md5Hasher.ComputeHash(encoder.GetBytes(txtPwd.Text))
Dim paramPwd as SqlParameter
paramPwd = New SqlParameter("@Password", SqlDbType.Binary, 16)
paramPwd.Value = hashedDataBytes
objCmd.Parameters.Add(paramPwd)
objConn.Open()
Dim iResults as Integer = objCmd.ExecuteScalar()
objConn.Close()
If iResults = 1 then
'正確
Else
'錯誤
End If
End Sub
</script>
<Form runat="server">
<h1>Login</h1>
用戶賬號: <asp:TextBox runat="server" id="txtUsername" />
<br />密碼:
<asp:TextBox runat="server" id="txtPwd" TextMode="Password" />
<p><asp:Button runat="server" Text="Login" onClick="登錄" />
</form>
使用加密方式保存密碼到數據庫的限制
在決定是否使用加密方式保存密碼以前,我們還需要考慮一些問題。因為MD5是單次加密算法,加密以后的信息不可以解密,所以,如果用戶丟失密碼,任何人都很難找到用戶原來的密碼,這時候,網站也就相應的失去一個很重要的功能,那就是用戶提供其他信息來取得忘記的密碼的功能,這不能不說是網站的一個大缺陷。另外,采用這樣的加密方式,必須完全修改以前的用戶資料,要求用戶完全重新注冊,這也是這種方法比較困難的一個地方。
總 結
以上我們詳細介紹了MD5加密用戶密碼的實現方法,同時,也介紹了采用加密密碼方式以后,用戶鑒別的實現。并討論了使用這種加密方式的應用限制。在實際應用中,我們可以將次方法做適當的修改和補充,以更加適合我們的應用需要。
作為一個ASP程序員,你不會懷疑提高Web應用程序性能的重要性。為了讓程序運行的更快一些,你可能一直忙于優化數據庫或COM組件。如果這些你都做過了,你想到過靠加快最終生成HTML代碼在瀏覽器中的顯示速度來提高性能嗎?對于最終用戶來說,如果頁面能顯示的更快,你就能贏得更多的贊譽。
提高HTML在瀏覽器中顯示的速度可以通過一些鮮為人知的技術來實現。
1.使用表格嵌套?
在頁面中建立復雜的結構,一般通過在頁面中放置HTML表格來實現。如果要建立一個這樣的頁面:這個頁面有一個頂部導航欄一個左邊導航欄,一個右邊的內容區。可以用一個兩行兩列的大表格來建立它。第一行中,合并兩個列,然后插入一個頂部導航欄。第二行左邊的列中,插入一個表格來顯示導航按鈕。右邊的欄中,放置一個表格來實際內容。(見圖一)這樣嵌套的表格生成的代碼是這樣的:
<TABLE BORDER="0">
<TR>
<TD COLSPAN="2"><!-- content for top nav bar --></TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP"><!-- content for left nav bar --></TD>
<TD ALIGN="LEFT" VALIGN="TOP"><!-- content for body of page --></TD>
</TR>
</TABLE>
但是,實際上,瀏覽器找到<TABLE>標簽的時候并不是立刻把頁面顯示到屏幕上,除非它找到相應的結束標簽</TABLE>。所以,如果你的整個頁面在一個表格里的話,在收到最后一個</TABLE>之前,什么也不會顯示出來,這樣,這個頁面將在整個文件全部下載以后才能被用戶所看到。在頁面數據量比較大的時候(比如搜索引擎的搜索結果),這個特性會導致暫時的停頓。為了防止出現這種情況,可以在制作的時候把頁面分成許多小的表格。在每一個<TABLE>到相應的</TABLE>這一部分HTML代碼下載完的時候,瀏覽器就會把它顯示出來。在訪問者看來頁面是漸漸的,一部分一部分,越來越多的出現在屏幕上的。感覺上,這樣的頁面顯示速度比下載完整個文件再一次顯示出來更快。
按照這個原則來研究前面的例子,應該把頁面中整個的大表分成三個單獨的表。用第一個表顯示頂部的導航欄,調節它的寬度,使它足夠容納所有的內容,在一個<TABLE></TABLE>代碼段中完成它。頁面下半部分,左邊第二個表排成一列。使用第三個表容納實際內容。(見圖二)因為每一個部分都是一個完整的表格,所以,每一部分代碼下載后都會立刻被顯示出來。這樣,頂部和左邊的導航欄將比頁面的其它部分更顯顯示出來。用戶會在這個時候想象頁面開始下載,很快就能顯示在屏幕上。這樣比起讓用戶在較長時間內一直面對一個空白屏幕要好得多。
修改過的代碼是這樣的:
<TABLE BORDER="0" WIDTH="100%">
<TR>
<TD ALIGN="CENTER" VALIGN="TOP"><!-- content for top nav bar --></TD>
</TR>
</TABLE>
<TABLE BORDER="0" ALIGN="LEFT">
<TR>
<TD ALIGN="LEFT" VALIGN="TOP"><!-- content for left nav bar --></TD>
</TR>
</TABLE>
<TABLE BORDER="0">
<TR>
<TD ALIGN="LEFT" VALIGN="TOP"><!-- content for page body --></TD>
</TR>
</TABLE>
2.也要記住關閉其他的標記
在上面的例子中,我們僅僅早一些關閉<TABLE>標記,就能讓頁面在瀏覽器顯示的更快些。以此類推,還有一些類似的標記也有同樣的特性。
比如產生列表框和組合框<OPTION>標記和產生列表項的<LI>標記。通常,ASP程序員存取數據庫,并把數據送入通過<OPTION>建立的列表框或組合框中,這時候在代碼中寫上一個關閉<OPTION>標記,這樣簡單的改變也能使頁面在瀏覽器中顯示的更快。
不要使用這樣的代碼:
Do while not objRS.EOF
strOptionList = strOptionList & "<OPTION VALUE=""" & objRS("ID") &_""">"& _objRS("ProductName")
objRS.MoveNext
Loop
Response.Write "<SELECT SIZE=""1"">" & strOptionList & "</SELECT>"
要使用這樣的代碼:
Do while not objRS.EOF
strOptionList = strOptionList & "<OPTION VALUE=""" & objRS("ID") & _ """>" & objRS("ProductName") & "</OPTION>"
objRS.MoveNext
Loop
Response.Write "<SELECT SIZE=""1"">" & strOptionList & "</SELECT>"
不要使用這樣的代碼:
<UL>
<LI>Apples
<LI>Oranges
<LI>Bananas
</UL>
使用這樣的代碼:
<UL>
<LI>Apples</LI>
<LI>Oranges</LI>
<LI>Bananas</LI>
</UL>
現在看看,你的頁面在瀏覽器中是不是顯示的快了?
請不要輕視這些改變對提高ASP程序性能的重要性。也許,在你能找到的“技巧與提示”一類的書或在線資料中,很少提到過通過優化HTML代碼來使你的程序運行的更快。但是,在實際中應用這些技術,確實能使程序性能得到很大的提高。
看過數據庫的備份與還原。大多數都是用組件來完成的。其實可通過sql語句來完成。
由于時間關系,未對參數進行驗證和界面美化。代碼僅供參考。
共計4個文件:
conn.asp
<%
conn="Provider=SQLOLEDB.1;Persist Security Info=false;Server=127.0.0.1;UID=sa;pwd=www.zhi.net;database=master"
function rec(rs,sql)
set rs = server.CreateObject("ADODB.Recordset")
rs.Activeconnection = conn
rs.Source = sql
rs.CursorType = 0
rs.Cursorlocation = 3
rs.LockType = 1
rs.Open
if rs.eof and rs.bof then
rec= false
else
rec= true
end if
end function
function cmd(sql)
dim cmd1
set cmd1 = server.CreateObject("ADODB.Command")
cmd1.ActiveConnection = conn
cmd1.CommandText = sql
cmd1.CommandType = 1
cmd1.CommandTimeout = 0
cmd1.Prepared = true
cmd1.Execute()
end function
function cdb(rs)
rs.close()
set rs=nothing
end function
%>
dev.asp
<!--#include file="conn.asp"-->
<%
if request("AddDev") <> "" then
sql="select name,phyname from master..sysdevices where status=16 and name='"&request("devname")&"'"
rec chk,sql
if chk.eof and chk.bof then
sql = "sp_addumpdevice 'disk','"&request("devname")&"','"&request("phyname")&"'"
cmd sql
response.Write "<script language=javascript>window.location=reload;</script>"
else
response.Write "<script language=javascript>alert('數據庫中已存在"&request("devname")&"設備!');window.location='cmd.asp';</script>"
end if
end if
if request("deldev") <> "" then
sql = "sp_dropdevice '"&request("devname")&"'"
cmd sql
response.Write "<script language=javascript>window.location=reload;</script>"
end if
rec li,"select name,phyname from master..sysdevices where status=16"
if li.eof and li.bof then
response.Write "<font color=#ff0000>請新建備份設備用來備份還原數據庫</font>"
else
%><style type="text/css">
<!--
body {
margin-left: 0px;
margin-top: 0px;
}
-->
</style>
<table width="60%" border="0" cellspacing="0">
<tr>
<td width="29%">名稱:</td>
<td width="71%">位置:</td>
</tr>
<%
i=0
while not li.eof
i=i+1
%>
<tr>
<td><%=li(0)%></td>
<td><%=li(1)%> <a href='dev.asp?DelDev=1&devname=<%=li(0)%>'>刪除此設備</a></td>
</tr>
<%
li.movenext
wend
response.Write "<script language=javascript>top.document.all.dev.height='"&(i+1)*25&"';</script>"
cdb li
%>
</table>
<%
end if
%>
default.asp
<%@LANGUAGE="VBSCRIPT" CODEPAGE="936"%>
<!--#include file="conn.asp"-->
<%
'*************************************************
'數據庫備份與還原
'編 程:魔術師·楊(MagicYang.CN)
'完成日期:2004-4-11
'說明:由于時間關系,未對數據做安全性驗證。
'應用時請對數據進行驗證,確保安全。
'QQ:1168064 歡迎大家互相交流
'*************************************************
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>數據庫的備份與還原</title>
</head>
<body>
<%
%>
<table width="80%" border="0" align="center">
<tr>
<td width="21%" valign="top">數據庫設備:</td>
<td width="79%">
<iframe name="dev" src="dev.asp" width="100%" height="200" frameborder="0" scrolling="no"></iframe>
</td>
</tr>
<tr>
<td>添加設備:</td>
<td>設備名稱:
<input name="devname" type="text" id="devname" size="10" maxlength="10"></td>
</tr>
<tr>
<td> </td>
<td>文件路徑:
<input name="phyname" type="text" id="phyname" size="20" maxlength="50">
<input type="button" value="添加設備" onClick="document.all.dev.src='dev.asp?AddDev=1&devname='+document.all.devname.value+'&phyname='+document.all. phyname.value;"></td>
</tr>
<tr>
<td valign="top">備份/還原:</td>
<td>
數據庫名稱:
<%
rec li,"select name from master..sysdatabases where status=16"
%>
<select name="b_data">
<%
while not li.eof
response.Write "<option value="&li(0)&">"&li(0)&"</option>"
li.movenext
wend
cdb li
%>
</select><br>
備份到設備:<%
rec li,"select name,phyname from master..sysdevices where status=16"
%>
<select name="b_dev">
<%
while not li.eof
response.Write "<option value="&li(0)&">"&li(0)&"(文件:"&li(1)&")</option>"
li.movenext
wend
cdb li
%>
</select><br>
<input name="按鈕" type="button" value="備份數據庫" onClick="window.location='cmd.asp?action=backup&database='+document.all.b_data.value+'&dev='+document.all. b_dev.value;">
<input name="按鈕" type="button" value="還原數據庫" onClick="window.location='cmd.asp?action=RESTORE&database='+document.all.b_data.value+'&dev='+document.all. b_dev.value;"> </td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
</table>
</body>
</html>
cmd.asp
<!--#include file="conn.asp"-->
<%
on error resume next
if request("action") <> "" then
if request("action")="backup" then
sql ="BACKUP DATABASE "&request("database")&" To "& request("dev")
cmd sql
if err.number > 0 then
response.Write "<script language=javascript>alert('數據庫"&request("database")&"備份失敗!');window.location='default.asp';</script>"
else
response.Write "<script language=javascript>alert('數據庫"&request("database")&"備份成功!');window.location='default.asp';</script>"
end if
else
sql ="RESTORE DATABASE "&request("database")&" From "& request("dev")
cmd sql
if err.number > 0 then
response.Write "<script language=javascript>alert('數據庫"&request("database")&"還原失敗!');window.location='default.asp';</script>"
else
response.Write "<script language=javascript>alert('數據庫"&request("database")&"還原成功!');window.location='default.asp';</script>"
end if
end if
end if
%>
Active Server Page,簡稱ASP,是:
. 連接網友界面(HTML)和商業邏輯(Business Logic);
. 提供一致的、容易使用的、有狀態保持的、基于WEB的客戶端;
. 為那些需要事務處理的WEB 應用提供應用程序環境。
ASP不是:
. 實現商業邏輯(Business Logic)的地方;商業邏輯應該通過COM+、MTS或者數據庫來實現。
ASP的使用者應該有下面的教訓:
. 開發應用程序,而不是開發一個一個的孤立ASP頁面;
. 對輸入和輸出進行緩存;
. 在發布之前要測試;
. 選擇性能較好的部件;
. 減少數據庫的存取:緩存變換后的結果;
. 使用MSMQ來處理有時間延遲的工作;
站點設計
. 你的站點想提供什么?
. 信息架構:80/20準則;
. 站點導航;
. 頁面布局;
. 可用性;
. 使用ALT和Title屬性;
. 不使用圖片或者Image Map的導航;
. 適合大多數低版本瀏覽器,考慮他們對ActiveX、RDS、XML、DHTML、Java Applet的支持狀況;
. 屏幕分辨率和屏幕顏色數
. 是否支持WebTV、PDA…?
. 設置IMG的width和height屬性。
. 非瀏覽器的訪問,如自動機器人(Spider);
. 使用幀(Frame)?
. 使用Cookies的個性化;
. 避免壞連接;
. 使用meta標簽;
. 內容審核;
. 內容檢索;
. 結果反饋:用戶反饋和跟蹤;
. 減少下載時間;
三層、四層應用設計
可讀性、可維護性
. 使用注釋;
. 在VBScript腳本中使用<%Optio. Explicit%>;
. 使用字符串變量存儲SQL字符串:便于調試;
. 使用Server.MapPath和相對路徑;
. 使用ADODB.INC或者<!—metadata typelib=somelib file=somedll-->來引用常量,不要直接使用常量數值。
. 指定ADO調用的缺省參數,避免出錯;
. 使用庫或者部件來封裝代碼。
正確的方法:
. 使用Server.URLEncode
. 錯誤捕獲和處理
國際化:
. 使用<%CodePage%>
. 使用Session.CodePage
. 在IIS5.0中,Response.write支持UTF8
其他:
. 使用#include 重用代碼
. 使用分頁技術
站點安全:
. 客戶身份驗證
. 輸入驗證
. #include 文件不要使用.INC后綴,使用.ASP或者設置.INC的應用程序映射
. 把MDB文件存放在非WEB路徑下;
. 使用ADSI做安全管理
Session和Application狀態
Session的使用:
. 使用起來很方便但是很有問題;
. HTTP是一個無狀態的協議;
. 設計購物推車特別有用;
. 不利于可伸縮性設計(Scalability);
. 在不需要Session的頁面中使用<%EnableSessionState=false%>
. 盡可能完全避免使用Session;
. 在多個web服務器情況下不適合;
. 某些部件使Session運行在單一線程模式,減少了吞吐量;
. 消耗內存;
. Session有超時的問題
. 需要客戶端的瀏覽器打開cookie設置;
. 不要在session中保存recordset,或者緩存connection對象;
. 在global.asa不要使用空的Session_OnEnd;
. 可選方案:
. cookies
. 直接狀態編碼:簡單、容易、不安全
. 后端數據庫的ID作為狀態值
. querystring 參數
. 如amazon的url方式
. 隱藏的表單
Application變量:
. 共享變量
. 不能持久保存
. 多個web服務器時不行,除非只是只讀變量。
緩存
. 對靜態內容非常理想
. 不要使用Response.Expires=0,使用負數:
. Response.Expires=-10000;
. Response.AddHeader “Pragma”,”no-cache”
. Response.AddHeader “cache-control”,”no-store”
. 服務器緩存
. proxy緩存
. 客戶端緩存
部件
. 性能
. 伸縮性
. 分離商務邏輯和頁面表現
. 被ASP或其他環境重用
. 事務處理
. 類型安全
. 存取操作系統特性
. 保護知識產權
. 在下列情況下使用Server.CreateObject:
. MTS事務處理
. 上下文安全性
. ASP內部部件
. OnStartPage、OnEndPage
. 使用<Object RunAt=server>延遲對象初始化
. 是否保存到Session或者Application變量中
性能
. Response緩沖:Response.Buffer=True
. 關閉Connection并:set Connection=Nothing
. 使用局部變量
. 用<Object >代替Server.CreateObject
. 不要使用Session和Application變量
. 不要將COM對象存儲在Session或者Application變量中
. 關閉腳本調試
. 避免重復的字符串相加
. 在費時的頁面頂端使用Response.IsClientConnected
. 使用MSMQ
. 不要在Session或者Application中存儲大數組
. 不要ReDim 數組
. 將集合類型的對象賦給臨時變量
. 減小微處理器的最大線程數(運行regedt32,在HKEY_LOCAL_MACHINE\SYSTEM\ CurrentControlSet\Services\w3SVC\ASP\Parameters,增加ProcessorThreadMax,減小這個值,看看性能的變化;或者增大這個值。)
. 設置AspScriptEngineCacheMax,使它等于ProcessorThreadMax*CPU個數。缺省的是30;(在系統路徑下:\system32\inetsrv/adminisamples下,鍵入adsutil.vbs,設置/w3svc/AspScriptEngineCacheMax);
. 減少Session.Timeout;
. 在MMC中,設置ASP應用程序緩沖為有效。
把某些工作交給客戶端:
. CSS、DHTML
. XML
. RDS
. Remote Scripting
. Xmlhttp
. 客戶端驗證
. 減小文件大小
. 盡可能避免https和SSL
. 使用Response.End測試性能
數據庫
. 減少數據庫存取訪問;
. 緩存變換后的結果;
. 使用ODBC連接池和OLEDB資源池;
. 使用系統DSN或者非DSN,不要使用DSN或者文件DSN;
. 使ADO運行在雙線程模式(Both-threaded):makefre.bat;
. 使用ADO的Field對象;
. GetString或者GetRows比較快;
. RDS和XML把負載嫁到客戶端;
. 不要使用Select *,把字段寫出來;
. 盡量使用SQ. Server 7,不要使用Access;
. 使用SQ. Server的特性:存儲過程、Job、Join、sort、group
. 使用SQ. Analysis,優化SQL的性能
. 使用索引
. 本地使用Name-pipes,遠程使用Sockets
. 準確地指定Command Type
IIS 5的新特性
. 可靠的重新啟動
. ASP性能提高
. Server.Transfer比Server.Redirect更好
. Server.Execute
. Server.GetLastError
1、聲明VBScript變量
在ASP中,對vbscript提供了強勁的支持,能夠無縫集成vbscript的函數、方法,這樣給擴展ASP的現有功能提供了很大便利。由于ASP中已經模糊了變量類型的概念,所以,在進行ASP與vbscript交互的過程中,很多程序員也慣于不聲明vbscript的變量,這樣加重了服務器的解析負擔,進而影響服務器的響應請求速度。
鑒于此,我們可以象在VB中強制用戶進行變量聲明一樣在vbscript中強制用戶進行變量聲明。實現方法是在ASP程序行首放置<% option explicit%>。
2、對URL地址進行編碼
在我們使用asp動態生成一個帶參數URL地址并進行跳轉時,在IE中解析很正常,但在NetScrape瀏覽時卻有錯誤如下:
HTTP Error 400
400 Bad Request
Due to malformed syntax, the request could not be understood by the server.
The client should not repeat the request without modifications.
解決方法是對生成的URL參數使用ASP內置server對象的URLencode方法進行URL編碼,例子如下:
<%
URL="xur.asp"
var1="username=" & server.URLencode("xur")
var2="&company=" & server.URLencode("xurstudio")
var3="&phone=" & server.URLencode("021-53854336-186")
response.redirect URL & "?" & var1 & var2 & var3
%>
3、清空對象
當使用完對象后,首先使用Close方法來釋放對象所占用的系統資源;然后設置對象值為“nothing”釋放對象占用內存。當年,我就是在一張頁面上創建了百余個沒有清空對象的記錄集而崩潰了我的IIS 。下面的代碼使用數據庫內容建立一個下拉列表。代碼示例如下:
<% myDSN="DSN=xur;uid=xur;pwd=xur"
mySQL="select * from authors where AU_ID<100"
set conntemp=server.createobject("adodb.connection")
conntemp.open myDSN
set rstemp=conntemp.execute(mySQL)
if rstemp.eof then
response.write "數據庫為空"
response.write mySQL
conntemp.close
set conntemp=nothing
response.end
end if%>
<%do until rstemp.eof %>
<%
rstemp.movenext
loop
rstemp.close
set rstemp=nothing
conntemp.close
set conntemp=nothing
%>
4、使用字符串建立SQL查詢
使用字符串來建立查詢并不能加快服務器的解析速度,相反,它還會增加服務器的解析時間。但在這里仍然推薦使用字符串代替簡單的查詢語句來進行查詢。這樣做的好處是,可以迅速發現程序問題所在,從而便利高效地生成程序。示例如下:
<%mySQL= ""select * "
mySQL= mySQL & "from publishers"
mySQL= mySQL & "where state='NY'"
response.write mySQL
set rstemp=conntemp.execute(mySQL)
rstemp.close
set rstemp=nothing
%>
5、使用case進行條件選擇
在進行條件選擇的時候,盡量使用case語句,避免使用if語句。使用case語句,可以使程序流程化,執行起來也比if語句來的快。示例如下:
<%
FOR i = 1 TO 1000
n = i
Response.Write AddSuffix(n) & "
"
NEXT
%>
<%
Function AddSuffix(num)
numpart = RIGHT(num,1)
SELECT CASE numpart
CASE "1"
IF InStr(num,"11") THEN
num = num & "th"
ELSE
num = num & "st"
END IF
CASE "2"
IF InStr(num,"12") THEN
num = num & "th"
ELSE
num = num & "nd"
END IF
CASE "3"
IF InStr(num,"13") THEN
num = num & "th"
ELSE
num = num & "rd"
END IF
CASE "4"
num = num & "th"
CASE ELSE
num = num & "th"
END SELECT
AddSuffix = num
END FUNCTION
%>
6、使用adovbs.inc文件中定義的常量打開記錄集
打開記錄集時,可以定義記錄集打開的游標類型和鎖定類型。在adovbs.inc文件中定義了一些常量來定義這些類型。adovbs.inc文件保存在\inetpub\iissamples\IISamples目錄下面。下面列舉幾個常用的游標類型和鎖定類型。
游標類型:adOpenFowardOnly游標只能向前;adOpenKeyset游標可向前或者向后,如一用戶添加記錄,新記錄不會出現在記錄集中;adOpenDynamic游標動態隨意;adOpenStatic記錄集不對其它用戶造成的記錄修改有所反映。
鎖定類型:adLockReadOney不能修改記錄集中的記錄;adLockPessimistic在編輯一條記錄時鎖定它;adLockOptimstic調用記錄集Update方法時才鎖定記錄;adLockBatchOpeimstic記錄只能成批更新。
<!--#INCLUDE VIRTUAL="/ADOVBS.INC" -->
<%
connectme="DSN=xur;uid=xur;pwd=xur"
sqltemp="select * from publishers where name='xur'"
set rstemp=Server.CreateObject("adodb.Recordset")
rstemp.open sqltemp, connectme, adOpenStatic,adLockOptimstic
response.write rstemp.recordcount & " records in
" & sqltemp
rstemp.close
set rstemp=nothing
%>
7、避免在使用global.asa文件中進行對象定義
由于global.asa文件中的內容可以為站點內所有文件引用,無疑,在global.asa文件中進行對象定義可以省去很多重復工作。比如在global.asa中的application_onstart函數中進行如下定義:
<%SUB application_onstart
set application("theCONN")=server.createobject("adodb.connection")
END SUB %>;
這樣就可以在站點任何代碼中做類似引用:
<%
mySQL="select * from publishers where state='xur'
set rstemp=application("theconn").execute(mySQL)
%>
同樣地,可以在session_onstart函數中創建記錄集對象
<%SUB session_onstart
set session("rstemp")=server.createobject("adodb.recordset")
END SUB %>
然后在站點也面中進行如下引用:
<%
mySQL="select * from publishers where state='xur'
set session("rstemp")=conntemp.execute(mySQL)
%>
但這樣做的同時也有很大的負面影響,由于Application和session變量都只有在關閉網站的時候才釋放占用的資源,所以session參數會浪費大量不必要內存,而且此時application變量成為服務器性能的瓶頸。
解決方法:建立定義對象asp頁面,在需要進行調用這些對象的頁面上,引入這張asp頁面。假設定義對象的asp頁面名稱為define.asp,則只要在對應asp頁面中加入以下語句就能引入該頁面。
<!--#INCLUDE VIRTUAL="/define.asp" -->
在進行頁面引進時,最好在待引進的asp文件中不要包含<%@LANGUAGE="VBSCRIPT"%>語句。因為在asp文件中,只能有一句由@來定義的腳本解析語言。
8、安全防護
asp提供了很好的代碼保護機制,所有的asp代碼都在服務器端執行而只返回給客戶端代碼執行結果。即便這樣,在老版本的IIS中還可以在文件名后面家::$DATA來查看asp的源代碼,這已經屬于Web Server安全范疇不在本文討論范圍內。下面提出兩點簡單的安全注意事項。
雖然在asp中建議引入文件以inc作為擴展名,在這里仍建議以asp作為引文件的擴展名。當這些代碼在安全機制不好的Web Server上運行時,只需在地址欄上輸入引入文件的地址(inc為擴展名),就可以瀏覽該引入文件的內容,這是由于在Web Server上,如果沒有定義好解析某類型(比如inc)的動態連接庫時,該文件以源碼方式顯示。
不要把數據庫文件放在網站結構內部,這樣,當惡意人士獲取數據庫路徑后,就可以輕易獲取該數據庫,進而肆意更改數據庫內容。比較好的做法是,為數據庫建立DSN(Date Source Name),而在進行數據庫訪問時直接訪問該DSN。
技巧之一:提高使用Request集合的效率 訪問一個ASP集合來提取一個值是費時的、占用計算資源的過程。因為這個操作包含了一系列對相關集合的搜索,這比訪問一個局部變量要慢得多。因此,如果打算在頁面中多次使用Request集合中的一個值,應該考慮將其存貯為一個局部變量。例如將代碼寫成下面的形式以加快腳本引擎處理速度:
strTitle=Request.Form("Title")
strFirstName=Request.Form("FirstName")
strLastName=Request.Form("LastName")
If Len(strTitle) Then strTitle=strTitle & " "
If strFirstName="" Then strFullName=strTitle & " " & strLastName
Elseif Len(strFirstName)=1 Then
strFullName=strTitle & strFirstName & ". " & strLastName
Else
strFullName=strTitle & strFirstName & " " & strLastName
End If
技巧之二:直接訪問適當的集合
如果不是別無選擇,否則不要使用strPage=Request("page")的這樣的形式來獲取參數,因為這將按順序搜索全部的集合—QueryString、Form、Cookies、ClientCertificate、ServerVarible直到發現第一個匹配值的名稱。這樣做比直接訪問適當的集合效率低,并且是不安全的,除非能絕對保證這個值不會出現在另外一個集合中。
例如,可能希望搜索滿足客戶請求的WEB服務器名稱,這通過出現在每個查詢中的Request.ServerVarables集合中尋找“SERVER_NAME”來實現。然而,假如其他的集合也包含名為“SERVER_NAME”的值(鍵名不區分大小寫),當使用Request("server_Name")時,就會得到錯誤的結果。總而言之,應盡可能直接訪問適當的集合。
技巧之三:在費時操作前使用Response.IsClientConnected屬性
使用Response.IsClientConnected是觀察用戶是否仍連到服務器并正在載入ASP創建的網頁的有用方式。如果用戶斷開連接或停止下載,我們就不用再浪費服務器的資源創建網頁,因為緩沖區內容將被IIS丟棄。所以,對那些需要大量時間計算或資源使用較多的網頁來說,值得在每一階段都檢查游覽者是否已離線:
…… Code to create first part of the page
If Response.IsClientConnected Then
Response.Flush
Else
Response.End
End If
…… Code to create next part of page
技巧之四:優化ASP中的ADO操作
通常面言,數據構成了WEB站點的實際內容。所以,優化ADO操作以加速ASP代碼執行,十分有用:
a. 僅選擇所需的列:當打開ADO記錄集時,除非需要獲得所有的列,否則不應自動地使用表名(即SELECT *)。使用單獨的列意味著將減少發送到服務器或從服務器取出的數據量。即使需要使用全部列,單獨地命名每個列也會獲得最佳的性能,因為服務器不必再解釋這些列的名字。
b. 盡可能的使用存儲過程。存儲過程是預先編譯的程序,含有一個已經準備好的執行計劃,所以比SQL語句執行更快。
c. 使用適當的光標和鎖定模式。如果所做的全部工作只是從記錄集中讀取數據,并將其顯示在屏幕上,那么就使用缺省的只能前移、只讀的記錄集。ADO用來維護記錄和鎖定的細節的工作越少,執行的性能就越高。
d. 使用對象變量。當遍歷記錄集時一個肯定能提高性能的方法是使用對象變量指向集合中的成員。例如:
While Not RsGc.EOF
Response.Write "工程名稱:" & RsGc("GcMC") & "(工程代碼:" & RsGc("GcCode") & ")
"
RsGc.MoveNext
Wend
可以用改寫為下面的代碼以加快執行:
set GcMc=RsGc("GcMc")
set GcCode=RsGc("GcCode")
While Not rsGc.EOF Response.Write "工程名稱:" & GcMc & "(工程代碼:" & GcCode & ")
" RsGc.MoveNext
Wend
新的代碼建立了對象變量的引用,所以可以使用對象變量而不是實際的變量,這意味著腳本引擎的工作減少了,因為在集合中進行索引的次數變少了。
技巧五:不要混用腳本引擎
我們知道,ASP頁面中既可以使用VBScript,也可以使用JScript。但是在同一個頁面上同時使用JScript和VBScript則是不可取的。因為服務器必須實例化并嘗試緩存兩個(而不是一個)腳本引擎,這在一定程度上增加了系統負擔。因此,從性能上考慮,不應在同一頁面中混用多種腳本引擎。
本程序有兩文件test.asp 和tree.asp 還有一些圖標文件
1。test.asp 調用類生成樹 代碼如下
<%@ Language=VBScript %>
<html>
<head>
<link rel="stylesheet" href="tree.css">
<title>tree</title>
</head>
<!-- #include file="tree.asp" -->
<%
'========================================
' BUILDING A TREE PROGRAMATICALLY
'========================================
' This approach would be best suited for building
' dynamic trees using For..Next loops and such.
Set MyTree2 = New Tree
MyTree2.Top = 10
MyTree2.Left = 10
MyTree2.ExpandImage = "plus.gif"
MyTree2.CollapseImage = "minus.gif"
MyTree2.LeafImage = "webpage.gif"
' Notice the indentation used to reprensent the hierarchy
Set Node1 = MyTree2.CreateChild("script")
Set SubNode1 = Node1.CreateChild("server")
Set secSubNode1 = SubNode1.CreateChild("html")
secSubNode1.CreateChild "<A HREF=""http://127.0.0.1/"">asp</A>"
secSubNode1.CreateChild "<A HREF=""http://127.0.0.1/"">php</A>"
secSubNode1.CreateChild "<A HREF=""http://127.0.0.1/"">jsp</A>"
Set SubNode2 = Node1.CreateChild("os")
SubNode2.CreateChild "<A HREF=""#"">winnt</A>"
SubNode2.CreateChild "<A HREF=""#"">win2000</A>"
Set Node2 = MyTree2.CreateChild("Desktop")
Node2.CreateChild "<A HREF=""#"">Area Code Lookup</A>"
Node2.CreateChild "<A HREF=""#"">Arin Based Whois Search</A>"
Node2.CreateChild "<A HREF=""#"">World Time Zone Map</A>"
MyTree2.Draw()
Set MyTree2 = Nothing
%>
</BODY>
</HTML>
2。tree.asp 類的定義 代碼如下
<%
'******************************************************
' Author: Jacob Gilley
' Email: avis7@airmail.net
' My Terms: You can use this control in anyway you see fit
' cause I have no means to enforce any guidelines
' or BS that most developers think they can get
' you to agree to by spouting out words like
' "intellectual property" and "The Code Gods".
' - Viva la Microsoft!
'******************************************************
Dim gblTreeNodeCount:gblTreeNodeCount = 1
Class TreeNode
Public Value
Public ExpandImage
Public CollapseImage
Public LeafImage
Public Expanded
Private mszName
Private mcolChildren
Private mbChildrenInitialized
Public Property Get ChildCount()
ChildCount = mcolChildren.Count
End Property
Private Sub Class_Initialize()
mszName = "node" & CStr(gblTreeNodeCount)
gblTreeNodeCount = gblTreeNodeCount + 1
mbChildrenInitialized = False
Expanded = False
End Sub
Private Sub Class_Terminate()
If mbChildrenInitialized And IsObject(mcolChildren) Then
mcolChildren.RemoveAll()
Set mcolChildren = Nothing
End If
End Sub
Private Sub InitChildList()
Set mcolChildren = Server.CreateObject("Scripting.Dictionary")
mbChildrenInitialized = True
End Sub
Private Sub LoadState()
If Request(mszName) = "1" Or Request("togglenode") = mszName Then
Expanded = True
End If
End Sub
Public Function CreateChild(szValue)
If Not mbChildrenInitialized Then InitChildList()
Set CreateChild = New TreeNode
CreateChild.Value = szValue
CreateChild.ExpandImage = ExpandImage
CreateChild.CollapseImage = CollapseImage
CreateChild.LeafImage = LeafImage
mcolChildren.Add mcolChildren.Count + 1, CreateChild
End Function
Public Sub Draw()
LoadState()
Response.Write "<table border=""0"">" & vbCrLf
Response.Write "<tr><td>" & vbCrLf
If Expanded Then
Response.Write "<a href=""javascript:collapseNode('" & mszName & "')""><img src=""" & CollapseImage & """ border=""0""></a>" & vbCrLf
ElseIf Not mbChildrenInitialized Then
Response.Write "<img src=""" & LeafImage & """ border=0>" & vbCrLf
Else
Response.Write "<a href=""javascript:expandNode('" & mszName & "')""><img src=""" & ExpandImage & """ border=""0""></a>" & vbCrLf
End If
Response.Write "</td>" & vbCrLf
Response.Write "<td>" & Value & "</td></tr>" & vbCrLf
If Expanded Then
Response.Write "<input type=""hidden"" name=""" & mszName & """ value=""1"">" & vbCrLf
If mbChildrenInitialized Then
Response.Write "<tr><td> </td>" & vbCrLf
Response.Write "<td>" & vbCrLf
For Each ChildNode In mcolChildren.Items
ChildNode.Draw()
Next
Response.Write "</td>" & vbCrLf
Response.Write "</tr>" & vbCrLf
End If
End If
Response.Write "</table>" & vbCrLf
End Sub
End Class
Class Tree
Public Top
Public Left
Public ExpandImage
Public CollapseImage
Public LeafImage
Private mszPosition
Private mcolChildren
Public Property Let Absolute(bData)
If bData Then mszPosition = "absolute" Else mszPosition = "relative"
End Property
Public Property Get Absolute()
Absolute = CBool(mszPosition = "absolute")
End Property
Private Sub Class_Initialize()
Set mcolChildren = Server.CreateObject("Scripting.Dictionary")
mnTop = 0
mnLeft = 0
mszPosition = "absolute"
End Sub
Private Sub Class_Terminate()
mcolChildren.RemoveAll()
Set mcolChildren = Nothing
End Sub
Public Function CreateChild(szValue)
Set CreateChild = New TreeNode
CreateChild.Value = szValue
CreateChild.ExpandImage = ExpandImage
CreateChild.CollapseImage = CollapseImage
CreateChild.LeafImage = LeafImage
mcolChildren.Add mcolChildren.Count + 1, CreateChild
End Function
Public Sub LoadTemplate(szFileName)
Dim objWorkingNode
Dim colNodeStack
Dim fsObj, tsObj
Dim szLine
Dim nCurrDepth, nNextDepth
Set colNodeStack = Server.CreateObject("Scripting.Dictionary")
Set fsObj = CreateObject("Scripting.FileSystemObject")
Set tsObj = fsObj.OpenTextFile(szFileName, 1)
nCurrDepth = 0
While Not tsObj.AtEndOfLine
nNextDepth = 1
szLine = tsObj.ReadLine()
If nCurrDepth = 0 Then
Set objWorkingNode = CreateChild(Trim(szLine))
nCurrDepth = 1
Else
While Mid(szLine,nNextDepth,1) = vbTab Or Mid(szLine,nNextDepth,1) = " "
nNextDepth = nNextDepth + 1
WEnd
If nNextDepth > 1 Then szLine = Trim(Mid(szLine,nNextDepth))
If szLine <> "" Then
If nNextDepth > nCurrDepth Then
If colNodeStack.Exists(nCurrDepth) Then
Set colNodeStack.Item(nCurrDepth) = objWorkingNode
Else
colNodeStack.Add nCurrDepth, objWorkingNode
End If
Set objWorkingNode = objWorkingNode.CreateChild(szLine)
nCurrDepth = nCurrDepth + 1
ElseIf nNextDepth <= nCurrDepth Then
If nNextDepth > 1 Then
nNextDepth = nNextDepth - 1
While Not colNodeStack.Exists(nNextDepth) And nNextDepth > 1
nNextDepth = nNextDepth - 1
WEnd
Set objWorkingNode = colNodeStack.Item(nNextDepth)
Set objWorkingNode = objWorkingNode.CreateChild(szLine)
nNextDepth = nNextDepth + 1
Else
Set objWorkingNode = CreateChild(szLine)
End If
nCurrDepth = nNextDepth
End If
End If
End If
WEnd
tsObj.Close()
Set tsObj = Nothing
Set fsObj = Nothing
colNodeStack.RemoveAll()
Set colNodeStack = Nothing
End Sub
Public Sub Draw()
AddClientScript()
Response.Write "<div id=""treectrl"" style=""left: " & Left & "px; top: " & Top & "px; position: " & mszPosition & ";"">" & vbCrLf
Response.Write "<form name=""treectrlfrm"" action=""" & Request.ServerVariables("SCRIPT_NAME") & """ method=""get"">" & vbCrLf
Response.Write "<table border=""0"">" & vbCrLf
Response.Write "<tr><td>" & vbCrLf
For Each ChildNode In mcolChildren.Items
ChildNode.Draw()
Next
Response.Write "</td></tr>" & vbCrLf
Response.Write "</table>" & vbCrLf
Response.Write "<input type=""hidden"" name=""togglenode"" value="""">" & vbCrLf
Response.Write "</form>" & vbCrLf
Response.Write "</div>" & vbCrLf
End Sub
Private Sub AddClientScript()
%>
<script language="javascript">
function expandNode(szNodeName)
{
if(document.layers != null) {
document.treectrl.document.treectrlfrm.togglenode.value = szNodeName;
document.treectrl.document.treectrlfrm.submit();
}
else {
document.all["treectrlfrm"].togglenode.value = szNodeName;
document.all["treectrlfrm"].submit();
}
}
function collapseNode(szNodeName)
{
if(document.layers != null) {
document.treectrl.document.treectrlfrm.elements[szNodeName].value = -1;
document.treectrl.document.treectrlfrm.submit();
}
else {
document.treectrlfrm.elements[szNodeName].value = -1;
document.treectrlfrm.submit();
}
}
</script>
<%
End Sub
End Class
%>
這幾天開始接觸JSP里面一些BEAN的寫法,然后自己想了想,認為其實在ASP里面也可以采取這一思想來做。雖然不是很純,不徹底,但是能夠把一些邏輯處理分離出來,更適合程序的移植性,提高了開發周期。我自己寫了個類ConnEX包含了一些對數據庫的操作,覺得應該可以包括一大部分的邏輯處理,但是這樣也提高了錯誤幾率,如果你把SQL語句控制的比較好的話,應該是利大于弊的,這里都是一點點拙見,望大家指正。
程序的功能有了個大體的框架,其實可以自己添加一些功能,比如開始的數據庫連接 ,可以先設置變量然后通過INIT() 來選擇不同類型的數據庫
<%
'*******************************************************************************************
'* 程序:ConnEx.asp
'*
'* 描述:模仿JAVABEAN寫的一個類,專門操作數據庫,提供多種方法操作,但要避免SQL語法錯誤!
'*
'* 作者:田野 Email:Foxty@sina.com
'*
'* 日期:2005.06.0
'*******************************************************************************************
'On Error Resume Next
Class ConnEx
public ConnEx
public DBpath '---------數據庫路徑
public DBtype '---------數據庫類型 1(Access) 2(SqlServer) 3(可擴充)
public ConnMethod '--------連接方式 (DSN,非DSN)
public User
public Pass
Sub Class_initialize
End Sub
Sub Init()
ConnStr = "Driver={Microsoft Access Driver (*.mdb)};dbq="&Server.MapPath("Date.mdb")
Set ConnEx = Server.Createobject("ADODB.CONNECTION")
ConnEx.Open ConnStr
CatchError("Class_Terminate")
End Sub
Sub CatchError( Str )
If Err Then
Err.Clear
Class_Terminate()
Response.Write("捕捉到錯誤,程序結束!在"&Str&"處")
Response.End()
End If
End Sub
'******************************************
'*通過SQL語句來查找記錄是否存在,容易出錯
'******************************************
Function HasRecordBySql( Sql )
Call CheckSql(Sql,"R")
Dim Rs,HasR
Set Rs = ConnEx.Execute( Sql )
CatchError("HasReordSql")
If Not (Rs.eof Or Rs.bof) Then
HasR = False
Else
HasR = True
End If
Rs.Close
Set Rs = Nothing
HasRecordBySql = HasR
End Function
'***************************************
'*通過ID來查找記錄是否存在
'***************************************
Function HasRecordById( StrTableName , IntID )
'CheckValue( IntID , 1 )
Dim Rs,HasR
Sql = "Select top 1 * from "&StrTableName&" Where Id = "&IntID
Call CheckSql(Sql,"R")
Set Rs = ConnEx.Execute(Sql)
CatchError("HasRecordByID")
If Not (Rs.eof Or Rs.bof) Then
HasR = False
Else
HasR = True
End If
Rs.close
Set Rs = Nothing
HasRecordById = HasR
End Function
'**********************************************
'*通過SQL語句取得記錄集
'**********************************************
Function GetRsBySql( Sql )
Call CheckSql(Sql,"R")
Dim Rs
Set Rs = Server.CreateObject("Adodb.RecordSet")
Rs.Open Sql,ConnEx,1,1
Set GetRsBySql = Rs
End Function
'*********************************************
'*取得某個字段的值
'*********************************************
Function GetValueBySql( Sql )
Call CheckSql(Sql,"R")
Dim Rs,ReturnValue
Set Rs = ConnEx.Execute(Sql)
CatchError("GetValueBySql")
If Not( Rs.Eof Or Rs.Bof ) Then
ReturnValue = Rs(0)
Else
ReturnValue = "沒有記錄"
End If
Rs.Close
Set Rs = Nothing
GetValueBySql = ReturnValue
End Function
'==================================================Update,Insert==================================================================
'*********************************************
'*利用SQL修改數據
'*********************************************
Function UpdateBySql( Sql )
Call CheckSql(Sql,"w")
ConnEx.Execute(Sql)
CatchError("UpdateBySql")
UpdateBySql = True
End Function
'********************************************
'*利用SQL語句插入數據
'********************************************
Function InsertBySql(Sql)
Call CheckSql(Sql,"w")
ConnEx.Execute(Sql)
CatchError("InsertBySql")
InsertBySql = True
End Function
'======================================================Delete=============================================================
'********************************************
'*通過SQL語句刪除
'********************************************
Function DeleteBySql( Sql )
Call CheckSql(Sql,"D")
ConnEx.Execute(Sql)
CatchError("DeleteBySql")
DeleteBySql = True
End Function
'********************************************
'*檢查SQL語句權限,根據標志Flag 來檢測語句擁有的權限
'********************************************
Sub CheckSql( Sql , Flag )
Dim StrSql,SinCounts,DouCounts,i
StrSql = Lcase(Sql)
SinCounts = 0
DouCounts = 0
For i = 1 to Len(StrSql)
If Mid(StrSql,i,1) = "'" Then SinCounts = SinCounts + 1
If Mid(StrSql,i,1) = """" Then DouConnts = DouCounts + 1
Next
If (SinCounts Mod 2) <> 0 Or (DouCounts Mod 2) <> 0 Or Instr(StrSql,";") > 0 Then
Call Class_Terminate()
Response.Write("SQL語法錯誤!")
Response.End()
End If
Select Case Flag
Case "R","r":
If Instr(StrSql,"delete") > 0 Or Instr(StrSql,"update") Or Instr(StrSql,"drop") > 0 Or Instr(StrSql,"insert") > 0 Then
Class_Terminate()
Response.Write("權限不足,沒有執行寫操作的權限")
Response.End()
End If
Case "W","w":
If Instr(StrSql,"delete") > 0 Or Instr(StrSql,"drop") > 0 Or Instr(StrSql,"select") > 0 Then
Class_Terminate()
Response.Write("權限不足,沒有執行刪除操作的權限")
Response.End()
End If
Case "D","d":
Case Else:
Response.Write("函數CheckSql標志錯誤!")
End Select
End Sub
Sub Class_Terminate
If Not IsEmpty(FriendConn) Then
FriendConn.Close
Set FriendConn = Nothing
CatchError()
End If
End Sub
End Class
%>
關鍵字: 正則表達式,Regular Expression
作者:笑容
發表于:2004年05月03日
最后更新:2005年01月17日 19:54
版權聲明:使用創作公用版權協議
引用地址:<a >正則表達式(regular expression)</a>
NAV: 笑容的八小時外 / 笑容的八小時外資料索引
如何創建一個網站 (HOW TO: Initiate a website) Red Hat Enterprise Linux 介紹
前言
正則表達式是煩瑣的,但是強大的,學會之后的應用會讓你除了提高效率外,會給你帶來絕對的成就感。只要認真去閱讀這些資料,加上應用的時候進行一定的參考,掌握正則表達式不是問題。
索引
1._引子
2._正則表達式的歷史
3._正則表達式定義
3.1_普通字符
3.2_非打印字符
3.3_特殊字符
3.4_限定符
3.5_定位符
3.6_選擇
3.7_后向引用
4._各種操作符的運算優先級
5._全部符號解釋
6._部分例子
7._正則表達式匹配規則
7.1_基本模式匹配
7.2_字符簇
7.3_確定重復出現
目前,正則表達式已經在很多軟件中得到廣泛的應用,包括*nix(Linux, Unix等),HP等操作系統,PHP,C#,Java等開發環境,以及很多的應用軟件中,都可以看到正則表達式的影子。
正則表達式的使用,可以通過簡單的辦法來實現強大的功能。為了簡單有效而又不失強大,造成了正則表達式代碼的難度較大,學習起來也不是很容易,所以需要付出一些努力才行,入門之后參照一定的參考,使用起來還是比較簡單有效的。
例子: ^.+@.+\\..+$
這樣的代碼曾經多次把我自己給嚇退過。可能很多人也是被這樣的代碼給嚇跑的吧。繼續閱讀本文將讓你也可以自由應用這樣的代碼。
注意:這里的第7部分跟前面的內容看起來似乎有些重復,目的是把前面表格里的部分重新描述了一次,目的是讓這些內容更容易理解。
正則表達式的“祖先”可以一直上溯至對人類神經系統如何工作的早期研究。Warren McCulloch 和 Walter Pitts 這兩位神經生理學家研究出一種數學方式來描述這些神經網絡。
1956 年, 一位叫 Stephen Kleene 的數學家在 McCulloch 和 Pitts 早期工作的基礎上,發表了一篇標題為“神經網事件的表示法”的論文,引入了正則表達式的概念。正則表達式就是用來描述他稱為“正則集的代數”的表達式,因此采用“正則表達式”這個術語。
隨后,發現可以將這一工作應用于使用 Ken Thompson 的計算搜索算法的一些早期研究,Ken Thompson 是 Unix 的主要發明人。正則表達式的第一個實用應用程序就是 Unix 中的 qed 編輯器。
如他們所說,剩下的就是眾所周知的歷史了。從那時起直至現在正則表達式都是基于文本的編輯器和搜索工具中的一個重要部分。
正則表達式(regular expression)描述了一種字符串匹配的模式,可以用來檢查一個串是否含有某種子串、將匹配的子串做替換或者從某個串中取出符合某個條件的子串等。
- 列目錄時, dir *.txt或ls *.txt中的*.txt就不是一個正則表達式,因為這里*與正則式的*的含義是不同的。
正則表達式是由普通字符(例如字符 a 到 z)以及特殊字符(稱為元字符)組成的文字模式。正則表達式作為一個模板,將某個字符模式與所搜索的字符串進行匹配。
由所有那些未顯式指定為元字符的打印和非打印字符組成。這包括所有的大寫和小寫字母字符,所有數字,所有標點符號以及一些符號。
字符 |
含義 |
\cx |
匹配由x指明的控制字符。例如, \cM 匹配一個 Control-M 或回車符。x 的值必須為 A-Z 或 a-z 之一。否則,將 c 視為一個原義的 'c' 字符。 |
\f |
匹配一個換頁符。等價于 \x0c 和 \cL。 |
\n |
匹配一個換行符。等價于 \x0a 和 \cJ。 |
\r |
匹配一個回車符。等價于 \x0d 和 \cM。 |
\s |
匹配任何空白字符,包括空格、制表符、換頁符等等。等價于 [ \f\n\r\t\v]。 |
\S |
匹配任何非空白字符。等價于 [^ \f\n\r\t\v]。 |
\t |
匹配一個制表符。等價于 \x09 和 \cI。 |
\v |
匹配一個垂直制表符。等價于 \x0b 和 \cK。 |
所謂特殊字符,就是一些有特殊含義的字符,如上面說的"*.txt"中的*,簡單的說就是表示任何字符串的意思。如果要查找文件名中有*的文件,則需要對*進行轉義,即在其前加一個\。ls \*.txt。正則表達式有以下特殊字符。
特別字符 |
說明 |
$ |
匹配輸入字符串的結尾位置。如果設置了 RegExp 對象的 Multiline 屬性,則 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,請使用 \$。 |
( ) |
標記一個子表達式的開始和結束位置。子表達式可以獲取供以后使用。要匹配這些字符,請使用 \( 和 \)。 |
* |
匹配前面的子表達式零次或多次。要匹配 * 字符,請使用 \*。 |
+ |
匹配前面的子表達式一次或多次。要匹配 + 字符,請使用 \+。 |
. |
匹配除換行符 \n之外的任何單字符。要匹配 .,請使用 \。 |
[ |
標記一個中括號表達式的開始。要匹配 [,請使用 \[。 |
? |
匹配前面的子表達式零次或一次,或指明一個非貪婪限定符。要匹配 ? 字符,請使用 \?。 |
\ |
將下一個字符標記為或特殊字符、或原義字符、或向后引用、或八進制轉義符。例如, 'n' 匹配字符 'n'。'\n' 匹配換行符。序列 '\\' 匹配 "\",而 '\(' 則匹配 "("。 |
^ |
匹配輸入字符串的開始位置,除非在方括號表達式中使用,此時它表示不接受該字符集合。要匹配 ^ 字符本身,請使用 \^。 |
{ |
標記限定符表達式的開始。要匹配 {,請使用 \{。 |
| |
指明兩項之間的一個選擇。要匹配 |,請使用 \|。 |
- 構造正則表達式的方法和創建數學表達式的方法一樣。也就是用多種元字符與操作符將小的表達式結合在一起來創建更大的表達式。正則表達式的組件可以是單個的字符、字符集合、字符范圍、字符間的選擇或者所有這些組件的任意組合。
限定符用來指定正則表達式的一個給定組件必須要出現多少次才能滿足匹配。有*或+或?或{n}或{n,}或{n,m}共6種。
*、+和?限定符都是貪婪的,因為它們會盡可能多的匹配文字,只有在它們的后面加上一個?就可以實現非貪婪或最小匹配。
正則表達式的限定符有:
字符 |
描述 |
* |
匹配前面的子表達式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等價于{0,}。 |
+ |
匹配前面的子表達式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等價于 {1,}。 |
? |
匹配前面的子表達式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等價于 {0,1}。 |
{n} |
n 是一個非負整數。匹配確定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的兩個 o。 |
{n,} |
n 是一個非負整數。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等價于 'o+'。'o{0,}' 則等價于 'o*'。 |
{n,m} |
m 和 n 均為非負整數,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 將匹配 "fooooood" 中的前三個 o。'o{0,1}' 等價于 'o?'。請注意在逗號和兩個數之間不能有空格。 |
用來描述字符串或單詞的邊界,^和$分別指字符串的開始與結束,\b描述單詞的前或后邊界,\B表示非單詞邊界。
不能對定位符使用限定符。
用圓括號將所有選擇項括起來,相鄰的選擇項之間用|分隔。但用圓括號會有一個副作用,是相關的匹配會被緩存,此時可用?:放在第一個選項前來消除這種副作用。
其中?:是非捕獲元之一,還有兩個非捕獲元是?=和?!,這兩個還有更多的含義,前者為正向預查,在任何開始匹配圓括號內的正則表達式模式的位置來匹配搜索字符串,后者為負向預查,在任何開始不匹配該正則表達式模式的位置來匹配搜索字符串。
對一個正則表達式模式或部分模式兩邊添加圓括號將導致相關匹配存儲到一個臨時緩沖區中,所捕獲的每個子匹配都按照在正則表達式模式中從左至右所遇到的內容存儲。存儲子匹配的緩沖區編號從 1 開始,連續編號直至最大 99 個子表達式。每個緩沖區都可以使用 '\n' 訪問,其中 n 為一個標識特定緩沖區的一位或兩位十進制數。
可以使用非捕獲元字符 '?:', '?=', or '?!' 來忽略對相關匹配的保存。
相同優先級的從左到右進行運算,不同優先級的運算先高后低。各種操作符的優先級從高到低如下:
操作符 |
描述 |
\ |
轉義符 |
(), (?:), (?=), [] |
圓括號和方括號 |
*, +, ?, {n}, {n,}, {n,m} |
限定符 |
^, $, \anymetacharacter |
位置和順序 |
| |
“或”操作 |
字符 |
描述 |
\ |
將下一個字符標記為一個特殊字符、或一個原義字符、或一個 向后引用、或一個八進制轉義符。例如,'n' 匹配字符 "n"。'\n' 匹配一個換行符。序列 '\\' 匹配 "\" 而 "\(" 則匹配 "("。 |
^ |
匹配輸入字符串的開始位置。如果設置了 RegExp 對象的 Multiline 屬性,^ 也匹配 '\n' 或 '\r' 之后的位置。 |
$ |
匹配輸入字符串的結束位置。如果設置了RegExp 對象的 Multiline 屬性,$ 也匹配 '\n' 或 '\r' 之前的位置。 |
* |
匹配前面的子表達式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等價于{0,}。 |
+ |
匹配前面的子表達式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等價于 {1,}。 |
? |
匹配前面的子表達式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等價于 {0,1}。 |
{n} |
n 是一個非負整數。匹配確定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的兩個 o。 |
{n,} |
n 是一個非負整數。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等價于 'o+'。'o{0,}' 則等價于 'o*'。 |
{n,m} |
m 和 n 均為非負整數,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 將匹配 "fooooood" 中的前三個 o。'o{0,1}' 等價于 'o?'。請注意在逗號和兩個數之間不能有空格。 |
? |
當該字符緊跟在任何一個其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面時,匹配模式是非貪婪的。非貪婪模式盡可能少的匹配所搜索的字符串,而默認的貪婪模式則盡可能多的匹配所搜索的字符串。例如,對于字符串 "oooo",'o+?' 將匹配單個 "o",而 'o+' 將匹配所有 'o'。 |
. |
匹配除 "\n" 之外的任何單個字符。要匹配包括 '\n' 在內的任何字符,請使用象 '[.\n]' 的模式。 |
(pattern) |
匹配 pattern 并獲取這一匹配。所獲取的匹配可以從產生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中則使用 $0…$9 屬性。要匹配圓括號字符,請使用 '\(' 或 '\)'。 |
(?:pattern) |
匹配 pattern 但不獲取匹配結果,也就是說這是一個非獲取匹配,不進行存儲供以后使用。這在使用 "或" 字符 (|) 來組合一個模式的各個部分是很有用。例如, 'industr(?:y|ies) 就是一個比 'industry|industries' 更簡略的表達式。 |
(?=pattern) |
正向預查,在任何匹配 pattern 的字符串開始處匹配查找字符串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以后使用。例如,'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。預查不消耗字符,也就是說,在一個匹配發生后,在最后一次匹配之后立即開始下一次匹配的搜索,而不是從包含預查的字符之后開始。 |
(?!pattern) |
負向預查,在任何不匹配 pattern 的字符串開始處匹配查找字符串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。預查不消耗字符,也就是說,在一個匹配發生后,在最后一次匹配之后立即開始下一次匹配的搜索,而不是從包含預查的字符之后開始 |
x|y |
匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 則匹配 "zood" 或 "food"。 |
[xyz] |
字符集合。匹配所包含的任意一個字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。 |
[^xyz] |
負值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。 |
[a-z] |
字符范圍。匹配指定范圍內的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范圍內的任意小寫字母字符。 |
[^a-z] |
負值字符范圍。匹配任何不在指定范圍內的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范圍內的任意字符。 |
\b |
匹配一個單詞邊界,也就是指單詞和空格間的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B |
匹配非單詞邊界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\cx |
匹配由 x 指明的控制字符。例如, \cM 匹配一個 Control-M 或回車符。x 的值必須為 A-Z 或 a-z 之一。否則,將 c 視為一個原義的 'c' 字符。 |
\d |
匹配一個數字字符。等價于 [0-9]。 |
\D |
匹配一個非數字字符。等價于 [^0-9]。 |
\f |
匹配一個換頁符。等價于 \x0c 和 \cL。 |
\n |
匹配一個換行符。等價于 \x0a 和 \cJ。 |
\r |
匹配一個回車符。等價于 \x0d 和 \cM。 |
\s |
匹配任何空白字符,包括空格、制表符、換頁符等等。等價于 [ \f\n\r\t\v]。 |
\S |
匹配任何非空白字符。等價于 [^ \f\n\r\t\v]。 |
\t |
匹配一個制表符。等價于 \x09 和 \cI。 |
\v |
匹配一個垂直制表符。等價于 \x0b 和 \cK。 |
\w |
匹配包括下劃線的任何單詞字符。等價于'[A-Za-z0-9_]'。 |
\W |
匹配任何非單詞字符。等價于 '[^A-Za-z0-9_]'。 |
\xn |
匹配 n,其中 n 為十六進制轉義值。十六進制轉義值必須為確定的兩個數字長。例如,'\x41' 匹配 "A"。'\x041' 則等價于 '\x04' & "1"。正則表達式中可以使用 ASCII 編碼。. |
\num |
匹配 num,其中 num 是一個正整數。對所獲取的匹配的引用。例如,'(.)\1' 匹配兩個連續的相同字符。 |
\n |
標識一個八進制轉義值或一個向后引用。如果 \n 之前至少 n 個獲取的子表達式,則 n 為向后引用。否則,如果 n 為八進制數字 (0-7),則 n 為一個八進制轉義值。 |
\nm |
標識一個八進制轉義值或一個向后引用。如果 \nm 之前至少有 nm 個獲得子表達式,則 nm 為向后引用。如果 \nm 之前至少有 n 個獲取,則 n 為一個后跟文字 m 的向后引用。如果前面的條件都不滿足,若 n 和 m 均為八進制數字 (0-7),則 \nm 將匹配八進制轉義值 nm。 |
\nml |
如果 n 為八進制數字 (0-3),且 m 和 l 均為八進制數字 (0-7),則匹配八進制轉義值 nml。 |
\un |
匹配 n,其中 n 是一個用四個十六進制數字表示的 Unicode 字符。例如, \u00A9 匹配版權符號 (?)。 |
正則表達式 |
說明 |
/\b([a-z]+) \1\b/gi |
一個單詞連續出現的位置 |
/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/ |
將一個URL解析為協議、域、端口及相對路徑 |
/^(?:Chapter|Section) [1-9][0-9]{0,1}$/ |
定位章節的位置 |
/[-a-z]/ |
A至z共26個字母再加一個-號。 |
/ter\b/ |
可匹配chapter,而不能terminal |
/\Bapt/ |
可匹配chapter,而不能aptitude |
/Windows(?=95 |98 |NT )/ |
可匹配Windows95或Windows98或WindowsNT,當找到一個匹配后,從Windows后面開始進行下一次的檢索匹配。 |
7.1 基本模式匹配
一切從最基本的開始。模式,是正規表達式最基本的元素,它們是一組描述字符串特征的字符。模式可以很簡單,由普通的字符串組成,也可以非常復雜,往往用特殊的字符表示一個范圍內的字符、重復出現,或表示上下文。例如:
^once
這個模式包含一個特殊的字符^,表示該模式只匹配那些以once開頭的字符串。例如該模式與字符串"once upon a time"匹配,與"There once was a man from NewYork"不匹配。正如如^符號表示開頭一樣,$符號用來匹配那些以給定模式結尾的字符串。
bucket$
這個模式與"Who kept all of this cash in a bucket"匹配,與"buckets"不匹配。字符^和$同時使用時,表示精確匹配(字符串與模式一樣)。例如:
^bucket$
只匹配字符串"bucket"。如果一個模式不包括^和$,那么它與任何包含該模式的字符串匹配。例如:模式
once
與字符串
There once was a man from NewYork
Who kept all of his cash in a bucket.
是匹配的。
在該模式中的字母(o-n-c-e)是字面的字符,也就是說,他們表示該字母本身,數字也是一樣的。其他一些稍微復雜的字符,如標點符號和白字符(空格、制表符等),要用到轉義序列。所有的轉義序列都用反斜杠(\)打頭。制表符的轉義序列是:\t。所以如果我們要檢測一個字符串是否以制表符開頭,可以用這個模式:
^\t
類似的,用\n表示“新行”,\r表示回車。其他的特殊符號,可以用在前面加上反斜杠,如反斜杠本身用\\表示,句號.用\.表示,以此類推。
7.2 字符簇
在INTERNET的程序中,正規表達式通常用來驗證用戶的輸入。當用戶提交一個FORM以后,要判斷輸入的電話號碼、地址、EMAIL地址、信用卡號碼等是否有效,用普通的基于字面的字符是不夠的。
所以要用一種更自由的描述我們要的模式的辦法,它就是字符簇。要建立一個表示所有元音字符的字符簇,就把所有的元音字符放在一個方括號里:
[AaEeIiOoUu]
這個模式與任何元音字符匹配,但只能表示一個字符。用連字號可以表示一個字符的范圍,如:
[a-z] //匹配所有的小寫字母
[A-Z] //匹配所有的大寫字母
[a-zA-Z] //匹配所有的字母
[0-9] //匹配所有的數字
[0-9\.\-] //匹配所有的數字,句號和減號
[ \f\r\t\n] //匹配所有的白字符
同樣的,這些也只表示一個字符,這是一個非常重要的。如果要匹配一個由一個小寫字母和一位數字組成的字符串,比如"z2"、"t6"或"g7",但不是"ab2"、"r2d3" 或"b52"的話,用這個模式:
^[a-z][0-9]$
盡管[a-z]代表26個字母的范圍,但在這里它只能與第一個字符是小寫字母的字符串匹配。
前面曾經提到^表示字符串的開頭,但它還有另外一個含義。當在一組方括號里使用^是,它表示“非”或“排除”的意思,常常用來剔除某個字符。還用前面的例子,我們要求第一個字符不能是數字:
^[^0-9][0-9]$
這個模式與"&5"、"g7"及"-2"是匹配的,但與"12"、"66"是不匹配的。下面是幾個排除特定字符的例子:
[^a-z] //除了小寫字母以外的所有字符
[^\\\/\^] //除了(\)(/)(^)之外的所有字符
[^\"\'] //除了雙引號(")和單引號(')之外的所有字符
特殊字符"." (點,句號)在正規表達式中用來表示除了“新行”之外的所有字符。所以模式"^.5$"與任何兩個字符的、以數字5結尾和以其他非“新行”字符開頭的字符串匹配。模式"."可以匹配任何字符串,除了空串和只包括一個“新行”的字符串。
PHP的正規表達式有一些內置的通用字符簇,列表如下:
字符簇 含義
[[:alpha:]] 任何字母
[[:digit:]] 任何數字
[[:alnum:]] 任何字母和數字
[[:space:]] 任何白字符
[[:upper:]] 任何大寫字母
[[:lower:]] 任何小寫字母
[[:punct:]] 任何標點符號
[[:xdigit:]] 任何16進制的數字,相當于[0-9a-fA-F]
7.3 確定重復出現
到現在為止,你已經知道如何去匹配一個字母或數字,但更多的情況下,可能要匹配一個單詞或一組數字。一個單詞有若干個字母組成,一組數字有若干個單數組成。跟在字符或字符簇后面的花括號({})用來確定前面的內容的重復出現的次數。
字符簇 含義
^[a-zA-Z_]$ 所有的字母和下劃線
^[[:alpha:]]{3}$ 所有的3個字母的單詞
^a$ 字母a
^a{4}$ aaaa
^a{2,4}$ aa,aaa或aaaa
^a{1,3}$ a,aa或aaa
^a{2,}$ 包含多于兩個a的字符串
^a{2,} 如:aardvark和aaab,但apple不行
a{2,} 如:baad和aaa,但Nantucket不行
\t{2} 兩個制表符
.{2} 所有的兩個字符
這些例子描述了花括號的三種不同的用法。一個數字,{x}的意思是“前面的字符或字符簇只出現x次”;一個數字加逗號,{x,}的意思是“前面的內容出現x或更多的次數”;兩個用逗號分隔的數字,{x,y}表示“前面的內容至少出現x次,但不超過y次”。我們可以把模式擴展到更多的單詞或數字:
^[a-zA-Z0-9_]{1,}$ //所有包含一個以上的字母、數字或下劃線的字符串
^[0-9]{1,}$ //所有的正數
^\-{0,1}[0-9]{1,}$ //所有的整數
^\-{0,1}[0-9]{0,}\.{0,1}[0-9]{0,}$ //所有的小數
最后一個例子不太好理解,是嗎?這么看吧:與所有以一個可選的負號(\-{0,1})開頭(^)、跟著0個或更多的數字([0-9]{0,})、和一個可選的小數點(\.{0,1})再跟上0個或多個數字([0-9]{0,}),并且沒有其他任何東西($)。下面你將知道能夠使用的更為簡單的方法。
特殊字符"?"與{0,1}是相等的,它們都代表著:“0個或1個前面的內容”或“前面的內容是可選的”。所以剛才的例子可以簡化為:
^\-?[0-9]{0,}\.?[0-9]{0,}$
特殊字符"*"與{0,}是相等的,它們都代表著“0個或多個前面的內容”。最后,字符"+"與 {1,}是相等的,表示“1個或多個前面的內容”,所以上面的4個例子可以寫成:
^[a-zA-Z0-9_]+$ //所有包含一個以上的字母、數字或下劃線的字符串
^[0-9]+$ //所有的正數
^\-?[0-9]+$ //所有的整數
^\-?[0-9]*\.?[0-9]*$ //所有的小數
當然這并不能從技術上降低正規表達式的復雜性,但可以使它們更容易閱讀。
參考文獻:
JScript 和 VBScript 正則表達式
微軟MSDN上的例子(英文):
- Scanning for HREFS
- Provides an example that searches an input string and prints out all the href="..." values and their locations in the string.
- Changing Date Formats
- Provides an example that replaces dates of the form mm/dd/yy with dates of the form dd-mm-yy.
- Extracting URL Information
- Provides an example that extracts a protocol and port number from a string containing a URL. For example, "http://www.contoso.com:8080/letters/readme.html" returns "http:8080".
- Cleaning an Input String
- provides an example that strips invalid non-alphanumeric characters from a string.
- Confirming Valid E-Mail Format
- Provides an example that you can use to verify that a string is in valid e-mail format.
|
最后更新:2005年01月17日 19:54
版權聲明:使用創作公用版權協議
引用地址:<a >正則表達式(regular expression)</a>
Despite the tons of examples and docs, mod_rewrite is voodoo. Damned cool voodoo, but still voodoo. ''
-- Brian Moore
look to the master, follow the master, walk with the master, see through the master, become the master.
個性需要打造,牛逼需要證明。