#牆裂推薦Boost regex# C,C++11,Boost三種regex庫效能比較
在最近的一個專案中,發現之前的正則匹配模組對於長字串匹配效能損失比較厲害,因此對長字串下的各種正則匹配進行了略微研究並附有例項。本文參考了部落格http://www.cnblogs.com/pmars/archive/2012/10/24/2736831.html(下文稱文1),這篇文章也是對三種regex庫進行了比較,但有些地方我還有一些自己的見解,特此羅列如下,感謝這篇文章的作者。
1.C regex庫
由於專案中一直用的都是C regex庫,所以首先對C regex進行了研究。對於C regex的各種介面及引數可以參照文1,如果要看起來更專業的話,可以參考這一篇,http://pubs.opengroup.org/onlinepubs/000095399/functions/regcomp.html(下文稱文2)。
對於C regex的效能測試是這樣的,首先按照慣例我們將C regex提供的各個呼叫封裝成一個呼叫,其傳入引數為模式字串和待匹配字串,如果二者匹配,則返回1,否則返回0.
這個呼叫中的流程像文1中所述,首先呼叫regcomp對模式字串進行編譯,編譯時可以指定各種引數,如擴充套件/基礎正則語法,是否忽略大小寫,是否儲存結果以及對換行符的設定(此引數請參照文2)。之後,呼叫regexec將待匹配的目標字串與編譯好的正則表示式進行匹配,後邊的引數可以設定匹配到的字串儲存位置,以及將模式字串中的行首符(^)和行尾符($)是否當作一般字元處理,詳情可以參考文1,文2。最後,呼叫regfree釋放記憶體。
測試程式設定好模式字串以及待匹配的目標字串後,對此函式進行10000次呼叫,取開始和結束的時間之差作為效能評測依據,下文的C++11和Boost也是使用這種方法,簡單有效嘛。int match(const char* pattern, const char* target) { regex_t oRegex; int nErrCode = 0; char szErrMsg[1024] = {0}; size_t unErrMsgLen = 0; if ((nErrCode = regcomp(&oRegex, pattern, 0)) == 0) { if ((nErrCode = regexec(&oRegex, target, 0, NULL, 0)) == 0) { regfree(&oRegex); return 1; } } unErrMsgLen = regerror(nErrCode, &oRegex, szErrMsg, sizeof(szErrMsg)); unErrMsgLen = unErrMsgLen < sizeof(szErrMsg) ? unErrMsgLen : sizeof(szErrMsg) - 1; szErrMsg[unErrMsgLen] = '\0'; regfree(&oRegex); return 0; }
考慮到是重複呼叫10000次,我們自然會想,那我豈不是在這個match裡面把同一個模式字串不停的進行編譯,匹配,釋放記憶體,我們要測試的只是匹配效能啊,編譯一次不就好了嗎,好,我們可以改進一下,封裝成下面這個呼叫,看到了吧,我們傳進來編譯好的正則表示式regex_t指標不就好了嗎,然後迴圈10000次之後,再將其記憶體釋放掉不是很完美?
為了同時測試上述兩種方法的效能,我們使用巨集定義來編譯出不同版本的可執行程式,設計的主體如下,在其中,我們故意使用長目標字串(1000)來進行測試,以達到目的,當指定 #define NOT_PRE_COMP時,使用第一種方法,當指定#define PRE_COMP時使用第二種方法,二者不能同時存在,也不能同時不存在。使用#define LOOP_COUNT (XXX)可以指定迴圈次數。當然這些變了之後,得重新編譯。int match_pre_comp(regex_t * pattern, const char* target) { int nErrCode = 0; char szErrMsg[1024] = {0}; size_t unErrMsgLen = 0; if ((nErrCode = regexec(pattern, target, 0, NULL, 0)) == 0) { return 1; } unErrMsgLen = regerror(nErrCode, pattern, szErrMsg, sizeof(szErrMsg)); unErrMsgLen = unErrMsgLen < sizeof(szErrMsg) ? unErrMsgLen : sizeof(szErrMsg) - 1; szErrMsg[unErrMsgLen] = '\0'; return 0; }
/*
* Program:
* This program for test c regex performance
* Platform
* Ubuntu14.04 gcc-4.8.2
* History:
* weizheng 2014.11.05 1.0
*/
#include <sys/time.h>
#include <stdio.h>
#include "regex.h"
#define LOOP_COUNT ( 10000 )
#define PRE_COMP
/*
* you must choice PRE_COMP or NOT_PRE_COMP to decide pre_complie the regex expression or not
*/
#if defined(PRE_COMP) && defined(NOT_PRE_COMP)
#error can not define PRE_COMP and NOT_PRE_COMP at the same time
#elif !defined(NOT_PRE_COMP) && !defined(PRE_COMP)
#error please define PRE_COMP or NOT_PRE_COMP
#endif
/************************************ main ************************************/
int main(void)
{
char pattern[] = "ywfuncFlag.*rzgw/rzdk_rzzl.jsp";
char target[] = "<xml><param0>{\"bod\":{\"autoLoad\":false,\"keys\":[\"YWFUNC\"],\"state\":4,\"supportDynamic\":true},\"compId\":\"\",\"flag\":0,\"realUrl\":\"\",\"remoteIp\":\"\",\"ywType\":\"2\",\"ywfunc\":\"39\",\"ywfuncFlag\":\"/FMISWeb/faces/financing/dhkmanage/rzgw/rzdk_rzzl.jsp?^IRZDKServiceBC/RZFS%3d00000007%26amp;^IRZDKServiceBC/BEGINDATE%3d2014-10-01%26amp;^IRZDKServiceBC/ENDDATE%3d2014-10-31%26amp;^IRZDKServiceBC/ISTJTH%3d8\",\"ywfuncName\":\"\",\"ywfuncPageId\":\"DYHKRZZL\",\"RZWAIT_ZLLIST\":{\"bod\":{\"autoLoad\":false,\"keys\":[\"GID\"],\"state\":4,\"supportDynamic\":true},\"listItemClass\":\"com.soft.grm.investfinancing.service.masterdata.model.bo.RzptZltsOldModel\",\"listType\":0,\"masterProperty\":[],\"notUpdateField\":[],\"tableName\":\"\",\"useColumnNameToXml\":true,\"metaList\":{\"GID\":{\"columnCaption\":\"GID\",\"columnIndex\":0,\"columnName\":\"GID\",\"length\":0,\"mapName\":\"GID\",\"nullAble\":true,\"scale\":-127,\"sqlType\":2},\"ZLTS\":{\"columnCaption\":\"ZLTS\",\"columnIndex\":2,\"columnName\":\"ZLTS\",\"length\":4,\"mapName\":\"ZLTS\",\"nullAble\":true,\"scale\":0,\"sqlType\":";
#ifdef PRE_COMP
regex_t oRegex;
if (regcomp(&oRegex, pattern, 0))
printf("regex complie error\n");
#endif
/*
* record the start time
*/
struct timeval tv_start, tv_end;
gettimeofday(&tv_start, NULL);
/*
* matching
*/
int count = 0;
for(int i = 0; i < LOOP_COUNT; i++)
{
#ifdef PRE_COMP
if(match_pre_comp(&oRegex, target))
#endif
#ifdef NOT_PRE_COMP
if(match(pattern, target))
#endif
{
count++;
}
}
/*
* record the end time
*/
gettimeofday(&tv_end, NULL);
unsigned long time_used = (tv_end.tv_sec * 1000000 + tv_end.tv_usec - (tv_start.tv_sec * 1000000 + tv_start.tv_usec))/1000;
#ifdef PRE_COMP
regfree(&oRegex);
#endif
printf("used: %lu ms\n", time_used);
printf("matched %d times\n", count);
return 0;
}
編譯執行兩種版本,結果如下:
不對模式字串進行預編譯,即就是每次都在迴圈內進行重新編譯:
[email protected]:~/test$ ./c_regex_main
used: 418 ms
matched 10000 times
在迴圈之前對模式字串進行預先編譯,之後傳入編譯好的正則表示式用來迴圈,不進行重新編譯:
[email protected]:~/test$ ./c_regex_main
used: 404 ms
matched 10000 times
從結果中看出C regex的匹配耗時,這個時候也不好說到底是快還是慢,看跟誰比了,如果你想知道,請繼續往下看。不過就C regex來說,不預編譯和預編譯兩種方法差異並不大,應該對於這種長字串,編譯所佔時間遠遠小於匹配時間,所以編譯一次和多次影響不會很大。
2. C++11 regex
我想說,我寫這篇文章一個主要目的就是來吐槽C++ 11 regex,準確的說,應該是吐槽g++ 4.8的!C++對於正則匹配提供了兩個呼叫:regex_match,regex_search,如果整個輸入序列與表示式匹配,則regex_match函式返回true;如果輸入序列中有一個子串與表示式匹配,則regex_search函式返回true(參考C++ primer(第五版,中文版,P646))。對於regex_match還好說,對於regex_search,說起來,我真是一萬隻草泥馬呼嘯了......
這個regex_search麼,你給模式字串設定且僅設定好一個單詞,比如上文待匹配字串中的任意一個子串,"xml"吧,這很簡單了吧,一看都應該匹配上啊,但這尼瑪就是返回false,你有啥話說,你就是把待匹配目標字串設定成“xml”,它也是返回false。臥槽,我是不是開啟的方式不對,到底問題在哪裡!
於是,我把cplusplus上的官方示例拉下來執行,我想這應該沒問題吧,但我去....regex error(模式字串編譯錯誤),我去....啥情況麼,官方都不對?好吧,我只好把聖經C++ primer拿出來,我相信聖經不會錯的,臥槽,結果真的不出所料,仍舊的regex error。我去...我三觀都毀了,草泥馬到底長的是個什麼樣子......
不過,沒事麼,咱還可以google麼,雖然被封,但咱還是能翻牆的麼,呵呵,搜尋regex_search always return false,說到底還是google管用啊,我搜到了stackoverflow上類似的問題:
http://stackoverflow.com/questions/20027305/strange-results-when-using-c11-regexp-with-gcc-4-8-2-but-works-with-boost-reg
http://stackoverflow.com/questions/12279869/using-regex-search-from-the-c-regex-library
http://stackoverflow.com/questions/11628047/difference-between-regex-match-and-regex-search?lq=1
終於找到了答案,對,你猜對了,C++11 too new,g++ 4.8還沒有完全支援。有人說VS2010,VS2012已經支援了,好吧,換上我強大的雙系統,咱windows下解決問題麼,呵呵。
來,把上面的C regex程式碼修改修改換成使用C++11 regex的,如下,注意一點,在windows下獲取時間就不能用gettimeofday了,得換成相應windows呼叫。同樣,我們通過常量LOOP_COUNT來設定迴圈次數,C++使用模式字串構造regex_t物件就相當於C regex中的編譯正則表示式字串,所以這個就不用擔心多次重複編譯了。
/*
* Program:
* This program for test c++11 regex performance
* Platform
* windows8.1 VS2012
* History:
* weizheng 2014.11.06 1.0
*/
#include <regex>
#include <windows.h>
#include <stdio.h>
const int LOOP_COUNT = 10000;
/************************************ main ************************************/
int main()
{
std::regex pattern("ywfuncFlag.*rzgw/rzdk_rzzl.jsp");
std::string target = "<xml><param0>{\"bod\":{\"autoLoad\":false,\"keys\":[\"YWFUNC\"],\"state\":4,\"supportDynamic\":true},\"compId\":\"\",\"flag\":0,\"realUrl\":\"\",\"remoteIp\":\"\",\"ywType\":\"2\",\"ywfunc\":\"39\",\"ywfuncFlag\":\"/FMISWeb/faces/financing/dhkmanage/rzgw/rzdk_rzzl.jsp?^IRZDKServiceBC/RZFS%3d00000007%26amp;^IRZDKServiceBC/BEGINDATE%3d2014-10-01%26amp;^IRZDKServiceBC/ENDDATE%3d2014-10-31%26amp;^IRZDKServiceBC/ISTJTH%3d8\",\"ywfuncName\":\"\",\"ywfuncPageId\":\"DYHKRZZL\",\"RZWAIT_ZLLIST\":{\"bod\":{\"autoLoad\":false,\"keys\":[\"GID\"],\"state\":4,\"supportDynamic\":true},\"listItemClass\":\"com.soft.grm.investfinancing.service.masterdata.model.bo.RzptZltsOldModel\",\"listType\":0,\"masterProperty\":[],\"notUpdateField\":[],\"tableName\":\"\",\"useColumnNameToXml\":true,\"metaList\":{\"GID\":{\"columnCaption\":\"GID\",\"columnIndex\":0,\"columnName\":\"GID\",\"length\":0,\"mapName\":\"GID\",\"nullAble\":true,\"scale\":-127,\"sqlType\":2},\"ZLTS\":{\"columnCaption\":\"ZLTS\",\"columnIndex\":2,\"columnName\":\"ZLTS\",\"length\":4,\"mapName\":\"ZLTS\",\"nullAble\":true,\"scale\":0,\"sqlType\":";
/*
* record the start time
*/
LARGE_INTEGER lFreq,lSatrt,lEnd;
QueryPerformanceFrequency(&lFreq);
QueryPerformanceCounter(&lSatrt);
int count = 0;
for(int i = 0; i < LOOP_COUNT; i++)
{
if(std::regex_search(target, pattern))
{
count++;
}
}
/*
* record the end time
*/
QueryPerformanceCounter(&lEnd);
float time_used = (float)(lEnd.QuadPart - lSatrt.QuadPart)*1000/lFreq.QuadPart;
printf("used: %.2f ms\n", time_used);
printf("matched %d times\n", count);
return 0;
}
結果如下,這尼瑪簡直找不到更慢的了,不知道烏龜看到會不會後悔沒跟它比賽。你可能覺得C真特麼快,如果是這樣,你還需要繼續往下看:
used: 13754.17 ms
matched 10000 times
請按任意鍵繼續. . .
3. Boost regex
好的,下面就是我牆裂推薦的Boost regex了。Boost是C++標準庫的後備庫,有”準標準庫“之稱,頗負盛名。我當年大二寫了個自認為還不錯的系統,後來一個學長告訴我他用boost 10行程式碼就能完成...我當時就震精了。參考了Boost regex的基本介紹和一些樣例之後,發現這個設計完全跟C++11一樣啊,只要把regex呼叫的名稱空間從std換成boost似乎就好了,當然前提是你已經安裝好了boost庫,編譯時加上鍊接選項-lboost_regex。
ok,當然,測試程式碼也是簡單有效,不過看到結果時,我又一次被震精了。
/*
* Program:
* This program for test boost regex performance
* Platform
* Ubuntu14.04 g++-4.8.2
* History:
* weizheng 2014.11.06 1.0
*/
#include <boost/regex.hpp>
#include <sys/time.h>
#include <cstdio>
const int LOOP_COUNT = 10000;
/************************************ main ************************************/
int main()
{
boost::regex pattern("ywfuncFlag.*rzgw/rzdk_rzzl.jsp");
std::string target = "<xml><param0>{\"bod\":{\"autoLoad\":false,\"keys\":[\"YWFUNC\"],\"state\":4,\"supportDynamic\":true},\"compId\":\"\",\"flag\":0,\"realUrl\":\"\",\"remoteIp\":\"\",\"ywType\":\"2\",\"ywfunc\":\"39\",\"ywfuncFlag\":\"/FMISWeb/faces/financing/dhkmanage/rzgw/rzdk_rzzl.jsp?^IRZDKServiceBC/RZFS%3d00000007%26amp;^IRZDKServiceBC/BEGINDATE%3d2014-10-01%26amp;^IRZDKServiceBC/ENDDATE%3d2014-10-31%26amp;^IRZDKServiceBC/ISTJTH%3d8\",\"ywfuncName\":\"\",\"ywfuncPageId\":\"DYHKRZZL\",\"RZWAIT_ZLLIST\":{\"bod\":{\"autoLoad\":false,\"keys\":[\"GID\"],\"state\":4,\"supportDynamic\":true},\"listItemClass\":\"com.soft.grm.investfinancing.service.masterdata.model.bo.RzptZltsOldModel\",\"listType\":0,\"masterProperty\":[],\"notUpdateField\":[],\"tableName\":\"\",\"useColumnNameToXml\":true,\"metaList\":{\"GID\":{\"columnCaption\":\"GID\",\"columnIndex\":0,\"columnName\":\"GID\",\"length\":0,\"mapName\":\"GID\",\"nullAble\":true,\"scale\":-127,\"sqlType\":2},\"ZLTS\":{\"columnCaption\":\"ZLTS\",\"columnIndex\":2,\"columnName\":\"ZLTS\",\"length\":4,\"mapName\":\"ZLTS\",\"nullAble\":true,\"scale\":0,\"sqlType\":";
/*
* record the start time
*/
struct timeval tv_start, tv_end;
gettimeofday(&tv_start, NULL);
int count = 0;
for(int i = 0; i < LOOP_COUNT; i++)
{
if(boost::regex_search(target, pattern))
{
count++;
}
}
/*
* record the end time
*/
gettimeofday(&tv_end, NULL);
unsigned long time_used = (tv_end.tv_sec * 1000000 + tv_end.tv_usec - (tv_start.tv_sec * 1000000 + tv_start.tv_usec))/1000;
printf("used: %lu ms\n", time_used);
printf("matched %d times\n", count);
return 0;
}
結果如下:
[email protected]:~/test$ ./boost_regex_main
used: 11 ms
matched 10000 times
這結果,竟然比C快了40倍,而且這都是在模式字串可以匹配目標字串的前提下的,如果測試不匹配的情況,Boost竟然比C快了4000倍,我不敢相信這個結果,在此貼上測試用的模式字元和目標字元,看有沒有人跟我一樣,或者有別的原因,例如跟正則表示式或者目標字串本身結構有關,測試字串如下:
boost::regex pattern(".*CSZ.*XTCS.*CSMC.*NEWBB");
std::string target ="<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><SOAP-ENV:Envelope xmlns:SOAPSDK1=\"http://www.w3.org/2001/XMLSchema\" xmlns:SOAPSDK2=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:SOAPSDK3=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Body SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><SOAPSDK4:GetSystemDate xmlns:SOAPSDK4=\"http://svr.blf.common.fmis.ygsoft.com\"/></SOAP-ENV:Body></SOAP-ENV:Envelope>";
所以,從以上對比結果中看出boost regex的表現似乎極為突出,但文1中最後的比較結果是C regex更快,我與其測試結果不同,希望看到更多的測試結果,搞清楚問題。不過,目前最期待的還是如果將專案中原來的C regex替換成Boost regex,不知道結果會怎麼樣,拭目以待吧。相關推薦
#牆裂推薦Boost regex# C,C++11,Boost三種regex庫效能比較
在最近的一個專案中,發現之前的正則匹配模組對於長字串匹配效能損失比較厲害,因此對長字串下的各種正則匹配進行了略微研究並附有例項。本文參考了部落格http://www.cnblogs.com/pmars/archive/2012/10/24/2736831.html(下文稱文
乾貨分享,iOS日誌顏色分類 豐富你的控制檯 簡直不要太好用 牆裂推薦
StarConsoleLink 這個工具的名字叫做:StarConsoleLink 他能豐富你的控制檯,給你的日誌加上超連結,並且集成了XcodeColors,讓你的日誌顏色多樣化,現已支援Xcode7.2,Xcode7.3.1,並支援直接sh安裝。 別忘了隨手點顆星哦。
Linux Shell 程式設計基礎詳解——吐血整理,牆裂推薦!
#第一部分:Linux Shell 簡介 Shell 是一個用 C 語言編寫的程式,它是使用者使用 Linux 的橋樑。Shell 既是一種命令語言,又是一種程式設計語言。 Shell 是指一種應用程式,這個應用程式提供了一個介面,使用者通過這個介面訪問作業系統核心的服務。 Ken Thompson 的 sh
(轉載)(牆裂推薦)神經網路的基本工作原理
作者:SoftwareTeacher 來源:CSDN 原文:https://blog.csdn.net/SoftwareTeacher/article/details/83991254 版權宣告:本文為博主原創文章,轉載請附上博文連結! ---------------
2017年全球最受歡迎的前端技術JS框架和薪酬排行("牆裂"推薦)
回顧過去的一年,前端技術發展很快,讓我們看看全球前端技術的使用情況: 從圖中可以總結出以下以下幾點: I. 全球目前在使用並且想要使用的前三名(I’ve USED it before, and WOULD use it again): 第一名:Re
JAVA面向物件面試題帶答案(牆裂推薦)
1) 在Java中,如果父類中的某些方法不包含任何邏輯,並且需要有子類重寫,應該使用(c)關鍵字來申明父類的這些方法。 a) Finalc b) Static c) Abstract d) Void2) 給定兩個java程式,如下:public interface Face{ int counter = 4
C#學習筆記(12)——三種方法操作XML
結點 記得 ext 應用程序 eval 資源 特性 pla cells 說明(2017-7-11 16:56:13): 原文地址: C#中常用的幾種讀取XML文件的方法 XML文件是一種常用的文件格式,例如WinForm裏面的app.config以及Web程序中的web.c
C#實現發送郵件的三種方法
thumbnail catch plugins () listbox 幫助 哈希 .text sbo 本文實例講述了C#實現發送郵件的三種方法。分享給大家供大家參考。具體方法分析如下: 一、問題: 最近公司由於一個R&I;項目的需要,用戶要求在購買產品或出貨等
Problem C HDU - 5687(字典樹的三種操作)
傳送門 題意:有三種操作,第一種是插入一個單詞,第二種是刪除具有這個字首的所有單詞,第三種是查詢是否具有這個字首的單詞 題解:使用字典樹,挑戰最大的是刪除這個操作,可以這樣,如果沒有這個字首直接退出,如果有這個字首,那麼先將這個字首之後所有的都消去,再回上來刪除具有這個字首的數量(很重要),
C++結構體多級排序的三種方法
C++結構體多級排序的三種方法 struct node{ int chinese,math; char name[15]; }; 需求:按數學成績從大到小排序 1.自定義比較器 //自定義比較函式 bool cmp(node a,node b){ return
C++ STL 建立執行緒的三種方式
使用 stl thread 編寫多執行緒程式時,編譯需要加 -pthread 通過函式指標建立執行緒 #include <iostream> #include <thread> using namespace std; void func(int id
初夏小談:斐波那契三種實現方法(C語言版)(第三種相信你沒見過)
斐波那契數列(Fibonaccisequnce),又稱黃金分割數列。研究斐波那契數列有相當重要的價值,例在現代物理、準晶體結構、化學等領域都有直接的應用。因此研究斐波那契數列也是很有必要的。 今天初夏將為大家帶來計算斐波那契數列第n位的三種方法 第一種利用遞迴的方法計算,程式碼相當簡單,但其
C++: 繼承和多型(一)三種繼承方式與許可權
繼承 在C++中,我們常要對某個函式進行多次複用,例如: 資訊管理系統中,對於教師、學生、教務人員等"類"而言,有部分資訊是通用的:姓名,性別,年齡,聯絡方式等。如果為每一種角色都編寫一個"類",會有不少重複的程式碼,造成效率上的浪費。 &nbs
【c++】遍歷字串的三種方式
就以:把字串“1234”轉換為整形1234,為例來說明遍歷字串的三種方式: ①常規方式(下標+operator[]) #include <iostream> #include <string> #include <vector> #include <
C#中遍歷ArraryList的三種方法
using System; using System.Collections; using System.Linq; using System.Text; namespace ArrayListDemo { class Program { static void
c++ STL中sort函式的三種使用方法
複習一下~ STL,C++中的標準模板庫, 使用起來方便並且效率較高; sort函式有三種用法: 一:對基本型別陣列從小到大排序 sort( 陣列名+n1,陣列名+n2); 將陣列中下標從n1到n2的元素進行從小到大排序,不包括n2,通過n1,n2 可以對整
C語言學習(六)三種基本程式結構
例一:財務人員給員工發工資時經常遇到這樣一個問題,即根據每個人的工資額(以元作為單位)計算出各種面值的鈔票的張數,且要求總張數最少。 例如,某職工工資為3436元,發放方案為:100元34張,20元1張,10元1張,5元1張,1元1張。 #include <stdio
C語言寫一個計算器的三種方法
方法一:常規方法#include<stdio.h> int Add(int a, int b) { return a + b; } int Sub(int a, int b) { return a - b; } int Mul(int a, int b) {
C++ 有參建構函式的三種呼叫方法
class Test { private: int a; int b; public: //帶引數的建構函式 Test(int a) {
黃聰:C#獲取網頁HTML內容的三種方式
HttpWebRequest httpReq; HttpWebResponse httpResp; string strBuff = ""; char[] cbuffer = new char[256]; int byteRead = 0; string filename