欧美一区二区私人影院日本,国产在线观看一区,午夜视频一区在线观看http://www.shnenglu.com/sicheng/category/6000.html// I am new in programming, welcome to my blog <br> I am oyjpart(alpc12, 四城) zh-cnFri, 26 Feb 2010 21:41:44 GMTFri, 26 Feb 2010 21:41:44 GMT60[zz]當你輸入一個網(wǎng)址的時候,實際會發(fā)生什么?http://www.shnenglu.com/sicheng/archive/2010/02/26/108499.htmloyjpartoyjpartFri, 26 Feb 2010 08:22:00 GMThttp://www.shnenglu.com/sicheng/archive/2010/02/26/108499.htmlhttp://www.shnenglu.com/sicheng/comments/108499.htmlhttp://www.shnenglu.com/sicheng/archive/2010/02/26/108499.html#Feedback0http://www.shnenglu.com/sicheng/comments/commentRss/108499.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/108499.html 

作為一個軟件開發(fā)者,你一定會對網(wǎng)絡應用如何工作有一個完整的層次化的認知,同樣這里也包括這些應用所用到的技術(shù):像瀏覽器,HTTP,HTML,網(wǎng)絡服務器,需求處理等等。

本文將更深入的研究當你輸入一個網(wǎng)址的時候,后臺到底發(fā)生了一件件什么樣的事~

1. 首先嘛,你得在瀏覽器里輸入要網(wǎng)址:

image

 

2. 瀏覽器查找域名的IP地址

image

導航的第一步是通過訪問的域名找出其IP地址。DNS查找過程如下:

  • 瀏覽器緩存 – 瀏覽器會緩存DNS記錄一段時間。 有趣的是,操作系統(tǒng)沒有告訴瀏覽器儲存DNS記錄的時間,這樣不同瀏覽器會儲存?zhèn)€自固定的一個時間(2分鐘到30分鐘不等)。
  • 系統(tǒng)緩存 – 如果在瀏覽器緩存里沒有找到需要的記錄,瀏覽器會做一個系統(tǒng)調(diào)用(windows里是gethostbyname)。這樣便可獲得系統(tǒng)緩存中的記錄。
  • 路由器緩存 – 接著,前面的查詢請求發(fā)向路由器,它一般會有自己的DNS緩存。
  • ISP DNS 緩存 – 接下來要check的就是ISP緩存DNS的服務器。在這一般都能找到相應的緩存記錄。
  • 遞歸搜索 – 你的ISP的DNS服務器從跟域名服務器開始進行遞歸搜索,從.com頂級域名服務器到Facebook的域名服務器。一般DNS服務器的緩存中會有.com域名服務器中的域名,所以到頂級服務器的匹配過程不是那么必要了。

DNS遞歸查找如下圖所示:

500px-An_example_of_theoretical_DNS_recursion_svg

DNS有一點令人擔憂,這就是像wikipedia.org 或者 facebook.com這樣的整個域名看上去只是對應一個單獨的IP地址。還好,有幾種方法可以消除這個瓶頸:

  • 循環(huán) DNS 是DNS查找時返回多個IP時的解決方案。舉例來說,F(xiàn)acebook.com實際上就對應了四個IP地址。
  • 負載平衡器 是以一個特定IP地址進行偵聽并將網(wǎng)絡請求轉(zhuǎn)發(fā)到集群服務器上的硬件設備。 一些大型的站點一般都會使用這種昂貴的高性能負載平衡器。
  • 地理 DNS 根據(jù)用戶所處的地理位置,通過把域名映射到多個不同的IP地址提高可擴展性。這樣不同的服務器不能夠更新同步狀態(tài),但映射靜態(tài)內(nèi)容的話非常好。
  • Anycast 是一個IP地址映射多個物理主機的路由技術(shù)。 美中不足,Anycast與TCP協(xié)議適應的不是很好,所以很少應用在那些方案中。

大多數(shù)DNS服務器使用Anycast來獲得高效低延遲的DNS查找。

 

3. 瀏覽器給web服務器發(fā)送一個HTTP請求

image

因為像Facebook主頁這樣的動態(tài)頁面,打開后在瀏覽器緩存中很快甚至馬上就會過期,毫無疑問他們不能從中讀取。

所以,瀏覽器將把一下請求發(fā)送到Facebook所在的服務器:

GET http://facebook.com/ HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: facebook.com
Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]

GET 這個請求定義了要讀取的URL: “http://facebook.com/”。 瀏覽器自身定義 (User-Agent 頭), 和它希望接受什么類型的相應 (Accept and Accept-Encoding 頭). Connection頭要求服務器為了后邊的請求不要關(guān)閉TCP連接。

請求中也包含瀏覽器存儲的該域名的cookies??赡苣阋呀?jīng)知道,在不同頁面請求當中,cookies是與跟蹤一個網(wǎng)站狀態(tài)相匹配的鍵值。這樣cookies會存儲登錄用戶名,服務器分配的密碼和一些用戶設置等。Cookies會以文本文檔形式存儲在客戶機里,每次請求時發(fā)送給服務器。

用來看原始HTTP請求及其相應的工具很多。作者比較喜歡使用fiddler,當然也有像FireBug這樣其他的工具。這些軟件在網(wǎng)站優(yōu)化時會幫上很大忙。

除了獲取請求,還有一種是發(fā)送請求,它常在提交表單用到。發(fā)送請求通過URL傳遞其參數(shù)(e.g.: http://robozzle.com/puzzle.aspx?id=85)。發(fā)送請求在請求正文頭之后發(fā)送其參數(shù)。

像“http://facebook.com/”中的斜杠是至關(guān)重要的。這種情況下,瀏覽器能安全的添加斜杠。而像“http://example.com/folderOrFile”這樣的地址,因為瀏覽器不清楚folderOrFile到底是文件夾還是文件,所以不能自動添加斜杠。這時,瀏覽器就不加斜杠直接訪問地址,服務器會響應一個重定向,結(jié)果造成一次不必要的握手。

4. facebook服務的永久重定向響應

image

圖中所示為Facebook服務器發(fā)回給瀏覽器的響應:

HTTP/1.1 301 Moved Permanently
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
pre-check=0
Expires: Sat, 01 Jan 2000 00:00:00 GMT
Location: http://www.facebook.com/
P3P: CP="DSP LAW"
Pragma: no-cache
Set-Cookie: made_write_conn=deleted; expires=Thu, 12-Feb-2009 05:09:50 GMT;
path=/; domain=.facebook.com; httponly
Content-Type: text/html; charset=utf-8
X-Cnection: close
Date: Fri, 12 Feb 2010 05:09:51 GMT
Content-Length: 0

服務器給瀏覽器響應一個301永久重定向響應,這樣瀏覽器就會訪問“http://www.facebook.com/” 而非“http://facebook.com/”。

為什么服務器一定要重定向而不是直接發(fā)會用戶想看的網(wǎng)頁內(nèi)容呢?這個問題有好多有意思的答案。

其中一個原因跟搜索引擎排名有關(guān)。你看,如果一個頁面有兩個地址,就像http://www.igoro.com/ 和http://igoro.com/,搜索引擎會認為它們是兩個網(wǎng)站,結(jié)果造成每一個的搜索鏈接都減少從而降低排名。而搜索引擎知道301永久重定向是什么意思,這樣就會把訪問帶www的和不帶www的地址歸到同一個網(wǎng)站排名下。

還有一個是用不同的地址會造成緩存友好性變差。當一個頁面有好幾個名字時,它可能會在緩存里出現(xiàn)好幾次。

5. 瀏覽器跟蹤重定向地址

image

現(xiàn)在,瀏覽器知道了“http://www.facebook.com/”才是要訪問的正確地址,所以它會發(fā)送另一個獲取請求:

GET http://www.facebook.com/ HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Cookie: lsd=XW[...]; c_user=21[...]; x-referer=[...]
Host: www.facebook.com

頭信息以之前請求中的意義相同。

6. 服務器“處理”請求

image

服務器接收到獲取請求,然后處理并返回一個響應。

這表面上看起來是一個順向的任務,但其實這中間發(fā)生了很多有意思的東西- 就像作者博客這樣簡單的網(wǎng)站,何況像facebook那樣訪問量大的網(wǎng)站呢!

  • Web 服務器軟件
    web服務器軟件(像IIS和阿帕奇)接收到HTTP請求,然后確定執(zhí)行什么請求處理來處理它。請求處理就是一個能夠讀懂請求并且能生成HTML來進行響應的程序(像ASP.NET,PHP,RUBY...)。

    舉個最簡單的例子,需求處理可以以映射網(wǎng)站地址結(jié)構(gòu)的文件層次存儲。像http://example.com/folder1/page1.aspx這個地址會映射/httpdocs/folder1/page1.aspx這個文件。web服務器軟件可以設置成為地址人工的對應請求處理,這樣page1.aspx的發(fā)布地址就可以是http://example.com/folder1/page1。

  • 請求處理
    請求處理閱讀請求及它的參數(shù)和cookies。它會讀取也可能更新一些數(shù)據(jù),并講數(shù)據(jù)存儲在服務器上。然后,需求處理會生成一個HTML響應。

所有動態(tài)網(wǎng)站都面臨一個有意思的難點 -如何存儲數(shù)據(jù)。小網(wǎng)站一半都會有一個SQL數(shù)據(jù)庫來存儲數(shù)據(jù),存儲大量數(shù)據(jù)和/或訪問量大的網(wǎng)站不得不找一些辦法把數(shù)據(jù)庫分配到多臺機器上。解決方案有:sharding (基于主鍵值講數(shù)據(jù)表分散到多個數(shù)據(jù)庫中),復制,利用弱語義一致性的簡化數(shù)據(jù)庫。

委托工作給批處理是一個廉價保持數(shù)據(jù)更新的技術(shù)。舉例來講,F(xiàn)ackbook得及時更新新聞feed,但數(shù)據(jù)支持下的“你可能認識的人”功能只需要每晚更新(作者猜測是這樣的,改功能如何完善不得而知)。批處理作業(yè)更新會導致一些不太重要的數(shù)據(jù)陳舊,但能使數(shù)據(jù)更新耕作更快更簡潔。

7. 服務器發(fā)回一個HTML響應

image

圖中為服務器生成并返回的響應:

HTTP/1.1 200 OK
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
pre-check=0
Expires: Sat, 01 Jan 2000 00:00:00 GMT
P3P: CP="DSP LAW"
Pragma: no-cache
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
X-Cnection: close
Transfer-Encoding: chunked
Date: Fri, 12 Feb 2010 09:05:55 GMT

2b3????????T?n?@????[...]

整個響應大小為35kB,其中大部分在整理后以blob類型傳輸。

內(nèi)容編碼頭告訴瀏覽器整個響應體用gzip算法進行壓縮。解壓blob塊后,你可以看到如下期望的HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"    
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en" id="facebook" class=" no_js">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-language" content="en" />
...

關(guān)于壓縮,頭信息說明了是否緩存這個頁面,如果緩存的話如何去做,有什么cookies要去設置(前面這個響應里沒有這點)和隱私信息等等。

請注意報頭中把Content-type設置為“text/html”。報頭讓瀏覽器將該響應內(nèi)容以HTML形式呈現(xiàn),而不是以文件形式下載它。瀏覽器會根據(jù)報頭信息決定如何解釋該響應,不過同時也會考慮像URL擴展內(nèi)容等其他因素。

8. 瀏覽器開始顯示HTML

在瀏覽器沒有完整接受全部HTML文檔時,它就已經(jīng)開始顯示這個頁面了:

image

9. 瀏覽器發(fā)送獲取嵌入在HTML中的對象

image

在瀏覽器顯示HTML時,它會注意到需要獲取其他地址內(nèi)容的標簽。這時,瀏覽器會發(fā)送一個獲取請求來重新獲得這些文件。

下面是幾個我們訪問facebook.com時需要重獲取的幾個URL:

  • 圖片
    http://static.ak.fbcdn.net/rsrc.php/z12E0/hash/8q2anwu7.gif
    http://static.ak.fbcdn.net/rsrc.php/zBS5C/hash/7hwy7at6.gif
  • CSS 式樣表
    http://static.ak.fbcdn.net/rsrc.php/z448Z/hash/2plh8s4n.css
    http://static.ak.fbcdn.net/rsrc.php/zANE1/hash/cvtutcee.css
  • JavaScript 文件
    http://static.ak.fbcdn.net/rsrc.php/zEMOA/hash/c8yzb6ub.js
    http://static.ak.fbcdn.net/rsrc.php/z6R9L/hash/cq2lgbs8.js

這些地址都要經(jīng)歷一個和HTML讀取類似的過程。所以瀏覽器會在DNS中查找這些域名,發(fā)送請求,重定向等等...

但不像動態(tài)頁面那樣,靜態(tài)文件會允許瀏覽器對其進行緩存。有的文件可能會不需要與服務器通訊,而從緩存中直接讀取。服務器的響應中包含了靜態(tài)文件保存的期限信息,所以瀏覽器知道要把它們緩存多長時間。還有,每個響應都可能包含像版本號一樣工作的ETag頭(被請求變量的實體值),如果瀏覽器觀察到文件的版本ETag信息已經(jīng)存在,就馬上停止這個文件的傳輸。

試著猜猜看“fbcdn.net”在地址中代表什么?聰明的答案是"Facebook內(nèi)容分發(fā)網(wǎng)絡"。Facebook利用內(nèi)容分發(fā)網(wǎng)絡(CDN)分發(fā)像圖片,CSS表和JavaScript文件這些靜態(tài)文件。所以,這些文件會在全球很多CDN的數(shù)據(jù)中心中留下備份。

靜態(tài)內(nèi)容往往代表站點的帶寬大小,也能通過CDN輕松的復制。通常網(wǎng)站會使用第三方的CDN。例如,F(xiàn)acebook的靜態(tài)文件由最大的CDN提供商Akamai來托管。

舉例來講,當你試著ping static.ak.fbcdn.net的時候,可能會從某個akamai.net服務器上獲得響應。有意思的是,當你同樣再ping一次的時候,響應的服務器可能就不一樣,這說明幕后的負載平衡開始起作用了。

10. 瀏覽器發(fā)送異步(AJAX)請求

image

在Web 2.0偉大精神的指引下,頁面顯示完成后客戶端仍與服務器端保持著聯(lián)系。

以Facebook聊天功能為例,它會持續(xù)與服務器保持聯(lián)系來及時更新你那些亮亮灰灰的好友狀態(tài)。為了更新這些頭像亮著的好友狀態(tài),在瀏覽器中執(zhí)行的JavaScript代碼會給服務器發(fā)送異步請求。這個異步請求發(fā)送給特定的地址,它是一個按照程式構(gòu)造的獲取或發(fā)送請求。還是在Facebook這個例子中,客戶端發(fā)送給http://www.facebook.com/ajax/chat/buddy_list.php一個發(fā)布請求來獲取你好友里哪個在線的狀態(tài)信息。

提起這個模式,就必須要講講"AJAX"-- “異步JavaScript 和 XML”,雖然服務器為什么用XML格式來進行響應也沒有個一清二白的原因。再舉個例子吧,對于異步請求,F(xiàn)acebook會返回一些JavaScript的代碼片段。

除了其他,fiddler這個工具能夠讓你看到瀏覽器發(fā)送的異步請求。事實上,你不僅可以被動的做為這些請求的看客,還能主動出擊修改和重新發(fā)送它們。AJAX請求這么容易被蒙,可著實讓那些計分的在線游戲開發(fā)者們郁悶的了。(當然,可別那樣騙人家~)

Facebook聊天功能提供了關(guān)于AJAX一個有意思的問題案例:把數(shù)據(jù)從服務器端推送到客戶端。因為HTTP是一個請求-響應協(xié)議,所以聊天服務器不能把新消息發(fā)給客戶。取而代之的是客戶端不得不隔幾秒就輪詢下服務器端看自己有沒有新消息。

這些情況發(fā)生時長輪詢是個減輕服務器負載挺有趣的技術(shù)。如果當被輪詢時服務器沒有新消息,它就不理這個客戶端。而當尚未超時的情況下收到了該客戶的新消息,服務器就會找到未完成的請求,把新消息做為響應返回給客戶端。

總結(jié)一下

希望看了本文,你能明白不同的網(wǎng)絡模塊是如何協(xié)同工作的



oyjpart 2010-02-26 16:22 發(fā)表評論
]]>
記錄一個bughttp://www.shnenglu.com/sicheng/archive/2009/10/24/99336.htmloyjpartoyjpartFri, 23 Oct 2009 18:05:00 GMThttp://www.shnenglu.com/sicheng/archive/2009/10/24/99336.htmlhttp://www.shnenglu.com/sicheng/comments/99336.htmlhttp://www.shnenglu.com/sicheng/archive/2009/10/24/99336.html#Feedback0http://www.shnenglu.com/sicheng/comments/commentRss/99336.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/99336.html vector<SyllState> StateQueue;

 StateQueue.reserve(m_psz.GetLength());
 StateQueue.push_back(SyllState(_T(""), iEndCharIdx));
 BOOL bExit = FALSE;
 INT iBegin = 0;
 while(iBegin < (INT)StateQueue.size() && !bExit)
 {
  INT& iIndex = StateQueue[iBegin].iIndex;
  CString& strSyll = StateQueue[iBegin].strSyll;

  iBegin++;

  INT& nNumOfPres = m_pNumOfPre[iIndex];
  for(INT i = 0; i < nNumOfPres; ++i)
  {
   INT& nPre = m_pPre[iIndex][i];
   if(nPre < iBegCharIdx) continue;

   if(nPre == iBegCharIdx)
   {
    CString strSyllFound = m_psz.Mid(nPre, iIndex - nPre) + _T("\'") + strSyll;
    vecSubSyllable.Add(strSyllFound.Left(strSyllFound.GetLength()-1));
    if(vecSubSyllable.GetCount() >= MAX_SYLLABLE_NUM)
    {
     bExit = TRUE;
     break;
    }
   }
   else
   {
    StateQueue.push_back(SyllState(
     m_psz.Mid(nPre, iIndex - nPre) + _T("\'") + strSyll,
     nPre));
   }
  }
 }


上述代碼有問題嗎?
有。拿的是vector的引用,但是vector自動擴容去了,結(jié)果導致引用位置內(nèi)存被重寫,引起錯誤。

oyjpart 2009-10-24 02:05 發(fā)表評論
]]>
【轉(zhuǎn)】簡介vc中的release和debug版本的區(qū)別http://www.shnenglu.com/sicheng/archive/2009/03/16/76698.htmloyjpartoyjpartSun, 15 Mar 2009 16:25:00 GMThttp://www.shnenglu.com/sicheng/archive/2009/03/16/76698.htmlhttp://www.shnenglu.com/sicheng/comments/76698.htmlhttp://www.shnenglu.com/sicheng/archive/2009/03/16/76698.html#Feedback0http://www.shnenglu.com/sicheng/comments/commentRss/76698.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/76698.html原文地址http://www.cinrad.com/Get/Program/VC/VC/223112980.htm

Debug通常稱為調(diào)試版本,它包含調(diào)試信息,并且不作任何優(yōu)化,便于程序員調(diào)試程序。Release稱為發(fā)布版本,它往往是進行了各種優(yōu)化,使得程序在代碼大小和運行速度上都是最優(yōu)的,以便用戶很好地使用。

Debug 和 Release 的真正秘密,在于一組編譯選項。下面列出了分別針對二者的選項(當然除此之外還有其他一些,如/Fd /Fo,但區(qū)別并不重要,通常他們也不會引起 Release 版錯誤,在此不討論)

Debug 版本


參數(shù) 含義
/MDd /MLd 或 /MTd 使用 Debug runtime library (調(diào)試版本的運行時刻函數(shù)庫)
/Od 關(guān)閉優(yōu)化開關(guān)
/D "_DEBUG" 相當于 #define _DEBUG,打開編譯調(diào)試代碼開關(guān) (主要針對assert函數(shù))
/ZI 創(chuàng)建 Edit and continue(編輯繼續(xù))數(shù)據(jù)庫,這樣在調(diào)試過程中如果修改了源代碼不需重新編譯
/GZ 可以幫助捕獲內(nèi)存錯誤
/Gm 打開最小化重鏈接開關(guān), 減少鏈接時間

 

Release 版本


參數(shù) 含義
/MD /ML 或 /MT 使用發(fā)布版本的運行時刻函數(shù)庫
/O1 或 /O2 優(yōu)化開關(guān),使程序最小或最快
/D "NDEBUG" 關(guān)閉條件編譯調(diào)試代碼開關(guān) (即不編譯assert函數(shù))
/GF 合并重復的字符串, 并將字符串常量放到只讀內(nèi)存, 防止被修改

 

實際上,Debug 和 Release 并沒有本質(zhì)的界限,他們只是一組編譯選項的集合,編譯器只是按照預定的選項行動。事實上,我們甚至可以修改這些選項,從而得到優(yōu)化過的調(diào)試版本或是帶跟蹤語句的發(fā)布版本。

哪些情況下 Release 版會出錯

有了上面的介紹,我們再來逐個對照這些選項看看 Release 版錯誤是怎樣產(chǎn)生的

1、Runtime Library:鏈接哪種運行時刻函數(shù)庫通常只對程序的性能產(chǎn)生影響。調(diào)試版本的 Runtime Library 包含了調(diào)試信息,并采用了一些保護機制以幫助發(fā)現(xiàn)錯誤,因此性能不如發(fā)布版本。編譯器提供的 Runtime Library 通常很穩(wěn)定,不會造成 Release 版錯誤;倒是由于 Debug 的 Runtime Library 加強了對錯誤的檢測,如堆內(nèi)存分配,有時會出現(xiàn) Debug 有錯但 Release 正常的現(xiàn)象。應當指出的是,如果 Debug 有錯,即使 Release 正常,程序肯定是有 Bug 的,只不過可能是 Release 版的某次運行沒有表現(xiàn)出來而已。

2、優(yōu)化:這是造成錯誤的主要原因,因為關(guān)閉優(yōu)化時源程序基本上是直接翻譯的,而打開優(yōu)化后編譯器會作出一系列假設。這類錯誤主要有以下幾種:

1. 幀指針(Frame Pointer)省略(簡稱FPO):在函數(shù)調(diào)用過程中,所有調(diào)用信息(返回地址、參數(shù))以及自動變量都是放在棧中的。若函數(shù)的聲明與實現(xiàn)不同(參數(shù)、返 回值、調(diào)用方式),就會產(chǎn)生錯誤,但 Debug 方式下,棧的訪問通過 EBP 寄存器保存的地址實現(xiàn),如果沒有發(fā)生數(shù)組越界之類的錯誤(或是越界“不多”),函數(shù)通常能正常執(zhí)行;Release 方式下,優(yōu)化會省略 EBP ?;分羔?,這樣通過一個全局指針訪問棧就會造成返回地址錯誤是程序崩潰。

C++ 的強類型特性能檢查出大多數(shù)這樣的錯誤,但如果用了強制類型轉(zhuǎn)換,就不行了。你可以在 Release 版本中強制加入/Oy-編譯選項來關(guān)掉幀指針省略,以確定是否此類錯誤。此類錯誤通常有:MFC 消息響應函數(shù)書寫錯誤。正確的應為:


afx_msg LRESULT OnMessageOwn
(WPARAM wparam, LPARAM lparam);
 
ON_MESSAGE 宏包含強制類型轉(zhuǎn)換。防止這種錯誤的方法之一是重定義 ON_MESSAGE 宏,把下列代碼加到 stdafx.h 中(在#include "afxwin.h"之后),函數(shù)原形錯誤時編譯會報錯。


#undef ON_MESSAGE
#define ON_MESSAGE(message, memberFxn) \
{
message, 0, 0, 0, AfxSig_lwl, \
(AFX_PMSG)(AFX_PMSGW)
(static_cast< LRESULT (AFX_MSG_CALL \
CWnd::*)(WPARAM, LPARAM) > (&memberFxn)
},


2. volatile 型變量:volatile 告訴編譯器該變量可能被程序之外的未知方式修改(如系統(tǒng)、其他進程和線程)。優(yōu)化程序為了使程序性能提高,常把一些變量放在寄存器中(類似于 register 關(guān)鍵字),而其他進程只能對該變量所在的內(nèi)存進行修改,而寄存器中的值沒變。

如果你的程序是多線程的,或者你發(fā)現(xiàn)某個變量的值與預期的不符而你確信已正確的設置了,則很可能遇到這樣的問題。這種錯誤有時會表現(xiàn)為程序在最快優(yōu)化出錯而最小優(yōu)化正常。把你認為可疑的變量加上 volatile 試試。

3. 變量優(yōu)化:優(yōu)化程序會根據(jù)變量的使用情況優(yōu)化變量。例如,函數(shù)中有一個未被使用的變量,在 Debug 版中它有可能掩蓋一個數(shù)組越界,而在 Release 版中,這個變量很可能被優(yōu)化調(diào),此時數(shù)組越界會破壞棧中有用的數(shù)據(jù)。當然,實際的情況會比這復雜得多。與此有關(guān)的錯誤有非法訪問,包括數(shù)組越界、指針錯誤 等。例如:


void fn(void)
{
  int i;
  i = 1;
  int a[4];
  {
    int j;
    j = 1;
  }
  a[-1] = 1;
  //當然錯誤不會這么明顯,例如下標是變量
  a[4] = 1;
}
 

 

j 雖然在數(shù)組越界時已出了作用域,但其空間并未收回,因而 i 和 j 就會掩蓋越界。而 Release 版由于 i、j 并未其很大作用可能會被優(yōu)化掉,從而使棧被破壞。

3. DEBUG 與 NDEBUG :當定義了 _DEBUG 時,assert() 函數(shù)會被編譯,而 NDEBUG 時不被編譯。此外,TRACE() 宏的編譯也受 _DEBUG 控制。

所有這些斷言都只在 Debug版中才被編譯,而在 Release 版中被忽略。唯一的例外是 VERIFY()。事實上,這些宏都是調(diào)用了assert()函數(shù),只不過附加了一些與庫有關(guān)的調(diào)試代碼。如果你在這些宏中加入了任何程序代碼,而不只是 布爾表達式(例如賦值、能改變變量值的函數(shù)調(diào)用等),那么Release版都不會執(zhí)行這些操作,從而造成錯誤。初學者很容易犯這類錯誤,查找的方法也很簡 單,因為這些宏都已在上面列出,只要利用 VC++ 的 Find in Files 功能在工程所有文件中找到用這些宏的地方再一一檢查即可。另外,有些高手可能還會加入 #ifdef _DEBUG 之類的條件編譯,也要注意一下。

順便值得一提的是VERIFY()宏,這個宏允許你將程序代碼放在布爾表達式里。這個宏通常用來檢查 Windows API的返回值。有些人可能為這個原因而濫用VERIFY(),事實上這是危險的,因為VERIFY()違反了斷言的思想,不能使程序代碼和調(diào)試代碼完全 分離,最終可能會帶來很多麻煩。因此,專家們建議盡量少用這個宏。

4. /GZ 選項:這個選項會做以下這些事:

1. 初始化內(nèi)存和變量。包括用 0xCC 初始化所有自動變量,0xCD ( Cleared Data ) 初始化堆中分配的內(nèi)存(即動態(tài)分配的內(nèi)存,例如 new ),0xDD ( Dead Data ) 填充已被釋放的堆內(nèi)存(例如 delete ),0xFD( deFencde Data ) 初始化受保護的內(nèi)存(debug 版在動態(tài)分配內(nèi)存的前后加入保護內(nèi)存以防止越界訪問),其中括號中的詞是微軟建議的助記詞。這樣做的好處是這些值都很大,作為指針是不可能的(而且 32 位系統(tǒng)中指針很少是奇數(shù)值,在有些系統(tǒng)中奇數(shù)的指針會產(chǎn)生運行時錯誤),作為數(shù)值也很少遇到,而且這些值也很容易辨認,因此這很有利于在 Debug 版中發(fā)現(xiàn) Release 版才會遇到的錯誤。要特別注意的是,很多人認為編譯器會用0來初始化變量,這是錯誤的(而且這樣很不利于查找錯誤)。

2. 通過函數(shù)指針調(diào)用函數(shù)時,會通過檢查棧指針驗證函數(shù)調(diào)用的匹配性。(防止原形不匹配)

3. 函數(shù)返回前檢查棧指針,確認未被修改。(防止越界訪問和原形不匹配,與第二項合在一起可大致模擬幀指針省略 FPO )通常 /GZ 選項會造成 Debug 版出錯而 Release 版正常的現(xiàn)象,因為 Release 版中未初始化的變量是隨機的,這有可能使指針指向一個有效地址而掩蓋了非法訪問。除此之外,/Gm/GF等選項造成錯誤的情況比較少,而且他們的效果顯而 易見,比較容易發(fā)現(xiàn)。

怎樣“調(diào)試” Release 版的程序

遇到Debug成功但Release失敗,顯然是一件很沮喪的事,而且往往無從下手。如果你看了以上的分析,結(jié)合錯誤的具體表現(xiàn),很快找出了錯誤,固然很好。但如果一時找不出,以下給出了一些在這種情況下的策略。

1. 前面已經(jīng)提過,Debug和Release只是一組編譯選項的差別,實際上并沒有什么定義能區(qū)分二者。我們可以修改Release版的編譯選項來縮小錯誤 范圍。如上所述,可以把Release 的選項逐個改為與之相對的Debug選項,如/MD改為/MDd、/O1改為/Od,或運行時間優(yōu)化改為程序大小優(yōu)化。注意,一次只改一個選項,看改哪個 選項時錯誤消失,再對應該選項相關(guān)的錯誤,針對性地查找。這些選項在Project\Settings...中都可以直接通過列表選取,通常不要手動修 改。由于以上的分析已相當全面,這個方法是最有效的。

2. 在編程過程中就要時常注意測試 Release 版本,以免最后代碼太多,時間又很緊。

3. 在 Debug 版中使用 /W4 警告級別,這樣可以從編譯器獲得最大限度的錯誤信息,比如 if( i =0 )就會引起 /W4 警告。不要忽略這些警告,通常這是你程序中的 Bug 引起的。但有時 /W4 會帶來很多冗余信息,如 未使用的函數(shù)參數(shù) 警告,而很多消息處理函數(shù)都會忽略某些參數(shù)。我們可以用:


#progma warning(disable: 4702)
//禁止
//...
#progma warning(default: 4702)
//重新允許來暫時禁止某個警告,或使用
#progma warning(push, 3)
//設置警告級別為 /W3
//...
#progma warning(pop)
//重設為 /W4
 

 

來暫時改變警告級別,有時你可以只在認為可疑的那一部分代碼使用 /W4。

4. 你也可以像Debug一樣調(diào)試你的Release版,只要加入調(diào)試符號。在Project/Settings... 中,選中 Settings for "Win32 Release",選中 C/C++ 標簽,Category 選 General,Debug Info 選 Program Database。再在 Link 標簽 Project options 最后加上 "/OPT:REF" (引號不要輸)。這樣調(diào)試器就能使用 pdb 文件中的調(diào)試符號。

但調(diào)試時你會發(fā)現(xiàn)斷點很難設置,變量也很難找到??這些都被優(yōu)化過了。不過令人慶幸的是,Call Stack窗口仍然工作正常,即使幀指針被優(yōu)化,棧信息(特別是返回地址)仍然能找到。這對定位錯誤很有幫助。



oyjpart 2009-03-16 00:25 發(fā)表評論
]]>
SRM406 PTS500 FoldThePaperhttp://www.shnenglu.com/sicheng/archive/2008/06/18/53831.htmloyjpartoyjpartWed, 18 Jun 2008 03:29:00 GMThttp://www.shnenglu.com/sicheng/archive/2008/06/18/53831.htmlhttp://www.shnenglu.com/sicheng/comments/53831.htmlhttp://www.shnenglu.com/sicheng/archive/2008/06/18/53831.html#Feedback0http://www.shnenglu.com/sicheng/comments/commentRss/53831.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/53831.html

Problem Statement

     You have a rectangular piece of paper that's divided into 1x1 cells, each of which has an integer value. The paper will be described by a vector <string> paper. The ith element of paper will be a space delimited list of integers, where the jth integer of the ith element of paper represents the value of the jth cell of the ith row of the paper.



You want to perform a sequence of folds on the paper, where you may fold anywhere along an axis that is in between two rows or columns of the paper. After performing a fold, we wish to model the folded paper as a new, flat piece of paper. We will do this by considering two overlapping cells as a single cell, with a value that is the sum of the individual cells.



You wish to perform a sequence of folds such that the value of some single cell in the resulting piece of paper is as large as possible. Return this value.

Definition

    
Class: FoldThePaper
Method: getValue
Parameters: vector <string>
Returns: int
Method signature: int getValue(vector <string> paper)
(be sure your method is public)
    

Constraints

- paper will contain between 1 and 12 elements, inclusive. - Each element of paper will be a single-space delimited list of integers with no leading or trailing spaces. - Each element of paper will contain between 1 and 12 integers, inclusive. - Each element of paper will contain the same number of integers. - Each element of paper will contain between 1 and 50 characters, inclusive. - Each integer in paper will be between -100 and 100, inclusive. - Each integer in paper will have no leading zeros. - An integer in paper equal to zero will not have a preceding negative sign.

Examples

0)
    
{
"1 1 1",
"1 1 1"
}
Returns: 6
We can collapse every cell onto the upper-left cell.
1)
    
{
"1 -1",
"1 -1"
}
Returns: 2
We should perform only the fold between the two rows, and take the resulting left column.
2)
    
{
"1 -1 -1 1",
"-1 -1 -1 -1",
"-1 -1 -1 -1",
"1 -1 -1 1"
}
Returns: 4
Folding between the middle rows then the middle columns allows us to combine the four corner cells.
3)
    
{
"20 13 -2 100",
"-12 0 4 -3",
"4 1 -36 21"
}
Returns: 131

4)
    
{
"0"
}
Returns: 0

This problem statement is the exclusive and proprietary property of TopCoder, Inc. Any unauthorized use or reproduction of this information without the prior written consent of TopCoder, Inc. is strictly prohibited. (c)2003, TopCoder, Inc. All rights reserved.


題目大意是有一個12*12的矩陣,現(xiàn)在可以對這個矩陣橫向或縱向折疊,出在重疊位置的數(shù)相加。
求折疊過程中任意位置產(chǎn)生的最大數(shù)。

很多大牛fail了,我一個DFS+剪枝也超時了,一共32人pass sys test,1000pts無人ac,此套題難度還是很大的。

基本思路是狀態(tài)壓縮DP,橫向(1<<12)*縱向(1<<12)*加和。

但是這樣會超時。關(guān)鍵是沒有利用到折疊的信息。

預先生成某個位置的狀態(tài)(由那些位置疊加而來),就可以減少檢查量,就可以ac了。

如何生成這些狀態(tài)呢?沒錯,又是一個DP. 呵呵。




oyjpart 2008-06-18 11:29 發(fā)表評論
]]>
Tomcat JSP Web 開發(fā)中的亂碼問題小結(jié)http://www.shnenglu.com/sicheng/archive/2008/05/22/50739.htmloyjpartoyjpartThu, 22 May 2008 03:26:00 GMThttp://www.shnenglu.com/sicheng/archive/2008/05/22/50739.htmlhttp://www.shnenglu.com/sicheng/comments/50739.htmlhttp://www.shnenglu.com/sicheng/archive/2008/05/22/50739.html#Feedback0http://www.shnenglu.com/sicheng/comments/commentRss/50739.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/50739.html1. 靜態(tài)頁面的亂碼問題
文件的編碼和瀏覽器要顯示的編碼不一致。
1) 檢查文件原始的編碼, 可以用記事本打開, 然后選擇另存為來看;
2) 給當前頁面加入一個指令來建議瀏覽器用指定的編碼來顯示文件字符內(nèi)容.
<meta http-equiv="content-type" content="text/html; charset=GBK">
3) 如果系統(tǒng)是英文XP,沒裝東亞字符集支持, 也會顯示亂碼.
2. JSP 頁面的亂碼問題
1) page 指令有個 pageEncoding="GBK" 這個會指定當前頁面保存的編碼, 如果寫成
ISO8859-1 就不能保存漢字;
2) page 指令的 contentType="text/html; charset=ISO8859-1" 也會像靜態(tài)頁面一樣讓
瀏覽器來優(yōu)先選擇一種編碼.
如果JSP 亂碼的話,一般就顯示成?,而且不管你給瀏覽器選什么樣的編碼,它都不能正
確顯示
3. 表單提交的亂碼問題(Tomcat 特有)
1). POST 的亂碼
MyEclipse 6 Java 開發(fā)中文教程
157 劉長炯著
a. 首先瀏覽器提交表單的編碼是根據(jù)表單所在頁面來決定的, 而不是根據(jù)提交后的
JSP 頁面的編碼來決定的. 把所有的頁面的編碼都設置成一樣的,例如 GBK.
b. 處理方式就是在獲取參數(shù)之前設置編碼:
request.setCharacterEncoding("GBK");
c. 可以用過濾器的方式來解決, Tomcat 已經(jīng)帶了一個現(xiàn)成的:
apache-tomcat-5.5.23\webapps\jsp-examples\WEB-INF\classes\filters\SetCharacter
EncodingFilter.java
web.xml
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2) GET 方式的亂碼
用 setCharacterEncoding() 不能解決. TOMCAT 的一個BUG, GET 方式傳送的表單參
數(shù)總是用的 ISO8859-1 編碼. 我們要把它轉(zhuǎn)成 GBK 方式.
String username = request.getParameter("username");
System.out.println(username);
// 轉(zhuǎn)碼, 先取得原始的二進制字節(jié)數(shù)組
byte[] data = username.getBytes("ISO8859-1");
// 根據(jù)新的字符集再構(gòu)造新的字符串
username = new String(data, "GBK");
小結(jié):
所有的頁面(除了最后的 GET 的亂碼問題)都用統(tǒng)一的編碼(GBK 或者UTF-8), 就不會出現(xiàn)
亂碼問題

以上內(nèi)容為【轉(zhuǎn)】

下面是一個完整的過濾器例子:
package filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class TomcatFormFilter implements Filter {
    
/**
     * Request.java 對 HttpServletRequestWrapper 進行擴充, 不影響原來的功能并能提供所 有的
     * HttpServletRequest 接口中的功能. 它可以統(tǒng)一的對 Tomcat 默認設置下的中文問題進行解決而只 需要用新的 Request
     * 對象替換頁面中的 request 對象即可.
     
*/
    
class Request extends HttpServletRequestWrapper {
        
public Request(HttpServletRequest request) {
            
super(request);
        }

        
/**
         * 轉(zhuǎn)換由表單讀取的數(shù)據(jù)的內(nèi)碼. 從 ISO 字符轉(zhuǎn)到 GBK.
         
*/
        
public String toChi(String input) {
            
try {
                
byte[] bytes = input.getBytes("ISO8859-1");
                
return new String(bytes, "utf-8");
            } 
catch (Exception ex) {
            }
            
return null;
        }

        
/**
         * Return the HttpServletRequest holded by this object.
         
*/
        
private HttpServletRequest getHttpServletRequest() {
            
return (HttpServletRequest) super.getRequest();
        }

        
/**
         * 讀取參數(shù) -- 修正了中文問題.
         
*/
        
public String getParameter(String name) {
            
return toChi(getHttpServletRequest().getParameter(name));
        }

        
/**
         * 讀取參數(shù)列表 - 修正了中文問題.
         
*/
        
public String[] getParameterValues(String name) {
            String values[] 
= getHttpServletRequest().getParameterValues(name);
            
if (values != null) {
                
for (int i = 0; i < values.length; i++) {
                    values[i] 
= toChi(values[i]);
                }
            }
            
return values;
        }
    }

    
public void destroy() {
    }

    
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        HttpServletRequest httpreq 
= (HttpServletRequest) request;
        
if (httpreq.getMethod().equals("POST")) {
            request.setCharacterEncoding(
"utf-8");
        } 
else {
            request 
= new Request(httpreq);
        }
        response.setCharacterEncoding(
"utf-8");
        chain.doFilter(request, response);
    }

    
public void init(FilterConfig filterConfig) throws ServletException {
    }
}

web.xml中加入如下配置信息
    <filter>
        
<filter-name>TomcatFormFilter</filter-name>
        
<filter-class>filters.TomcatFormFilter</filter-class>
    
</filter>
    
<filter-mapping>
        
<filter-name>TomcatFormFilter</filter-name>
        
<url-pattern>/*</url-pattern>
    
</filter-mapping>


上述都是使用統(tǒng)一的GBK編碼,如果想用utf-8編碼的話,稍作修改就可以了。
在解決了JSP亂碼問題之后,還有一個問題,就是數(shù)據(jù)庫亂碼問題。
我使用的數(shù)據(jù)庫是mysql
在hibernate的配置文件中加入
    <property name="connection.useUnicode">true</property>
    <property name="connection.characterEncoding">UTF-8</property>
就算是一整套的中文解決方案了。



oyjpart 2008-05-22 11:26 發(fā)表評論
]]>
有信息的搜索--傳教士與食人魔的故事http://www.shnenglu.com/sicheng/archive/2008/04/04/46267.htmloyjpartoyjpartFri, 04 Apr 2008 10:15:00 GMThttp://www.shnenglu.com/sicheng/archive/2008/04/04/46267.htmlhttp://www.shnenglu.com/sicheng/comments/46267.htmlhttp://www.shnenglu.com/sicheng/archive/2008/04/04/46267.html#Feedback6http://www.shnenglu.com/sicheng/comments/commentRss/46267.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/46267.html閱讀全文

oyjpart 2008-04-04 18:15 發(fā)表評論
]]>
【轉(zhuǎn)】正則表達式30分鐘入門教程http://www.shnenglu.com/sicheng/archive/2008/03/20/44944.htmloyjpartoyjpartThu, 20 Mar 2008 06:15:00 GMThttp://www.shnenglu.com/sicheng/archive/2008/03/20/44944.htmlhttp://www.shnenglu.com/sicheng/comments/44944.htmlhttp://www.shnenglu.com/sicheng/archive/2008/03/20/44944.html#Feedback1http://www.shnenglu.com/sicheng/comments/commentRss/44944.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/44944.html正則表達式30分鐘入門教程

匹配Email地址的正則表達式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
匹配網(wǎng)址URL的正則表達式:http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

版本:v2.21 (2007-8-3) 作者:deerchao 來源:unibetter大學生社區(qū) 轉(zhuǎn)載請注明來源

目錄

  1. 本文目標
  2. 如何使用本教程
  3. 正則表達式到底是什么?
  4. 入門
  5. 測試正則表達式
  6. 元字符
  7. 字符轉(zhuǎn)義
  8. 重復
  9. 字符類
  10. 反義
  11. 替換
  12. 分組
  13. 后向引用
  14. 零寬斷言
  15. 負向零寬斷言
  16. 注釋
  17. 貪婪與懶惰
  18. 處理選項
  19. 平衡組/遞歸匹配
  20. 還有些什么東西沒提到
  21. 聯(lián)系作者
  22. 最后,來點廣告...
  23. 一些我認為你可能已經(jīng)知道的術(shù)語的參考
  24. 網(wǎng)上的資源及本文參考文獻
  25. 更新說明

本文目標

30分鐘內(nèi)讓你明白正則表達式是什么,并對它有一些基本的了解,讓你可以在自己的程序或網(wǎng)頁里使用它。

如何使用本教程

最重要的是——請給我30分鐘,如果你沒有使用正則表達式的經(jīng)驗,請不要試圖在30內(nèi)入門。當然,如果你是超人,那自然得另當別論。

別被下面那些復雜的表達式嚇倒,只要跟著我一步一步來,你會發(fā)現(xiàn)正則表達式其實并沒有你想像中的那么困難。當然,如果你看完了這篇教程之后,發(fā)現(xiàn)自己明白了很多,卻又幾乎什么都記不得,那也是很正常的——我認為,沒接觸過正則表達式的人在看完這篇教程后,能把提到過的語法記住80%以上的可能性為零。這里只是讓你明白基本的原理,以后你還需要多練習,多使用,才能熟練掌握正則表達式。

除了作為入門教程之外,本文還試圖成為可以在日常工作中使用的正則表達式語法參考手冊。就作者本人的經(jīng)歷來說,這個目標還是完成得不錯的——你看,我自己也沒能把所有的東西記下來,不是嗎?

文本格式約定:專業(yè)術(shù)語 元字符/語法格式 正則表達式 正則表達式中的一部分(用于分析) 用于在其中搜索的字符串 對正則表達式或其中一部分的說明清除格式

正則表達式到底是什么?

在編寫處理字符串的程序或網(wǎng)頁時,經(jīng)常會有查找符合某些復雜規(guī)則的字符串的需要。正則表達式就是用于描述這些規(guī)則的工具。換句話說,正則表達式就是記錄文本規(guī)則的代碼。

很可能你使用過Windows/Dos下用于文件查找的通配符(wildcard),也就是*?。如果你想查找某個目錄下的所有的Word文檔的話,你會搜索*.doc。在這里,*會被解釋成任意的字符串。和通配符類似,正則表達式也是用來進行文本匹配的工具,只不過比起通配符,它能更精確地描述你的需求——當然,代價就是更復雜——比如你可以編寫一個正則表達式,用來查找所有以0開頭,后面跟著2-3個數(shù)字,然后是一個連字號“-”,最后是7或8位數(shù)字的字符串(像010-123456780376-7654321)。

正則表達式是用于進行文本匹配的工具,所以本文里多次提到了在字符串里搜索/查找,這種說法的意思是在給定的字符串中,尋找與給定的正則表達式相匹配的部分。有可能字符串里有不止一個部分滿足給定的正則表達式,這時每一個這樣的部分被稱為一個匹配。匹配在本文里可能會有三種意思:一種是形容詞性的,比如說一個字符串匹配一個表達式;一種是動詞性的,比如說在字符串里匹配正則表達式;還有一種是名詞性的,就是剛剛說到的“字符串中滿足給定的正則表達式的一部分”。

入門

學習正則表達式的最好方法是從例子開始,理解例子之后再自己對例子進行修改,實驗。下面給出了不少簡單的例子,并對它們作了詳細的說明。

假設你在一篇英文小說里查找hi,你可以使用正則表達式hi。

這是最簡單的正則表達式了,它可以精確匹配這樣的字符串:由兩個字符組成,前一個字符是h,后一個是i。通常,處理正則表達式的工具會提供一個忽略大小寫的選項,如果選中了這個選項,它可以匹配hi,HI,Hi,hI這四種情況中的任意一種。

不幸的是,很多單詞里包含hi這兩個連續(xù)的字符,比如him,history,high等等。用hi來查找的話,這里邊的hi也會被找出來。如果要精確地查找hi這個單詞的話,我們應該使用\bhi\b

\b是正則表達式規(guī)定的一個特殊代碼(好吧,某些人叫它元字符,metacharacter),代表著單詞的開頭或結(jié)尾,也就是單詞的分界處。雖然通常英文的單詞是由空格或標點符號或換行來分隔的,但是\b并不匹配這些單詞分隔符中的任何一個,它只匹配一個位置。(如果需要更精確的說法,\b匹配這樣的位置:它的前一個字符和后一個字符不全是(一個是,一個不是或不存在)\w

假如你要找的是hi后面不遠處跟著一個Lucy,你應該用\bhi\b.*\bLucy\b。

這里,.是另一個元字符,匹配除了換行符以外的任意字符。*同樣是元字符,不過它代表的不是字符,也不是位置,而是數(shù)量——它指定*前邊的內(nèi)容可以連續(xù)重復出現(xiàn)任意次以使整個表達式得到匹配。因此,.*連在一起就意味著任意數(shù)量的不包含換行的字符。現(xiàn)在\bhi\b.*\bLucy\b的意思就很明顯了:先是一個單詞hi,然后是任意個任意字符(但不能是換行),最后是Lucy這個單詞。

如果同時使用其它的一些元字符,我們就能構(gòu)造出功能更強大的正則表達式。比如下面這個例子:

0\d\d-\d\d\d\d\d\d\d\d匹配這樣的字符串:以0開頭,然后是兩個數(shù)字,然后是一個連字號“-”,最后是8個數(shù)字(也就是中國的電話號碼。當然,這個例子只能匹配區(qū)號為3位的情形)。

這里的\d是一個新的元字符,匹配任意的數(shù)字(0,或1,或2,或……)-不是元字符,只匹配它本身——連字號。

為了避免那么多煩人的重復,我們也可以這樣寫這個表達式:0\d{2}-\d{8}。 這里\d后面的{2}({8})的意思是前面\d必須連續(xù)重復匹配2次(8次)。

測試正則表達式

如果你不覺得正則表達式很難讀寫的話,要么你是一個天才,要么,你不是地球人。正則表達式的語法很令人頭疼,即使對經(jīng)常使用它的人來說也是如此。由于難于讀寫,容易出錯,所以很有必要創(chuàng)建一種工具來測試正則表達式。

由于在不同的環(huán)境下正則表達式的一些細節(jié)是不相同的,本教程介紹的是Microsoft .Net 2.0下正則表達式的行為,所以,我向你介紹一個.Net下的工具Regex Tester。首先你確保已經(jīng)安裝了.Net Framework 2.0,然后下載Regex Tester。這是個綠色軟件,下載完后打開壓縮包,直接運行RegexTester.exe就可以了。

下面是Regex Tester運行時的截圖:

Regex Tester運行時的截圖

元字符

現(xiàn)在你已經(jīng)知道幾個很有用的元字符了,如\b,.,*,還有\d.當然還有更多的元字符可用,比如\s匹配任意的空白符,包括空格,制表符(Tab),換行符,中文全角空格等\w匹配字母或數(shù)字或下劃線或漢字等。

下面來試試更多的例子:

\ba\w*\b匹配以字母a開頭的單詞——先是某個單詞開始處(\b),然后是字母a,然后是任意數(shù)量的字母或數(shù)字(\w*),最后是單詞結(jié)束處(\b)(好吧,現(xiàn)在我們說說正則表達式里的單詞是什么意思吧:就是幾個連續(xù)的\w。不錯,這與學習英文時要背的成千上萬個同名的東西的確關(guān)系不大)。

\d+匹配1個或更多連續(xù)的數(shù)字。這里的+是和*類似的元字符,不同的是*匹配重復任意次(可能是0次),而+則匹配重復1次或更多次。

\b\w{6}\b 匹配剛好6個字母/數(shù)字的單詞。

表1.常用的元字符
代碼 說明
. 匹配除換行符以外的任意字符
\w 匹配字母或數(shù)字或下劃線或漢字
\s 匹配任意的空白符
\d 匹配數(shù)字
\b 匹配單詞的開始或結(jié)束
^ 匹配字符串的開始
$ 匹配字符串的結(jié)束

元字符^(和數(shù)字6在同一個鍵位上的符號)以及$\b有點類似,都匹配一個位置。^匹配你要用來查找的字符串的開頭,$匹配結(jié)尾。這兩個代碼在驗證輸入的內(nèi)容時非常有用,比如一個網(wǎng)站如果要求你填寫的QQ號必須為5位到12位數(shù)字時,可以使用:^\d{5,12}$。

這里的{5,12}和前面介紹過的{2}是類似的,只不過{2}匹配只能不多不少重復2次,{5,12}則是重復的次數(shù)不能少于5次,不能多于12次,否則都不匹配。

因為使用了^$,所以輸入的整個字符串都要用來和\d{5,12}來匹配,也就是說整個輸入必須是5到12個數(shù)字,因此如果輸入的QQ號能匹配這個正則表達式的話,那就符合要求了。

和忽略大小寫的選項類似,有些正則表達式處理工具還有一個處理多行的選項。如果選中了這個選項,^$的意義就變成了匹配行的開始處和結(jié)束處。

字符轉(zhuǎn)義

如果你想查找元字符本身的話,比如你查找.,或者*,就出現(xiàn)了問題:你沒法指定它們,因為它們會被解釋成其它的意思。這時你就必須使用\來取消這些字符的特殊意義。因此,你應該使用\.\*。當然,要查找\本身,你也得用\\.

例如:www\.unibetter\.com匹配www.unibetter.com,c:\\Windows匹配c:\Windows。

重復

你已經(jīng)看過了前面的*,+,{2},{5,12}這幾個匹配重復的方式了。下面是正則表達式中所有的限定符(指定數(shù)量的代碼,例如*,{5,12}等):

表2.常用的限定符
代碼/語法 說明
* 重復零次或更多次
+ 重復一次或更多次
? 重復零次或一次
{n} 重復n次
{n,} 重復n次或更多次
{n,m} 重復n到m次

下面是一些使用重復的例子:

Windows\d+匹配Windows后面跟1個或更多數(shù)字

13\d{9}匹配13后面跟9個數(shù)字(中國的手機號)

^\w+匹配一行的第一個單詞(或整個字符串的第一個單詞,具體匹配哪個意思得看選項設置)

字符類

要想查找數(shù)字,字母或數(shù)字,空白是很簡單的,因為已經(jīng)有了對應這些字符集合的元字符,但是如果你想匹配沒有預定義元字符的字符集合(比如元音字母a,e,i,o,u),應該怎么辦?

很簡單,你只需要在中括號里列出它們就行了,像[aeiou]就匹配任何一個英文元音字母,[.?!]匹配標點符號(.或?或!)(英文語句通常只以這三個標點結(jié)束)。

我們也可以輕松地指定一個字符范圍,像[0-9]代表的含意與\d就是完全一致的:一位數(shù)字,同理[a-z0-9A-Z_]也完全等同于\w(如果只考慮英文的話)。

下面是一個更復雜的表達式:\(?0\d{2}[) -]?\d{8}。

這個表達式可以匹配幾種格式的電話號碼,像(010)88886666,或022-22334455,或02912345678等。我們對它進行一些分析吧:首先是一個轉(zhuǎn)義字符\(,它能出現(xiàn)0次或1次(?),然后是一個0,后面跟著2個數(shù)字(\d{2}),然后是)-空格中的一個,它出現(xiàn)1次或不出現(xiàn)(?),最后是8個數(shù)字(\d{8})。不幸的是,它也能匹配010)12345678(022-87654321這樣的“不正確”的格式。要解決這個問題,請在本教程的下面查找答案。

反義

有時需要查找不屬于某個能簡單定義的字符類的字符。比如想查找除了數(shù)字以外,其它任意字符都行的情況,這時需要用到反義

表3.常用的反義代碼
代碼/語法 說明
\W 匹配任意不是字母,數(shù)字,下劃線,漢字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非數(shù)字的字符
\B 匹配不是單詞開頭或結(jié)束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou這幾個字母以外的任意字符

例子:\S+匹配不包含空白符的字符串。

<a[^>]+>匹配用尖括號括起來的以a開頭的字符串。

替換

好了,現(xiàn)在終于到了解決3位或4位區(qū)號問題的時間了。正則表達式里的替換指的是有幾種規(guī)則,如果滿足其中任意一種規(guī)則都應該當成匹配,具體方法是用|把不同的規(guī)則分隔開。聽不明白?沒關(guān)系,看例子:

0\d{2}-\d{8}|0\d{3}-\d{7}這個表達式能匹配兩種以連字號分隔的電話號碼:一種是三位區(qū)號,8位本地號(如010-12345678),一種是4位區(qū)號,7位本地號(0376-2233445)。

\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}這個表達式匹配3位區(qū)號的電話號碼,其中區(qū)號可以用小括號括起來,也可以不用,區(qū)號與本地號間可以用連字號或空格間隔,也可以沒有間隔。你可以試試用替換|把這個表達式擴展成也支持4位區(qū)號的。

\d{5}-\d{4}|\d{5}這個表達式用于匹配美國的郵政編碼。美國郵編的規(guī)則是5位數(shù)字,或者用連字號間隔的9位數(shù)字。之所以要給出這個例子是因為它能說明一個問題:使用替換時,順序是很重要的。如果你把它改成\d{5}|\d{5}-\d{4}的話,那么就只會匹配5位的郵編(以及9位郵編的前5位)。原因是匹配替換時,將會從左到右地測試每個分枝條件,如果滿足了某個分枝的話,就不會去管其它的替換條件了。

Windows98|Windows2000|WindosXP這個例子是為了告訴你替換不僅僅能用于兩種規(guī)則,也能用于更多種規(guī)則。

分組

我們已經(jīng)提到了怎么重復單個字符(直接在字符后面加上限定符就行了);但如果想要重復多個字符又該怎么辦?你可以用小括號來指定子表達式(也叫做分組),然后你就可以指定這個子表達式的重復次數(shù)了,你也可以對子表達式進行其它一些操作(后面會有介紹)。

(\d{1,3}\.){3}\d{1,3}是一個簡單的IP地址匹配表達式。要理解這個表達式,請按下列順序分析它:\d{1,3}匹配1到3位的數(shù)字,(\d{1,3}\.){3}匹配三位數(shù)字加上一個英文句號(這個整體也就是這個分組)重復3次,最后再加上一個一到三位的數(shù)字(\d{1,3})。

不幸的是,它也將匹配256.300.888.999這種不可能存在的IP地址(IP地址中每個數(shù)字都不能大于255。題外話,好像反恐24小時第三季的編劇不知道這一點,汗...)。如果能使用算術(shù)比較的話,或許能簡單地解決這個問題,但是正則表達式中并不提供關(guān)于數(shù)學的任何功能,所以只能使用冗長的分組,選擇,字符類來描述一個正確的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)。

理解這個表達式的關(guān)鍵是理解2[0-4]\d|25[0-5]|[01]?\d\d?,這里我就不細說了,你自己應該能分析得出來它的意義。

后向引用

使用小括號指定一個子表達式后,匹配這個子表達式的文本(也就是此分組捕獲的內(nèi)容)可以在表達式或其它程序中作進一步的處理。默認情況下,每個分組會自動擁有一個組號,規(guī)則是:從左向右,以分組的左括號為標志,第一個出現(xiàn)的分組的組號為1,第二個為2,以此類推。

后向引用用于重復搜索前面某個分組匹配的文本。例如,\1代表分組1匹配的文本。難以理解?請看示例:

\b(\w+)\b\s+\1\b可以用來匹配重復的單詞,像go go, kitty kitty。首先是一個單詞,也就是單詞開始處和結(jié)束處之間的多于一個的字母或數(shù)字(\b(\w+)\b),然后是1個或幾個空白符(\s+),最后是前面匹配的那個單詞(\1)。

你也可以自己指定子表達式的組名。要指定一個子表達式的組名,請使用這樣的語法:(?<Word>\w+)(或者把尖括號換成'也行:(?'Word'\w+)),這樣就把\w+的組名指定為Word了。要反向引用這個分組捕獲的內(nèi)容,你可以使用\k<Word>,所以上一個例子也可以寫成這樣:\b(?<Word>\w+)\b\s+\k<Word>\b。

使用小括號的時候,還有很多特定用途的語法。下面列出了最常用的一些:

表4.分組語法
捕獲
(exp) 匹配exp,并捕獲文本到自動命名的組里
(?<name>exp) 匹配exp,并捕獲文本到名稱為name的組里,也可以寫成(?'name'exp)
(?:exp) 匹配exp,不捕獲匹配的文本,也不給此分組分配組號
零寬斷言
(?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
注釋
(?#comment) 這種類型的組不對正則表達式的處理產(chǎn)生任何影響,用于提供注釋讓人閱讀

我們已經(jīng)討論了前兩種語法。第三個(?:exp)不會改變正則表達式的處理方式,只是這樣的組匹配的內(nèi)容不會像前兩種那樣被捕獲到某個組里面

零寬斷言

接下來的四個用于查找在某些內(nèi)容(但并不包括這些內(nèi)容)之前或之后的東西,也就是說它們像\b,^,$那樣用于指定一個位置,這個位置應該滿足一定的條件(斷言),因此它們也被稱為零寬斷言。最好還是拿例子來說明吧:

(?=exp)也叫零寬度正預測先行斷言,它斷言自身出現(xiàn)的位置的后面能匹配表達式exp。比如\b\w+(?=ing\b),匹配以ing結(jié)尾的單詞的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.時,它會匹配singdanc

(?<=exp)也叫零寬度正回顧后發(fā)斷言,它斷言自身出現(xiàn)的位置的前面能匹配表達式exp。比如(?<=\bre)\w+\b會匹配以re開頭的單詞的后半部分(除了re以外的部分),例如在查找reading a book時,它匹配ading。

假如你想要給一個很長的數(shù)字中每三位間加一個逗號(當然是從右邊加起了),你可以這樣查找需要在前面和里面添加逗號的部分:((?<=\d)\d{3})*\b,用它對1234567890進行查找時結(jié)果是234567890。

下面這個例子同時使用了這兩種斷言:(?<=\s)\d+(?=\s)匹配以空白符間隔的數(shù)字(再次強調(diào),不包括這些空白符)

負向零寬斷言

前面我們提到過怎么查找不是某個字符或不在某個字符類里的字符的方法(反義)。但是如果我們只是想要確保某個字符沒有出現(xiàn),但并不想去匹配它時怎么辦?例如,如果我們想查找這樣的單詞--它里面出現(xiàn)了字母q,但是q后面跟的不是字母u,我們可以嘗試這樣:

\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的單詞。但是如果多做測試(或者你思維足夠敏銳,直接就觀察出來了),你會發(fā)現(xiàn),如果q出現(xiàn)在單詞的結(jié)尾的話,像Iraq,Benq,這個表達式就會出錯。這是因為[^u]總要匹配一個字符,所以如果q是單詞的最后一個字符的話,后面的[^u]將會匹配q后面的單詞分隔符(可能是空格,或者是句號或其它的什么),后面的\w*\b將會匹配下一個單詞,于是\b\w*q[^u]\w*\b就能匹配整個Iraq fighting。負向零寬斷言能解決這樣的問題,因為它只匹配一個位置,并不消費任何字符?,F(xiàn)在,我們可以這樣來解決這個問題:\b\w*q(?!u)\w*\b。

零寬度負預測先行斷言(?!exp)斷言此位置的后面不能匹配表達式exp。例如:\d{3}(?!\d)匹配三位數(shù)字,而且這三位數(shù)字的后面不能是數(shù)字;\b((?!abc)\w)+\b匹配不包含連續(xù)字符串a(chǎn)bc的單詞。

同理,我們可以用(?<!exp),零寬度正回顧后發(fā)斷言斷言此位置的前面不能匹配表達式exp(?<![a-z])\d{7}匹配前面不是小寫字母的七位數(shù)字。

一個更復雜的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含屬性的簡單HTML標簽內(nèi)里的內(nèi)容。(<?(\w+)>)指定了這樣的前綴被尖括號括起來的單詞(比如可能是<b>),然后是.*(任意的字符串),最后是一個后綴(?=<\/\1>)。注意后綴里的\/,它用到了前面提過的字符轉(zhuǎn)義;\1則是一個反向引用,引用的正是捕獲的第一組,前面的(\w+)匹配的內(nèi)容,這樣如果前綴實際上是<b>的話,后綴就是</b>了。整個表達式匹配的是<b>和</b>之間的內(nèi)容(再次提醒,不包括前綴和后綴本身)。

注釋

小括號的另一種用途是能過語法(?#comment)來包含注釋。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。

要包含注釋的話,最好是啟用“忽略模式里的空白符”選項,這樣在編寫表達式時能任意的添加空格,Tab,換行,而實際使用時這些都將被忽略。啟用這個選項后,在#后面到這一行結(jié)束的所有文本都將被當成注釋忽略掉。

例如,我們可以前面的一個表達式寫成這樣:

      (?<=    # 斷言要匹配的文本的前綴
<(\w+)> # 查找尖括號括起來的字母或數(shù)字(即HTML/XML標簽)
)       # 前綴結(jié)束
.*      # 匹配任意文本
(?=     # 斷言要匹配的文本的后綴
<\/\1>  # 查找尖括號括起來的內(nèi)容:前面是一個"/",后面是先前捕獲的標簽
)       # 后綴結(jié)束

貪婪與懶惰

當正則表達式中包含能接受重復的限定符時,通常的行為是(在使整個表達式能得到匹配的前提下)匹配盡可能多的字符??紤]這個表達式:a.*b,它將會匹配最長的以a開始,以b結(jié)束的字符串。如果用它來搜索aabab的話,它會匹配整個字符串aabab。這被稱為貪婪匹配。

有時,我們更需要懶惰匹配,也就是匹配盡可能少的字符。前面給出的限定符都可以被轉(zhuǎn)化為懶惰匹配模式,只要在它后面加上一個問號?。這樣.*?就意味著匹配任意數(shù)量的重復,但是在能使整個匹配成功的前提下使用最少的重復?,F(xiàn)在看看懶惰版的例子吧:

a.*?b匹配最短的,以a開始,以b結(jié)束的字符串。如果把它應用于aabab的話,它會匹配aabab(為什么第一個匹配是aab而不是ab?簡單地說,因為正則表達式有另一條規(guī)則,比懶惰/貪婪規(guī)則的優(yōu)先級更高:最先開始的匹配擁有最高的優(yōu)先權(quán)——The Match That Begins Earliest Wins)。

表5.懶惰限定符
*? 重復任意次,但盡可能少重復
+? 重復1次或更多次,但盡可能少重復
?? 重復0次或1次,但盡可能少重復
{n,m}? 重復n到m次,但盡可能少重復
{n,}? 重復n次以上,但盡可能少重復

處理選項

上面介紹了幾個選項如忽略大小寫,處理多行等,這些選項能用來改變處理正則表達式的方式。下面是.Net中常用的正則表達式選項:

表6.常用的處理選項
名稱 說明
IgnoreCase(忽略大小寫) 匹配時不區(qū)分大小寫。
Multiline(多行模式) 更改^$的含義,使它們分別在任意一行的行首和行尾匹配,而不僅僅在整個字符串的開頭和結(jié)尾匹配。(在此模式下,$的精確含意是:匹配\n之前的位置以及字符串結(jié)束前的位置.)
Singleline(單行模式) 更改.的含義,使它與每一個字符匹配(包括換行符\n)。
IgnorePatternWhitespace(忽略空白) 忽略表達式中的非轉(zhuǎn)義空白并啟用由#標記的注釋。
RightToLeft(從右向左查找) 匹配從右向左而不是從左向右進行。
ExplicitCapture(顯式捕獲) 僅捕獲已被顯式命名的組。
ECMAScript(JavaScript兼容模式) 使表達式的行為與它在JavaScript里的行為一致。

一個經(jīng)常被問到的問題是:是不是只能同時使用多行模式和單行模式中的一種?答案是:不是。這兩個選項之間沒有任何關(guān)系,除了它們的名字比較相似(以至于讓人感到疑惑)以外。

平衡組/遞歸匹配

注意:這里介紹的平衡組語法是由.Net Framework支持的;其它語言/庫不一定支持這種功能,或者支持此功能但需要使用不同的語法。

有時我們需要匹配像( 100 * ( 50 + 15 ) )這樣的可嵌套的層次性結(jié)構(gòu),這時簡單地使用\(.+\)則只會匹配到最左邊的左括號和最右邊的右括號之間的內(nèi)容(這里我們討論的是貪婪模式,懶惰模式也有下面的問題)。假如原來的字符串里的左括號和右括號出現(xiàn)的次數(shù)不相等,比如( 5 / ( 3 + 2 ) ) ),那我們的匹配結(jié)果里兩者的個數(shù)也不會相等。有沒有辦法在這樣的字符串里匹配到最長的,配對的括號之間的內(nèi)容呢?

為了避免(\(把你的大腦徹底搞糊涂,我們還是用尖括號代替圓括號吧。現(xiàn)在我們的問題變成了如何把xx <aa <bbb> <bbb> aa> yy這樣的字符串里,最長的配對的尖括號內(nèi)的內(nèi)容捕獲出來?

這里需要用到以下的語法構(gòu)造:

  • (?'group') 把捕獲的內(nèi)容命名為group,并壓入堆棧
  • (?'-group') 從堆棧上彈出最后壓入堆棧的名為group的捕獲內(nèi)容,如果堆棧本來為空,則本分組的匹配失敗
  • (?(group)yes|no) 如果堆棧上存在以名為group的捕獲內(nèi)容的話,繼續(xù)匹配yes部分的表達式,否則繼續(xù)匹配no部分
  • (?!) 零寬負向先行斷言,由于沒有后綴表達式,試圖匹配總是失敗

如果你不是一個程序員(或者你是一個對堆棧的概念不熟的程序員),你就這樣理解上面的三種語法吧:第一個就是在黑板上寫一個 "group",第二個就是從黑板上擦掉一個"group",第三個就是看黑板上寫的還有沒有"group",如果有就繼續(xù)匹配yes部分,否則就匹配 no部分。

我們需要做的是每碰到了左括號,就在黑板上寫一個"group",每碰到一個右括號,就擦掉一個,到了最后就看看黑板上還有沒有--如果有那就證明左括號比右括號多,那匹配就應該失敗。

<                         #最外層的左括號
[^<>]*                #最外層的左括號后面的不是括號的內(nèi)容
(
(
(?'Open'<)    #碰到了左括號,在黑板上寫一個"Open"
[^<>]*       #匹配左括號后面的不是括號的內(nèi)容
)+
(
(?'-Open'>)   #碰到了右括號,擦掉一個"Open"
[^<>]*        #匹配右括號后面不是括號的內(nèi)容
)+
)*
(?(Open)(?!))         #在遇到最外層的右括號前面,判斷黑板上還有沒有沒擦掉的"Open";如果還有,則匹配失敗
>                         #最外層的右括號

平衡組的一個最常見的應用就是匹配HTML,下面這個例子可以匹配嵌套的<div>標簽<div[^>]*>[^<>]*(((?'Open'<div[^>]*>)[^<>]*)+((?'-Open'</div>)[^<>]*)+)*(?(Open)(?!))</div>.

還有些什么東西沒提到

我已經(jīng)描述了構(gòu)造正則表達式的大量元素,還有一些我沒有提到的東西。下面是未提到的元素的列表,包含語法和簡單的說明。你可以在網(wǎng)上找到更詳細的參考資料來學習它們--當你需要用到它們的時候。如果你安裝了MSDN Library,你也可以在里面找到關(guān)于.net下正則表達式詳細的文檔。

表7.尚未詳細討論的語法
\a 報警字符(打印它的效果是電腦嘀一聲)
\b 通常是單詞分界位置,但如果在字符類里使用代表退格
\t 制表符,Tab
\r 回車
\v 豎向制表符
\f 換頁符
\n 換行符
\e Escape
\0nn ASCII代碼中八進制代碼為nn的字符
\xnn ASCII代碼中十六進制代碼為nn的字符
\unnnn Unicode代碼中十六進制代碼為nnnn的字符
\cN ASCII控制字符。比如\cC代表Ctrl+C
\A 字符串開頭(類似^,但不受處理多行選項的影響)
\Z 字符串結(jié)尾或行尾(不受處理多行選項的影響)
\z 字符串結(jié)尾(類似$,但不受處理多行選項的影響)
\G 當前搜索的開頭
\p{name} Unicode中命名為name的字符類,例如\p{IsGreek}
(?>exp) 貪婪子表達式
(?<x>-<y>exp) 平衡組
(?im-nsx:exp) 在子表達式exp中改變處理選項
(?im-nsx) 為表達式后面的部分改變處理選項
(?(exp)yes|no) 把exp當作零寬正向先行斷言,如果在這個位置能匹配,使用yes作為此組的表達式;否則使用no
(?(exp)yes) 同上,只是使用空表達式作為no
(?(name)yes|no) 如果命名為name的組捕獲到了內(nèi)容,使用yes作為表達式;否則使用no
(?(name)yes) 同上,只是使用空表達式作為no

聯(lián)系作者

好吧,我承認,我騙了你,讀到這里你肯定花了不止30分鐘.相信我,這是我的錯,而不是因為你太笨.我之所以說"30分鐘",是為了讓你有信心,有耐心繼續(xù)下去.既然你看到了這里,那證明我的陰謀成功了.上這種當?shù)淖涛哆€不錯吧?

要投訴我,或者覺得我其實可以做得更好,或者有任何其它問題,歡迎來我的博客進行討論.

一些我認為你可能已經(jīng)知道的術(shù)語的參考

字符
程序處理文字時最基本的單位,可能是字母,數(shù)字,標點符號,空格,換行符,漢字等等。
字符串
0個或更多個字符的序列。
文本
文字,字符串。
匹配
符合規(guī)則,檢驗是否符合規(guī)則,符合規(guī)則的部分。
斷言
聲明一個應該為真的事實。只有當斷言為真時才會對正則表達式繼續(xù)進行匹配。

網(wǎng)上的資源及本文參考文獻

 



oyjpart 2008-03-20 14:15 發(fā)表評論
]]>
JAVA常用設計模式2 MVC [ Model-View-Controller ]http://www.shnenglu.com/sicheng/archive/2008/01/16/41274.htmloyjpartoyjpartWed, 16 Jan 2008 06:01:00 GMThttp://www.shnenglu.com/sicheng/archive/2008/01/16/41274.htmlhttp://www.shnenglu.com/sicheng/comments/41274.htmlhttp://www.shnenglu.com/sicheng/archive/2008/01/16/41274.html#Feedback4http://www.shnenglu.com/sicheng/comments/commentRss/41274.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/41274.html閱讀全文

oyjpart 2008-01-16 14:01 發(fā)表評論
]]>
JAVA常用設計模式1 Adapterhttp://www.shnenglu.com/sicheng/archive/2008/01/15/41233.htmloyjpartoyjpartTue, 15 Jan 2008 14:45:00 GMThttp://www.shnenglu.com/sicheng/archive/2008/01/15/41233.htmlhttp://www.shnenglu.com/sicheng/comments/41233.htmlhttp://www.shnenglu.com/sicheng/archive/2008/01/15/41233.html#Feedback0http://www.shnenglu.com/sicheng/comments/commentRss/41233.htmlhttp://www.shnenglu.com/sicheng/services/trackbacks/41233.html一個適配器允許不帶有兼容接口的組件之間進行通信。
適配器模式是使用面向?qū)ο笤O計概念的優(yōu)秀示例,其中一個原因就是因為他非常簡單。
同時,它也是有關(guān)三個重要設計原則的優(yōu)秀示例:委托,繼承和抽象。

概念UML類圖

實例類圖


 1// Triks.java
 2
 3package oyjpart.designpattern;
 4
 5public interface Tricks {
 6    public void walk();
 7    public void run();
 8    public void fetch();
 9}

10


 

 1// DogShow.java
 2
 3package oyjpart.designpattern;
 4
 5public class DogShow {
 6    public void compete(Tricks target) {
 7        target.run();
 8        target.walk();
 9        target.fetch();
10    }

11    
12    public static void main(String[] args) {
13        OldDog oldDog = new OldDog("Tim");
14        OldDogTricksAdapter oldDogTricksAdapter = new OldDogTricksAdapter(oldDog); 
15        new DogShow().compete(oldDogTricksAdapter);
16    }

17}

18
19

 

 

 1// OldDog.java
 2
 3package oyjpart.designpattern;
 4
 5public class OldDog {
 6    String name;
 7    
 8    public OldDog(String name) {
 9        this.name = name;
10    }

11    
12    public void walk() {
13        System.out.println("Old Dog " + name + " is walking");
14    }

15    
16    public void sleep() {
17        System.out.println("Old Dog " + name + " is sleeping");
18    }

19    
20}

21
22

 

 

 1
 2// OldDogTricksAdapter.java
 3
 4package oyjpart.designpattern;
 5
 6public class OldDogTricksAdapter implements Tricks {
 7    private OldDog adaptee;
 8    
 9    public OldDogTricksAdapter(OldDog oldDog) {
10        this.adaptee = oldDog;
11    }

12
13    public void walk() {
14        System.out.println("This dog can walk");
15    }

16
17    public void fetch() {
18        System.out.println("This dog cannot fetch");
19    }

20    
21    public void run() {
22        System.out.println("This dog cannot run");
23    }

24}

25
26


oyjpart 2008-01-15 22:45 發(fā)表評論
]]>
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲五月婷婷| 精品999在线观看| 一区二区三区不卡视频在线观看| 欧美韩国日本综合| 免费观看日韩av| 在线视频你懂得一区二区三区| 9国产精品视频| 国产精品一区2区| 久久资源av| 欧美精品综合| 欧美一区二区三区另类| 久久精品一二三区| 亚洲精品少妇| 亚洲影音一区| 亚洲成在人线av| 宅男66日本亚洲欧美视频| 国产曰批免费观看久久久| 亚洲国产日韩精品| 国产精品亚洲综合一区在线观看 | 一区二区三区鲁丝不卡| 亚洲一区二区日本| 亚洲人成绝费网站色www| 亚洲伊人观看| 日韩手机在线导航| 久久se精品一区精品二区| 亚洲毛片在线看| 欧美在线亚洲一区| 亚洲视频第一页| 久久综合久久美利坚合众国| 亚洲性感美女99在线| 久久久久久一区| 午夜精品久久久99热福利| 你懂的亚洲视频| 久久国产天堂福利天堂| 欧美精品午夜视频| 蜜臀久久99精品久久久画质超高清 | 亚洲无亚洲人成网站77777| 久久裸体视频| 久久精品一级爱片| 国产精品欧美日韩| 一本色道久久综合亚洲精品不| 在线看片日韩| 久久精品动漫| aa成人免费视频| 国模精品一区二区三区色天香| 亚洲美女精品一区| 亚洲激情专区| 久久精品视频在线播放| 欧美诱惑福利视频| 国产精品一区二区在线观看| 日韩视频永久免费| 亚洲免费观看在线视频| 久热成人在线视频| 欧美va天堂| 亚洲高清激情| 久久在线免费观看| 你懂的国产精品永久在线| 国产主播喷水一区二区| 午夜免费日韩视频| 久久激情五月丁香伊人| 国产精品久久夜| 亚洲一区二区在线免费观看视频| 正在播放欧美视频| 欧美日韩国产综合一区二区| 最新日韩精品| 一区二区三区四区在线| 欧美视频一区二区在线观看 | 亚洲婷婷综合久久一本伊一区| 99国产精品国产精品毛片| 欧美精品三区| 一区二区三区国产精品| 亚洲欧美日韩国产综合精品二区| 国产精品jvid在线观看蜜臀| 一区二区三区欧美成人| 午夜日韩在线| 国产一区激情| 母乳一区在线观看| 999在线观看精品免费不卡网站| 日韩午夜黄色| 国产精品爱啪在线线免费观看| 午夜精品久久久久| 美日韩精品视频| 一本色道久久99精品综合| 国产精品成人v| 欧美一区免费| 亚洲电影成人| 午夜老司机精品| 亚洲福利专区| 欧美三级日本三级少妇99| 午夜日韩福利| 亚洲国产另类精品专区 | 久久久精品国产免大香伊 | 亚洲欧美日韩成人高清在线一区| 国产精品一区二区久久久| 久久精品视频播放| 亚洲免费av片| 久热精品视频在线| 亚洲一级免费视频| 一色屋精品视频在线看| 欧美日韩八区| 久久裸体艺术| 午夜精品久久久久久久99樱桃| 欧美国产视频在线观看| 亚洲一区影音先锋| 91久久精品国产91性色| 国产精品入口日韩视频大尺度| 久久久久免费视频| 欧美午夜激情在线| 欧美日韩卡一卡二| 午夜国产欧美理论在线播放| 亚洲狠狠丁香婷婷综合久久久| 香蕉成人伊视频在线观看| 亚洲国产精彩中文乱码av在线播放| 国产精品久久二区二区| 欧美暴力喷水在线| 久久精品视频免费| 欧美一级视频精品观看| 在线中文字幕日韩| 99re66热这里只有精品3直播 | 亚洲国产精品精华液2区45| 久久精品国产一区二区三区| 一区二区三区精品视频在线观看| 一区二区三区我不卡| 国产伦一区二区三区色一情| 欧美日韩一区在线播放| 男女视频一区二区| 欧美h视频在线| 久久这里有精品15一区二区三区 | 在线一区亚洲| 中文欧美日韩| 中国成人亚色综合网站| 亚洲精品一区二区三区婷婷月| 亚洲电影免费观看高清完整版在线观看 | 国产视频久久久久| 国产精品丝袜久久久久久app| 欧美久久电影| 欧美欧美天天天天操| 欧美激情一区| 欧美精品尤物在线| 欧美日韩少妇| 国产精品护士白丝一区av| 欧美日韩一区二区在线视频| 欧美视频不卡中文| 国产精品家庭影院| 国产女主播视频一区二区| 国产精品午夜春色av| 国产九九精品| 一区二区视频免费完整版观看| 国产日韩欧美高清| 国产自产精品| 亚洲人成网站999久久久综合| 亚洲精品国产精品国自产观看浪潮| 亚洲茄子视频| 亚洲一区二区三| 久久成年人视频| 免费永久网站黄欧美| 欧美激情精品久久久久久变态| 最新亚洲一区| 一区二区三区精品视频在线观看| 亚洲免费在线播放| 久久亚洲精品视频| 欧美日韩美女| 国产亚洲欧美日韩在线一区| 亚洲电影免费观看高清完整版| 日韩视频精品在线| 欧美一区二区精品| 欧美电影在线播放| 亚洲丝袜av一区| 久久久久国产免费免费| 欧美激情亚洲激情| 国产日韩欧美高清免费| 亚洲精品乱码久久久久久日本蜜臀 | 国产精品免费网站| 在线播放亚洲| 欧美呦呦网站| 亚洲女爱视频在线| 麻豆成人在线观看| 在线中文字幕一区| 久热re这里精品视频在线6| 欧美日韩在线视频一区二区| 国内一区二区三区在线视频| 日韩视频在线永久播放| 久久精品欧美日韩精品| 亚洲全黄一级网站| 久久久久欧美| 国产精品网站一区| 99精品国产在热久久| 久久久一本精品99久久精品66| 日韩午夜免费视频| 久久久伊人欧美| 国产欧美日韩视频在线观看| 亚洲美女淫视频| 久久人人爽人人爽爽久久| 亚洲精品综合| 嫩草影视亚洲| 伊人久久噜噜噜躁狠狠躁 | 国产在线不卡| 欧美在线播放视频| 在线视频精品一| 欧美噜噜久久久xxx|