看了很久,還是決定把它關起來好了。
參考來源:
Why Delphi says 'cannot resolve unit name xxx'?
Delphi XE shows at least 200 errors but the program compiles just fine
How do I disable Error Insight?
2012/12/25
2012/12/21
2012/12/19
[轉] Delphi XE Refactor 重構功能簡單說明
Delphi XE Refactor重构功能简单说明
節錄部份說明如下:1. Rename: 包括重命名变量,函数名,组件名等等。好处是它会把整个项目里所有引用的地方都自动修改!经常碰到名字起的不好或者不一致的,想要修改又嫌麻烦,现在省事多了。
2. Declare Variable,Declare Field: 自动声明,举个例子: for i:=0 to TmpList.Count-1 do begin ...end; 如果变量i没有声明,可以用这个功能加个声明。麻烦的是会弹出对话框让自己选变量类型,并没有节省太多时间
3. Extract Method: 提取方法。这个也很实用,举个例子:
procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.Caption := 'This is a Test';
Button1.Enabled := false;
end;
我们经常会在form的create事件里做很多的初始化操作,有时候发现这些初始化操作需要重复执行,选中要提取的代码,选择Extract Method,指定方法名为InitScreen,如下:
procedure TForm1.FormCreate(Sender: TObject);
begin
InitScreen;
end;
procedure TForm1.InitScreen;
begin
Form1.Caption := 'This is a Test';
Button1.Enabled := false;
end;
如果提取的代码中引用了本地变量,会自动给新方法加上引用声明。不过,如果提取的代码过于复杂的时候有时会出错,需要自己手工修改。
4. Extract Resource String: 把一个字符串声明称resourcestring类型的常量,目前没发现有什么用处
5. Change Params: 修改函数参数,很不实用的功能
6. Find Unit: 查找类所在的单元,这个功能本来应该很有用,比如你在网上拷贝了一段代码,结果编译提示你找不到某个类型,用这个功能,输入类名,就可以告诉你在哪个单元里,并自动加到Interface或者implementation区域。可是,在实际使用的过程中,先是打开特别慢,我ssd的硬盘都要转半天,似乎每次都重新索引。其次就是有时有些类明明有就是找不到,目前也不清楚怎么回事。可惜这么好的一个功能变鸡肋,不知道XE2中有没有改进。
2012/12/18
自由倉頡真的好用
資料來源:Windows all x86/x64 倉頡輸入法(自由倉頡(轉載)+自製第五代碼表)
第五代碼表可採用"倉頡平台"的字碼表
終於有套符合TSF又支援第五代倉頡的免費輸入法了!
萬用字元*,用於模糊查找很好用
微軟倉是「難」鍵
自由倉是「:」鍵(沒錯,就是Shift + :)
中英切換
微軟倉是「Shift」
自由倉是「Shift + Backspace」
有些地方的切換反倒是不太習慣。
參考資料:《自由倉頡輸入法》常用熱鍵
2015/08/03 更新
由於在win7上的skype上聊天一陣子後,會造成軟體崩潰,最終還是放棄使用自由倉頡。
另一套「小小輸入法」平台則是在 PowerPoint 2010 輸入時一定會使 Powerpoint崩潰,故也無法使用。
目前,我使用的是 GCIN輸入法平台,在經過調整後,已經和微軟倉頡十分接近。
只是對win 8.1 以上的版本相容性還沒跟上。
GCIN是一套值得關注的好輸入法!
2012/12/14
幾種提昇 MDAC 應用程式效能的方法
資料來源:http://www.reocities.com/huanlin_tsai/faq/n012.txt
1.在記錄的迴圈當中使用預先連結的欄位物件
參考下面的範例:
procedure TForm1.Button1Click(Sender: TObject);
var
fldID, fldName, fldAge: TField;
begin
fldID := ADODataSet1.FieldByName('ID');
fldName := ADODataSet1.FieldByName('Name');
fldAge := ADODataSet1.FieldByName('Age');
ADODataSet1.First;
while not ADODataSet1.Eof do
begin
writeln(fldID.AsString);
writeln(fldName.AsString);
writeln(fldAge.AsString);
ADODataSet1.Next;
end;
ADODataSet1.Close;
end;
2.除非必要,否則不要使用 cursor-based 的方式更新資料
雖然使用 SQL 命令來更新資料在某些情況下並不適合,你還是應該盡量
使用這種方式,經由 recordset 物件 (TADODataSet 及其後代) 來更新
資料雖然方便,但所必須付出的代價也較大,儘管 SQL 命令比較難以使
用,但是絕對值得。
3.使用帶輸出參數的預儲程序取代只傳回一筆記錄的 Select 敘述
當你知道 query 傳回的結果集只有一筆記錄時,你可以使用帶輸出參數
的預儲程序取代開啟一個 recordset。
當你開啟一個 recordset 時,傳回的結果集包含了資料以及 metadata
,通常 metadata 的資料量蠻大的,所以你可能會想用一個帶輸出參數
的預儲程序。
Sub SingletonSp()
Dim cmd As New Command
cmd.ActiveConnection =
"Provider=SQLOLEDB;DataSource=sureshk1;Database=pubs;" & _
"User Id=sa;Password=;"
cmd.CommandText = "GetAuthorName"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Append cmd.CreateParameter(
"Id", adChar, adParamInput, 11)
cmd.Parameters.Append cmd.CreateParameter(
"FName", adChar, adParamOutput, 30)
cmd.Parameters.Append cmd.CreateParameter(
"LName", adChar, adParamOutput, 30)
cmd(0) = "172-32-1176"
cmd.Execute
Debug.Print cmd(1), cmd(2)
End Sub
Sub SingletonSelect()
Dim rs As New Recordset
rs.ActiveConnection =
"Provider=SQLOLEDB;Data Source=sureshk1;Database=pubs;" & _
"User Id=sa;Password=;"
rs.Source = "select au_fname, au_lname from authors where" & _
" au_id = '172-32-1176'"
rs.Open
Debug.Print rs!au_fname, rs!au_lname
End Sub
4.如果你必須使用 cursor,對傳回一筆記錄的結果集使用 Collect 方法
Recordset::get_Collect 及 Recordset::set_Collect 方法可以讓你
不用透過欄位物件而快速的存取欄位值,這種方法最適合用在只傳回一
筆記錄的結果集。
Sub Collect()
Dim rs As New Recordset
rs.ActiveConnection =
"Provider=SQLOLEDB;Data Source=sureshk1;Database=pubs;" & _
"User Id=sa;Password=;"
rs.Source = "select au_id, au_fname, au_lname from authors" & _
" where au_lname = 'Green'"
rs.Open
Debug.Print rs.Collect(0), rs.Collect(1), rs.Collect(2)
' VBA shortcut
Debug.Print rs!au_id, rs!au_fname, rs!au_lname
End Sub
5.只取得你所要的資料
Select * 的簡單使用容易讓人養成壞習慣,考慮只將你要的欄位 select
進來,同時也要考慮用 where 來限制傳回的記錄數量。
6.小心選擇你的 cursor location
如果你不需要捲動或者更新記錄,就使用 ADO 的內定值:
CursorLocation = adUseServer
CursorType = adOpenForwardOnly
LockType = adLockReadOnly
此內定值讓你最快速的存取只能向前捲動的結果集。
把握一個原則: 根據需求做適當的設定,不要給多餘而不必要的功能。
如果你需要前後移動記錄,就不要使用 adUseServer,ADO 將
adUserServer 做為內定值是由於向後相容的考量。在大部分需要捲動
的情況下,使用 client cursor 會比較適當,只有在比較特殊的情況
下,像是資料量非常龐大的時候,就應該使用 server cursor。
另外,如果你使用 client cursor,但是並不需要更新資料時,請將
LockType 設定為 adLockReadOnly,如果你需要更新資料,client
cursor 引擎必須取得額外的 metadata,而這會付出昂貴的代價。
後端為 SQL Server 時應避免使用 Static 及 Keyset cursor,SQL
Server 並不直接支援 Static 及 Keyset cursor,它必須複製資料到
暫存資料表,因此降低了效能。
註1:當 CursorLocation 為 adUseClient 時,ADO 會自動將 CursorType
設定為 adOpenStatic,即使你設定成其他值,在開啟資料集時 ADO
就會自動改為 adOpenStatic。
註2:Microsoft Jet database engine 不支援 dynamic cursor,所以OLE
DB Provider for Microsoft Jet 不支援 adLockDynamic cursor。
註3:當 CursorType 為 adOpenForwardOnly 或 adOpenDynamic 時,
Recordset 的 RecordCount 屬性總是等於-1。參考 http://
support.microsoft.com/support/kb/articles/q194/9/73.asp
7.調整 Recordset 的 CacheSize 屬性 (TADODataSet.CacheSize)
ADO 使用 Recordset 的 CacheSize 來決定要快取的記錄數量,當你在
快取的記錄範圍內移動 cursor 時,ADO 會直接由快取中傳回資料,當
你移出快取範圍時,ADO 會釋放快取,並且取得下一批快取記錄。
那麼 CacheSize 要設定多少會有最佳效能呢? 答案是視情況而定。你
應當嘗試用多個不同的 CacheSize 來找出讓你的應用程式表現最佳的
設定值(建議值: 10)。
8.儘快釋放掉沒有使用的 ADO 物件
9.自行描述命令參數
即 Delphi ADOExpress 的 TADOCommand.Parameters 屬性, 例如:
with ADOCommand1.Parameters do
begin
Items[0].Name := 'ReturnValue';
Items[0].DataType := ftInteger;
Items[0].Direction := pdReturnValue;
end;
10.使用原生 OLE DB Providers
11.對唯讀資料集以及需要較長時間的處理可以切斷 client cursor 與
connection 的連結以增進效能
切斷 Recordset 連結是 client cursor 引擎的一項功能,當你在處
理較費時的工作時可以利用這項功能,處理完之後可以再恢復連結。
Delphi 的 TADODataSet 及其後代應該只要將 Connection 屬性設為
nil 即可 (未驗證)。
12.不需要傳回結果集時使用 Connection 的 adExecuteNoRecords 選項
當你使用這個選項時,ADO 不會建立 Recordset 物件,也不會設定任
何 cursor 屬性。
Delphi:
TADOConnection.Execute(CommandText, eoExecuteNoRecords);
13.對於只使用一次的命令使用 Connection::Execute
當你使用 Connection::Execute 方法執行只需一次的命令時,ADO 對
此做了些效能最佳化。常見的使用時機是用在 IIS, ASP, 及 MTS 環境
下,典型的步驟是: 開啟 connection,執行傳回或不傳回資料集的命
令,處理結果集,然後關閉 connection。在這種情況下應該使用
Connection::Execute 以取代 Recordset::Open 或 Command::Execute
。當你使用 Connection::Execute 方法時,ADO 不會保留任何命令的
狀態資訊,因此會獲得較好的效能。如果你需要較多功能的 cursor 或
者你需要再執行 Execute 方法時帶參數的話,你仍然需要使用
Recordset::Open 或 Command::Execute 方法。
參考文件
http://www.microsoft.com/data/impperf.htm
http://vbdata.iwarp.com/speed.htm
1.在記錄的迴圈當中使用預先連結的欄位物件
參考下面的範例:
procedure TForm1.Button1Click(Sender: TObject);
var
fldID, fldName, fldAge: TField;
begin
fldID := ADODataSet1.FieldByName('ID');
fldName := ADODataSet1.FieldByName('Name');
fldAge := ADODataSet1.FieldByName('Age');
ADODataSet1.First;
while not ADODataSet1.Eof do
begin
writeln(fldID.AsString);
writeln(fldName.AsString);
writeln(fldAge.AsString);
ADODataSet1.Next;
end;
ADODataSet1.Close;
end;
2.除非必要,否則不要使用 cursor-based 的方式更新資料
雖然使用 SQL 命令來更新資料在某些情況下並不適合,你還是應該盡量
使用這種方式,經由 recordset 物件 (TADODataSet 及其後代) 來更新
資料雖然方便,但所必須付出的代價也較大,儘管 SQL 命令比較難以使
用,但是絕對值得。
3.使用帶輸出參數的預儲程序取代只傳回一筆記錄的 Select 敘述
當你知道 query 傳回的結果集只有一筆記錄時,你可以使用帶輸出參數
的預儲程序取代開啟一個 recordset。
當你開啟一個 recordset 時,傳回的結果集包含了資料以及 metadata
,通常 metadata 的資料量蠻大的,所以你可能會想用一個帶輸出參數
的預儲程序。
Sub SingletonSp()
Dim cmd As New Command
cmd.ActiveConnection =
"Provider=SQLOLEDB;DataSource=sureshk1;Database=pubs;" & _
"User Id=sa;Password=;"
cmd.CommandText = "GetAuthorName"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Append cmd.CreateParameter(
"Id", adChar, adParamInput, 11)
cmd.Parameters.Append cmd.CreateParameter(
"FName", adChar, adParamOutput, 30)
cmd.Parameters.Append cmd.CreateParameter(
"LName", adChar, adParamOutput, 30)
cmd(0) = "172-32-1176"
cmd.Execute
Debug.Print cmd(1), cmd(2)
End Sub
Sub SingletonSelect()
Dim rs As New Recordset
rs.ActiveConnection =
"Provider=SQLOLEDB;Data Source=sureshk1;Database=pubs;" & _
"User Id=sa;Password=;"
rs.Source = "select au_fname, au_lname from authors where" & _
" au_id = '172-32-1176'"
rs.Open
Debug.Print rs!au_fname, rs!au_lname
End Sub
4.如果你必須使用 cursor,對傳回一筆記錄的結果集使用 Collect 方法
Recordset::get_Collect 及 Recordset::set_Collect 方法可以讓你
不用透過欄位物件而快速的存取欄位值,這種方法最適合用在只傳回一
筆記錄的結果集。
Sub Collect()
Dim rs As New Recordset
rs.ActiveConnection =
"Provider=SQLOLEDB;Data Source=sureshk1;Database=pubs;" & _
"User Id=sa;Password=;"
rs.Source = "select au_id, au_fname, au_lname from authors" & _
" where au_lname = 'Green'"
rs.Open
Debug.Print rs.Collect(0), rs.Collect(1), rs.Collect(2)
' VBA shortcut
Debug.Print rs!au_id, rs!au_fname, rs!au_lname
End Sub
5.只取得你所要的資料
Select * 的簡單使用容易讓人養成壞習慣,考慮只將你要的欄位 select
進來,同時也要考慮用 where 來限制傳回的記錄數量。
6.小心選擇你的 cursor location
如果你不需要捲動或者更新記錄,就使用 ADO 的內定值:
CursorLocation = adUseServer
CursorType = adOpenForwardOnly
LockType = adLockReadOnly
此內定值讓你最快速的存取只能向前捲動的結果集。
把握一個原則: 根據需求做適當的設定,不要給多餘而不必要的功能。
如果你需要前後移動記錄,就不要使用 adUseServer,ADO 將
adUserServer 做為內定值是由於向後相容的考量。在大部分需要捲動
的情況下,使用 client cursor 會比較適當,只有在比較特殊的情況
下,像是資料量非常龐大的時候,就應該使用 server cursor。
另外,如果你使用 client cursor,但是並不需要更新資料時,請將
LockType 設定為 adLockReadOnly,如果你需要更新資料,client
cursor 引擎必須取得額外的 metadata,而這會付出昂貴的代價。
後端為 SQL Server 時應避免使用 Static 及 Keyset cursor,SQL
Server 並不直接支援 Static 及 Keyset cursor,它必須複製資料到
暫存資料表,因此降低了效能。
註1:當 CursorLocation 為 adUseClient 時,ADO 會自動將 CursorType
設定為 adOpenStatic,即使你設定成其他值,在開啟資料集時 ADO
就會自動改為 adOpenStatic。
註2:Microsoft Jet database engine 不支援 dynamic cursor,所以OLE
DB Provider for Microsoft Jet 不支援 adLockDynamic cursor。
註3:當 CursorType 為 adOpenForwardOnly 或 adOpenDynamic 時,
Recordset 的 RecordCount 屬性總是等於-1。參考 http://
support.microsoft.com/support/kb/articles/q194/9/73.asp
7.調整 Recordset 的 CacheSize 屬性 (TADODataSet.CacheSize)
ADO 使用 Recordset 的 CacheSize 來決定要快取的記錄數量,當你在
快取的記錄範圍內移動 cursor 時,ADO 會直接由快取中傳回資料,當
你移出快取範圍時,ADO 會釋放快取,並且取得下一批快取記錄。
那麼 CacheSize 要設定多少會有最佳效能呢? 答案是視情況而定。你
應當嘗試用多個不同的 CacheSize 來找出讓你的應用程式表現最佳的
設定值(建議值: 10)。
8.儘快釋放掉沒有使用的 ADO 物件
9.自行描述命令參數
即 Delphi ADOExpress 的 TADOCommand.Parameters 屬性, 例如:
with ADOCommand1.Parameters do
begin
Items[0].Name := 'ReturnValue';
Items[0].DataType := ftInteger;
Items[0].Direction := pdReturnValue;
end;
10.使用原生 OLE DB Providers
11.對唯讀資料集以及需要較長時間的處理可以切斷 client cursor 與
connection 的連結以增進效能
切斷 Recordset 連結是 client cursor 引擎的一項功能,當你在處
理較費時的工作時可以利用這項功能,處理完之後可以再恢復連結。
Delphi 的 TADODataSet 及其後代應該只要將 Connection 屬性設為
nil 即可 (未驗證)。
12.不需要傳回結果集時使用 Connection 的 adExecuteNoRecords 選項
當你使用這個選項時,ADO 不會建立 Recordset 物件,也不會設定任
何 cursor 屬性。
Delphi:
TADOConnection.Execute(CommandText, eoExecuteNoRecords);
13.對於只使用一次的命令使用 Connection::Execute
當你使用 Connection::Execute 方法執行只需一次的命令時,ADO 對
此做了些效能最佳化。常見的使用時機是用在 IIS, ASP, 及 MTS 環境
下,典型的步驟是: 開啟 connection,執行傳回或不傳回資料集的命
令,處理結果集,然後關閉 connection。在這種情況下應該使用
Connection::Execute 以取代 Recordset::Open 或 Command::Execute
。當你使用 Connection::Execute 方法時,ADO 不會保留任何命令的
狀態資訊,因此會獲得較好的效能。如果你需要較多功能的 cursor 或
者你需要再執行 Execute 方法時帶參數的話,你仍然需要使用
Recordset::Open 或 Command::Execute 方法。
參考文件
http://www.microsoft.com/data/impperf.htm
http://vbdata.iwarp.com/speed.htm
关于在DELPHI6中使用正则表达式的一些心得
转载地址:
http://www.delphibbs.com/keylife/iblog_show.asp?xid=25712
http://blog.csdn.net/senfore/article/details/2154944
一. 工具选择
DELPHI6本身没有处理正则表达式的库,只能找第三方库。在选择方面,我的标准是:
1. 不必向开发环境注册控件
2. 接口简单
3. 符合主流的perl式正则语法
4. 专业,至少应该有个看上去专业的专门网站
5. 免费
目前符合以上标准, 比较好的RegEx类库有TRegExpr ( http://www.regexpstudio.com/ )与PerlRegEx ( http://www.regular-expressions.info/ )。
TRegExpr 是俄国人做的RE类库,应该说是目前国内最主流的免费RegEx类库了。纯DELPHI写成,支持中文,可以选择安装为开发环境控件,也可直接作为类库单 元使用(只有一个主类,一个pas文件)。美中不足是自从2004年后就没有更新了,版本一直是0.9xxx,就是不上1。而且不支持 Lookaround语法(前瞻与回溯功能)
PerlRegEx底层是用C的类库,完全符合PCRE标准(兼容Perl的正则表达式)。 文件结构比TRegExpr复杂一点,包括一个放底层obj文件的子目录和两个接口.pas文件,实际使用时只需要向项目中添加一个单元(当然也可以注册 成为控件)。之前的版本据说对中文支持不够,最新版本我在中文环境下用倒没遇到什么问题。说明文档也声称支持Unicode。
现在我是常 备两个这两个类库,但主要还是用PerlRegEx。除了迷信最后更新日期与C的执行效率外。还看中了RegexRubby这个基于同一套C类库的 RegEx编写工具,以及PerlRegEx提供了一个study方法,声称可以对正则式做点前期编译,提高执行效率。
=========================================================================
二 使用方法:
解 压了PerlRegEx包后,如果不想注册控件,除了PerlRegEx.pas、CHelpers.pas和PCRE目录外,其他的东西可以建个隐藏目 录搁置起来(没认真阅读用户协议,不知道能不能随便删。。。)。维持这两个文件与PCRE的目录结构不要变。使用时只需要把PerlRegEx.pas添 加进项目,在单元中uses PerlRegEx就可以了。
PerlRegEx提供了TPerlRegEx类。主要用法是:
RegEx : TPerlRegEx;
....
RegEx := TPerlRegEx;
try
RegEx.Subject := '要匹配的正文';
RegEx.RegEx := '正则表达式';
if RegEx.Match then ....
finally
RegEx.free
end;
如果要多次匹配并做一些处理,可以:
Matched : boolean;
....
RegEx.Match;
while RegEx.FoundMatch do
begin
....
RegEx.MatchAgain;
end;
如果要替换匹配到的内容,可以
RegEx.Subject := '要匹配的正文';
RegEx.RegEx := '正则表达式';
RegEx.Replace := '替换的字符串'
if RegEx.Match then RegEx.ReplaceAll; //结果在RegEx.subject
或者 if RegEx.Match then Result := RegEx.Replacement;
匹配到的字符串放在RegEx.MatchedExpression中,长度在RegEx.MatchedExpressionLength中,上一次匹配的结束位置在RegEx.Stop中
匹配到的子串放在RegEx.SubExpressions[i]中,子串个数在RegEx.SubExpressionCount中。
如果正则式很复杂而且常用,可创建一个生存期相对长的TPerlRegEx实例.设置好RegEx属性后,使用.Study方法对正则表达式进行预处理.据帮助文档说,文档资料会大大提高效率.
详情可参考文档。有一点文档上没有提到(又或者我看漏了),在第一次匹配之后,如果没有重新赋值subject,下一次匹配无论用Match或者MatchAgain,都是从上次的结束位置开始。所以如果要重新开始匹配,应先把RegEx.Start := 0;
==========================================================================
三. 使用和编写正则表达式要点
使用正则表达式,通常是用作三种用途:校验字符串,提取信息,处理字符串.
当 用作校验时,通常是对正文整体校验,例如通常是判断正文是否正确的邮件地址,而不是判断正文是否含有正确的邮件地址.因此应在正则表达式的两端加上行开始 锚点^与行结束锚点$.如果待校验的文字允许两端有空格,则应该在锚点前后用' *'或'/s*'(允许空格与TAB)匹配进去.
设计正则表达式的要点在于分段.对要匹配的内容分好段,就能够容易地各个击破.通常在要匹配的文字中会有一些分段的提示,例如逻辑上的单位、重复出现的模式或者不能连续重复出现的字符(串)。
以设计校验输入数字的正则条件为例,可以先列出符合条件的情况:
1234 / 12.34 / -12.34 / 12.3e4 / 12.3e-4 / .12E-34
可以看出,逻辑上的单位有:符号,整数部分,小数点,小数部分,e(或E),指数符号,指数部分
技术上的分段标志有:
符号:在开始与E后面各可能出现一次
小数点:只能出现一次,若出现,其后必须有小数部分。
e:只能出现一次,若出现,其后必须有指数部分。
所有“若出现,其后必须有。。。”的都可以考虑分为一组。可得初步方案: [+/-]?/d*(/./d+)?([Ee][+/-]*/d+)?
但 这个设计有问题,前半段的 /d*(/./d+)? 是可以匹配空串的,而需求是如果有整数部分,则小数部分可选。如果无整数部分,则必须有小数部分,直观的做法是改为(/d+(/./d+)?|/. /d+)。再认真观察一下,可以发现这个选择式无论任何情况,都是以/d+结尾,而我们实际上并不关心这个/d+是匹配到整数部分还是小数部分,至于前面 的小数点与整数部分都是可选的。因此,这部分可以改写为 /d*/.?/d+
所以最终的校验式是:^ *[+/-]?/d*/.?/d+([Ee][+/-]*/d+)? *$
使 用正则表达式提取信息是一个难点,但也是体现正则表达式强大实力的一个方面。提取信息的正则表达式必须要考虑四个方面:不误判(应该有一定语法检验能 力),不漏判,子串能匹配到正确位置。一些结构复杂或具有循环结构的正文,可能需要多次处理或使用开发语言的循环结构来辅助提取。具体技巧我现在还觉得比 较模糊,以下仅举几个例子:
1. 查找并分析 XX1>XX2,<,=,!,空格,TAB符号的任何字符串,式子两端与元素之间允许有空格或TAB
由 于这个不等式可能在上下文中,我们需要先在正文中把合语法的不等式隔离出来,否则下面的循环部分就会匹配到下一条不等式的部分。在这个例子中还算简单,找 到 '/b[^<>=!/s]+((不等符号)[^<>=!/s]+)+/b' 就可以了(其中不等符号在下面解释)。但这样只能匹配到整个式子而不能分别提取式子中的子串信息,当出现(...)+时,对应子串内容是只是最后一个匹配 到的串。
对每个匹配到的结果,因此这里需要分开两次提取,第二次需要使用循环来辅助。
先取最开始的子串,这个很容易,直接 '^/s*([^<>=/s]+)'就可以了,注意^ *是为了去掉开头多余的空格与TAB。真正的XX1在匹配到的子串1中。
接 着开始分析 '((不等符号)[^<>=!/s]+)+' 部分。整个(...)+结构需要在外部用开发语言的循环来逐次提取。先列出合法的不等符号:>=,>,=,<=,<,<& gt;,==,!>,!<,!>=,!<=,!=。因此不等符号部分应该是(!?>=?|!?& lt;=?|!?==?|<>)。
因此,要分析篇正文,就需要:
1)A匹配 '/b[^<>=!/s]+(/s*(!?>=?|!?<=?|!?==?|<>)/s*[^<>=!/s]+)+/b' 找出不等式。
2) 对每个A匹配结果, B匹配'^/s*([^<>=!/s])',提取子串1记录为变量名称。
3) 接着B匹配'/s*(!?>=?|!?<=?|!?==?|<>)/s*([^<>=!/s]+)',提取子串1记录为符号,子串2记录为变量名称。
4) 从3)开始循环直到找不到B匹配结果
5)从1)开始循环直到找不到A匹配结果
2. 提取电话号码
这 是不久前帮朋友做的一个小程序。事情是这样的:他的公司需要撒网式找澳大利亚酿酒公司合作伙伴,他的任务就是把网上查到的酿酒公司的联系方式记录入库。记 录联系方式的数据库是把公司名称,地址,电话号码,传真号码,邮箱等信息分开不同字段储存的。于是他必须用鼠标在网页准确选下各种信息然后粘贴到数据表 中,不但工作效率低,而且据说由于鼠标精确动作太多,手腕酸得不得了。于是找我帮忙写个小程序,需求是他可以把联系方式部分整个复制下来,我的程序自动提 取出有关信息。以下是提取电话号码信息功能的设计过程:
我看了一下联系方式的可能情况,发现有时里面会包括多个电话号码,而朋友的数据库只记录一个号码。因此我决定把他选定文本中所有电话号码都提取出来,列在一个ComboBox中让他选择。在正文中标记为“Phone”或"Ph"或"P"的优先列在前面。
电话号码的写法五花八门,先考虑电话的标记,一般有以下几种:
Phone, Phone:, (Phone), P, P-, PH 等等
因 此可把匹配标记的正则表达式设计为 '(?-i)(?:/(?Phone|Ph?)[-:]?/s*/)?)'。这种写法有个缺陷是无法保证两边括号能匹配(例如能匹配到 '(Phone:')。但在这里的目的并不是校验,括号不匹配并不影响我提取电话号码,只求简单写成这样就够了,否则就要写 成:'(?:Phone|Ph?)[-:]?|/((?:Phone|Ph?)[-:]?/s*/))' ,麻烦得多。
然后考虑电话号码本身,一个完整的固定电话号码可能是这样的:
+61 2 1234 5678 , (61 2) 1234 5678 , (61-)2-12345678, 61 (0) 2 12345678
而也可能省略国家代码(+61)或洲区号(02)简写成
02 1234 5678 , 1234 5678, 2-1234-5678
也有可能不按主号码四字一断的做法,写成
02 123 456 78 等等
构建正则表达式的过程如下:
匹 配国家代号:(?:[+( ]*61[-) ]*)? 可以看出,这个式子如果用来校验是不合格的,它能匹配到'(++61-)-'这样的正文。但我在这里是为了提取公司网页上的信息,公司不会在自己的联系 方法信息里放入这样的乱码。提取信息时,根据需求,在不误判的前提下可以假定输入信息不会出现太离谱的错误。
匹配区号:(?:/(? *0?/)? *(/d) *[-)]? *)? 同上,这个式子也可能匹配到不合法的正文。这里如果把区号与主体号码合并处理会简单很多。但我在这里想把区号提取出来,作为判断省份的一个依据。
匹 配电话号码主体:(?:/d{5,}(?:[ -]/d+)*|/d{1,4}(?:[ -]/d+)+) 如果仅考虑匹配电话号码,用/d*(?: [ -]/d+)*就行了。但澳大利亚的邮政编码刚好是四位数字,而且地址中的信箱号有可能是3到4位数字。因此这里用了个麻烦的写法,如果连续数字小于5 位,则后面必须跟一个连接号或空格,然后再跟数字,才能匹配到。
因此优先选取的号码的正则表达式是:(?-i) (?:/(?Phone|Ph?)[-:]?/s*/)?)((?:[+( ]*61[-) ]*)?(?:/(? *0?/)? *(/d) ?[-)]?/s*)?(?:/d{5,}(?:[ -]/d+)*|/d{1,4}(?:[ -]/d+)+))
电话号码在子串1,区号在子串2
第 一轮扫描正文,一边找到匹配,一边把其替换成''。完成后,就可以用((?:[+( ]*61[-) ]*)?(?:/(? *0?/)? *(/d) ?[-)]?/s*)?(?:/d{5,}(?:[ -]/d+)*|/d{1,4}(?:[ -]/d+)+)) (去掉了匹配电话标志部分)对替换后的正文,找出所有没有电话标志,但符合格式的子串。
http://www.delphibbs.com/keylife/iblog_show.asp?xid=25712
http://blog.csdn.net/senfore/article/details/2154944
一. 工具选择
DELPHI6本身没有处理正则表达式的库,只能找第三方库。在选择方面,我的标准是:
1. 不必向开发环境注册控件
2. 接口简单
3. 符合主流的perl式正则语法
4. 专业,至少应该有个看上去专业的专门网站
5. 免费
目前符合以上标准, 比较好的RegEx类库有TRegExpr ( http://www.regexpstudio.com/ )与PerlRegEx ( http://www.regular-expressions.info/ )。
TRegExpr 是俄国人做的RE类库,应该说是目前国内最主流的免费RegEx类库了。纯DELPHI写成,支持中文,可以选择安装为开发环境控件,也可直接作为类库单 元使用(只有一个主类,一个pas文件)。美中不足是自从2004年后就没有更新了,版本一直是0.9xxx,就是不上1。而且不支持 Lookaround语法(前瞻与回溯功能)
PerlRegEx底层是用C的类库,完全符合PCRE标准(兼容Perl的正则表达式)。 文件结构比TRegExpr复杂一点,包括一个放底层obj文件的子目录和两个接口.pas文件,实际使用时只需要向项目中添加一个单元(当然也可以注册 成为控件)。之前的版本据说对中文支持不够,最新版本我在中文环境下用倒没遇到什么问题。说明文档也声称支持Unicode。
现在我是常 备两个这两个类库,但主要还是用PerlRegEx。除了迷信最后更新日期与C的执行效率外。还看中了RegexRubby这个基于同一套C类库的 RegEx编写工具,以及PerlRegEx提供了一个study方法,声称可以对正则式做点前期编译,提高执行效率。
=========================================================================
二 使用方法:
解 压了PerlRegEx包后,如果不想注册控件,除了PerlRegEx.pas、CHelpers.pas和PCRE目录外,其他的东西可以建个隐藏目 录搁置起来(没认真阅读用户协议,不知道能不能随便删。。。)。维持这两个文件与PCRE的目录结构不要变。使用时只需要把PerlRegEx.pas添 加进项目,在单元中uses PerlRegEx就可以了。
PerlRegEx提供了TPerlRegEx类。主要用法是:
RegEx : TPerlRegEx;
....
RegEx := TPerlRegEx;
try
RegEx.Subject := '要匹配的正文';
RegEx.RegEx := '正则表达式';
if RegEx.Match then ....
finally
RegEx.free
end;
如果要多次匹配并做一些处理,可以:
Matched : boolean;
....
RegEx.Match;
while RegEx.FoundMatch do
begin
....
RegEx.MatchAgain;
end;
如果要替换匹配到的内容,可以
RegEx.Subject := '要匹配的正文';
RegEx.RegEx := '正则表达式';
RegEx.Replace := '替换的字符串'
if RegEx.Match then RegEx.ReplaceAll; //结果在RegEx.subject
或者 if RegEx.Match then Result := RegEx.Replacement;
匹配到的字符串放在RegEx.MatchedExpression中,长度在RegEx.MatchedExpressionLength中,上一次匹配的结束位置在RegEx.Stop中
匹配到的子串放在RegEx.SubExpressions[i]中,子串个数在RegEx.SubExpressionCount中。
如果正则式很复杂而且常用,可创建一个生存期相对长的TPerlRegEx实例.设置好RegEx属性后,使用.Study方法对正则表达式进行预处理.据帮助文档说,文档资料会大大提高效率.
详情可参考文档。有一点文档上没有提到(又或者我看漏了),在第一次匹配之后,如果没有重新赋值subject,下一次匹配无论用Match或者MatchAgain,都是从上次的结束位置开始。所以如果要重新开始匹配,应先把RegEx.Start := 0;
==========================================================================
三. 使用和编写正则表达式要点
使用正则表达式,通常是用作三种用途:校验字符串,提取信息,处理字符串.
当 用作校验时,通常是对正文整体校验,例如通常是判断正文是否正确的邮件地址,而不是判断正文是否含有正确的邮件地址.因此应在正则表达式的两端加上行开始 锚点^与行结束锚点$.如果待校验的文字允许两端有空格,则应该在锚点前后用' *'或'/s*'(允许空格与TAB)匹配进去.
设计正则表达式的要点在于分段.对要匹配的内容分好段,就能够容易地各个击破.通常在要匹配的文字中会有一些分段的提示,例如逻辑上的单位、重复出现的模式或者不能连续重复出现的字符(串)。
以设计校验输入数字的正则条件为例,可以先列出符合条件的情况:
1234 / 12.34 / -12.34 / 12.3e4 / 12.3e-4 / .12E-34
可以看出,逻辑上的单位有:符号,整数部分,小数点,小数部分,e(或E),指数符号,指数部分
技术上的分段标志有:
符号:在开始与E后面各可能出现一次
小数点:只能出现一次,若出现,其后必须有小数部分。
e:只能出现一次,若出现,其后必须有指数部分。
所有“若出现,其后必须有。。。”的都可以考虑分为一组。可得初步方案: [+/-]?/d*(/./d+)?([Ee][+/-]*/d+)?
但 这个设计有问题,前半段的 /d*(/./d+)? 是可以匹配空串的,而需求是如果有整数部分,则小数部分可选。如果无整数部分,则必须有小数部分,直观的做法是改为(/d+(/./d+)?|/. /d+)。再认真观察一下,可以发现这个选择式无论任何情况,都是以/d+结尾,而我们实际上并不关心这个/d+是匹配到整数部分还是小数部分,至于前面 的小数点与整数部分都是可选的。因此,这部分可以改写为 /d*/.?/d+
所以最终的校验式是:^ *[+/-]?/d*/.?/d+([Ee][+/-]*/d+)? *$
使 用正则表达式提取信息是一个难点,但也是体现正则表达式强大实力的一个方面。提取信息的正则表达式必须要考虑四个方面:不误判(应该有一定语法检验能 力),不漏判,子串能匹配到正确位置。一些结构复杂或具有循环结构的正文,可能需要多次处理或使用开发语言的循环结构来辅助提取。具体技巧我现在还觉得比 较模糊,以下仅举几个例子:
1. 查找并分析 XX1>XX2
由 于这个不等式可能在上下文中,我们需要先在正文中把合语法的不等式隔离出来,否则下面的循环部分就会匹配到下一条不等式的部分。在这个例子中还算简单,找 到 '/b[^<>=!/s]+((不等符号)[^<>=!/s]+)+/b' 就可以了(其中不等符号在下面解释)。但这样只能匹配到整个式子而不能分别提取式子中的子串信息,当出现(...)+时,对应子串内容是只是最后一个匹配 到的串。
对每个匹配到的结果,因此这里需要分开两次提取,第二次需要使用循环来辅助。
先取最开始的子串,这个很容易,直接 '^/s*([^<>=/s]+)'就可以了,注意^ *是为了去掉开头多余的空格与TAB。真正的XX1在匹配到的子串1中。
接 着开始分析 '((不等符号)[^<>=!/s]+)+' 部分。整个(...)+结构需要在外部用开发语言的循环来逐次提取。先列出合法的不等符号:>=,>,=,<=,<,<& gt;,==,!>,!<,!>=,!<=,!=。因此不等符号部分应该是(!?>=?|!?& lt;=?|!?==?|<>)。
因此,要分析篇正文,就需要:
1)A匹配 '/b[^<>=!/s]+(/s*(!?>=?|!?<=?|!?==?|<>)/s*[^<>=!/s]+)+/b' 找出不等式。
2) 对每个A匹配结果, B匹配'^/s*([^<>=!/s])',提取子串1记录为变量名称。
3) 接着B匹配'/s*(!?>=?|!?<=?|!?==?|<>)/s*([^<>=!/s]+)',提取子串1记录为符号,子串2记录为变量名称。
4) 从3)开始循环直到找不到B匹配结果
5)从1)开始循环直到找不到A匹配结果
2. 提取电话号码
这 是不久前帮朋友做的一个小程序。事情是这样的:他的公司需要撒网式找澳大利亚酿酒公司合作伙伴,他的任务就是把网上查到的酿酒公司的联系方式记录入库。记 录联系方式的数据库是把公司名称,地址,电话号码,传真号码,邮箱等信息分开不同字段储存的。于是他必须用鼠标在网页准确选下各种信息然后粘贴到数据表 中,不但工作效率低,而且据说由于鼠标精确动作太多,手腕酸得不得了。于是找我帮忙写个小程序,需求是他可以把联系方式部分整个复制下来,我的程序自动提 取出有关信息。以下是提取电话号码信息功能的设计过程:
我看了一下联系方式的可能情况,发现有时里面会包括多个电话号码,而朋友的数据库只记录一个号码。因此我决定把他选定文本中所有电话号码都提取出来,列在一个ComboBox中让他选择。在正文中标记为“Phone”或"Ph"或"P"的优先列在前面。
电话号码的写法五花八门,先考虑电话的标记,一般有以下几种:
Phone, Phone:, (Phone), P, P-, PH 等等
因 此可把匹配标记的正则表达式设计为 '(?-i)(?:/(?Phone|Ph?)[-:]?/s*/)?)'。这种写法有个缺陷是无法保证两边括号能匹配(例如能匹配到 '(Phone:')。但在这里的目的并不是校验,括号不匹配并不影响我提取电话号码,只求简单写成这样就够了,否则就要写 成:'(?:Phone|Ph?)[-:]?|/((?:Phone|Ph?)[-:]?/s*/))' ,麻烦得多。
然后考虑电话号码本身,一个完整的固定电话号码可能是这样的:
+61 2 1234 5678 , (61 2) 1234 5678 , (61-)2-12345678, 61 (0) 2 12345678
而也可能省略国家代码(+61)或洲区号(02)简写成
02 1234 5678 , 1234 5678, 2-1234-5678
也有可能不按主号码四字一断的做法,写成
02 123 456 78 等等
构建正则表达式的过程如下:
匹 配国家代号:(?:[+( ]*61[-) ]*)? 可以看出,这个式子如果用来校验是不合格的,它能匹配到'(++61-)-'这样的正文。但我在这里是为了提取公司网页上的信息,公司不会在自己的联系 方法信息里放入这样的乱码。提取信息时,根据需求,在不误判的前提下可以假定输入信息不会出现太离谱的错误。
匹配区号:(?:/(? *0?/)? *(/d) *[-)]? *)? 同上,这个式子也可能匹配到不合法的正文。这里如果把区号与主体号码合并处理会简单很多。但我在这里想把区号提取出来,作为判断省份的一个依据。
匹 配电话号码主体:(?:/d{5,}(?:[ -]/d+)*|/d{1,4}(?:[ -]/d+)+) 如果仅考虑匹配电话号码,用/d*(?: [ -]/d+)*就行了。但澳大利亚的邮政编码刚好是四位数字,而且地址中的信箱号有可能是3到4位数字。因此这里用了个麻烦的写法,如果连续数字小于5 位,则后面必须跟一个连接号或空格,然后再跟数字,才能匹配到。
因此优先选取的号码的正则表达式是:(?-i) (?:/(?Phone|Ph?)[-:]?/s*/)?)((?:[+( ]*61[-) ]*)?(?:/(? *0?/)? *(/d) ?[-)]?/s*)?(?:/d{5,}(?:[ -]/d+)*|/d{1,4}(?:[ -]/d+)+))
电话号码在子串1,区号在子串2
第 一轮扫描正文,一边找到匹配,一边把其替换成''。完成后,就可以用((?:[+( ]*61[-) ]*)?(?:/(? *0?/)? *(/d) ?[-)]?/s*)?(?:/d{5,}(?:[ -]/d+)*|/d{1,4}(?:[ -]/d+)+)) (去掉了匹配电话标志部分)对替换后的正文,找出所有没有电话标志,但符合格式的子串。
DBGrid繪圖上的一個小Bug
在Delphi 2010之後,DBGrid多了一個Drawing Style屬性,可以讓我們DBGrid更為漂亮
但不知怎麼的,常常在使用上見到「殘影」,難道Delphi改走武術路線不成?
當然不是,這是DBGrid的一個Bug
一直到XE3(2013)為止,這個Bug一直沒有解決
於是國外有人修改了VCL Source來解決這個問題
這邊轉貼他的Fix法:
但不知怎麼的,常常在使用上見到「殘影」,難道Delphi改走武術路線不成?
當然不是,這是DBGrid的一個Bug
一直到XE3(2013)為止,這個Bug一直沒有解決
於是國外有人修改了VCL Source來解決這個問題
這邊轉貼他的Fix法:
2012/12/13
2012/12/10
Chrome套件介紹-Webpage Screenshot 網頁快照
當你看到某個網頁很不錯想抓圖下來留念,或者希望能將網頁上的訊息抓圖保存下來,留待日後當做證據或其他備份用途,可以試試看用下面的網頁抓圖軟體來抓圖。
Webpage Screenshot是個Gooogle Chrome瀏覽器的擴充套件,可以安裝在4.x以上版本的瀏覽器,內建繁體中文語系。可以將目前看到的可視範圍內的網頁畫面抓圖下來,存成JPG圖檔,也可將整個網頁從上到下、完整的抓圖下來,整個操作相當簡單。
詳見:重灌狂人-「網頁快照」支援自動捲頁、將網頁全文抓圖存檔(Google Chrome套件)
Webpage Screenshot是個Gooogle Chrome瀏覽器的擴充套件,可以安裝在4.x以上版本的瀏覽器,內建繁體中文語系。可以將目前看到的可視範圍內的網頁畫面抓圖下來,存成JPG圖檔,也可將整個網頁從上到下、完整的抓圖下來,整個操作相當簡單。
詳見:重灌狂人-「網頁快照」支援自動捲頁、將網頁全文抓圖存檔(Google Chrome套件)
圖片來源:重灌狂人 |
2012/12/05
DataSetProvider.ResolveToDataSet
DataSetProvider.ResolveToDataSet=false时
代表DataSetProvider自动产生更新数据的Sql叙述用于更新数据库。
DataSetProvider.ResolveToDataSet=true时
代表通过DataSetProvider连接的Query来更新数据。
資料來源
关于用query 作为数据集更新数据和DataSetProvider.ResolveToDataSet的关系
代表DataSetProvider自动产生更新数据的Sql叙述用于更新数据库。
DataSetProvider.ResolveToDataSet=true时
代表通过DataSetProvider连接的Query来更新数据。
資料來源
关于用query 作为数据集更新数据和DataSetProvider.ResolveToDataSet的关系
訂閱:
文章 (Atom)