盡管Visual Basic并不是我最喜歡的開(kāi)發(fā)工具,但我喜歡它簡(jiǎn)單而又豐富的庫(kù)集。每當(dāng)開(kāi)發(fā)一個(gè)需要處理大量文本數(shù)據(jù)的應(yīng)用程序時(shí),需要具有拼寫(xiě)錯(cuò)誤糾正功能,例如,微軟的Word程序,當(dāng)運(yùn)行"拼寫(xiě)檢查"時(shí),將提供給你一個(gè)改正錯(cuò)誤的機(jī)會(huì)(盡管是建議),它同時(shí)也提供了"查找替換"工具,用以進(jìn)行用戶(hù)定義的單詞替換。這篇文章我將解釋如何在VB應(yīng)用程序中實(shí)現(xiàn)"查找替換"功能。
二、前提條件
在解釋代碼的時(shí)候,我假定讀者朋友們已經(jīng)有使用Visual Basic的經(jīng)驗(yàn),熟悉Visual Studio開(kāi)發(fā)環(huán)境中各種內(nèi)置的控件及庫(kù)函數(shù)(盡管我使用的不多)。我已經(jīng)嘗試著盡可能地簡(jiǎn)化程序代碼,用不了多久你就可以明白程序的邏輯。如果想對(duì)一些庫(kù)函數(shù)(如參數(shù),語(yǔ)法)進(jìn)一步詳細(xì)地理解,可以參閱MSDN。圖一是程序運(yùn)行后的效果圖:
圖一、程序?qū)崿F(xiàn)"查找替換"功能時(shí)的效果圖 |
首先創(chuàng)建一個(gè)標(biāo)準(zhǔn)的EXE類(lèi)型的VB工程,將默認(rèn)窗體更名為frmMainForm ,在默認(rèn)窗體上增添一個(gè)菜單,具體設(shè)置如下(符號(hào)"&"用于加速鍵,單詞mnu后的名字用來(lái)說(shuō)明菜單項(xiàng)的名字(在代碼中使用)):
&Edit ...&Find and Replace mnuFindandreplace E&xit mnuExit |
使用Project > Add Form菜單向工程中添加另外一個(gè)窗體,將這個(gè)窗體命名為"frmFindReplace",并在屬性窗口中將它的BorderStyle屬性設(shè)置為"4 - FixedToolWindow"。現(xiàn)在,添加兩個(gè)TextBox控件,并分別命名為"txtSearchTerm"和"txtReplaceWithString"。添加一個(gè)復(fù)選框,命名為chkCaseSense。最后,添加一個(gè)命令按鈕控件,命名為"cmdReplace"。
在frmMainForm窗體中添加如下代碼:
Private Sub mnuExit_Click() End End Sub Private Sub mnuFindandreplace_Click() frmFindReplace.FindnReplace txtClientArea End Sub |
從上面代碼中可以非常明顯地看出, 當(dāng)點(diǎn)擊Exit菜單時(shí),我們想終結(jié)應(yīng)用程序,當(dāng)點(diǎn)擊"Find and Replace"菜單時(shí),想通過(guò)共用接口frmFindReplace及FindnReplace()方法來(lái)激活frmFindReplace窗體。這個(gè)公用的接口使查找算法具有普遍性,使用這個(gè)接口時(shí),需要提供一個(gè)TextBox作為參數(shù)(在這里面,搜尋將被執(zhí)行)。通過(guò)使用你自己的TextBox的名字來(lái)代替txtClientArea的名字,可以在多個(gè)文本框內(nèi)執(zhí)行"查找替換"功能,而不用更改代碼。"查找和替換"的實(shí)現(xiàn)代碼主要是在frmFindReplace窗體內(nèi),這個(gè)模塊的代碼如下:
' This variable is used for making the algorithm generic. Dim txtClient As TextBox ' This method is the public interface to SnR functionality. Public Sub FindnReplace(ByRef Tb As TextBox) Set txtClient = Tb Me.Show , txtClient.Parent End Sub Private Sub cmdReplace_Click() Dim CaseSense As Integer Dim SourceText As String Dim SourceTextCopy As String Dim Cnt As Integer ' Check for the case sensitivity options If (chkCaseSense.Value = vbChecked) Then CaseSense = 0 Else CaseSense = 1 End If ' One contains the original text and another contains replaced ' (updated) one. ' Used to check whether a replacement was done or not. SourceText = txtClient.Text SourceTextCopy = SourceText If Len(SourceText) = 0 Then Exit Sub End If On Error GoTo ErrHandler Dim SearchTermLen As Integer Dim FndPos As Integer SearchTermLen = Len(txtSearchTerm.Text) ' Search from the begining of the document. Cnt = 1 ' This is endless loop (terminated on a condition checked inside ' the loop body). While (1) FndPos = InStr(Cnt, SourceText, txtSearchTerm.Text, CaseSense) ' When a match is found, replace it appropriately. If (FndPos > 0) Then SourceText = ReplaceFun(SourceText, FndPos, Len(txtSearchTerm.Text), txtReplaceWithString.Text) Cnt = FndPos + SearchTermLen Else Cnt = Cnt + 1 End If ' Whether a replacement was done at all or not If (Cnt >= Len(SourceText)) Then txtClient.Text = SourceText If (SourceTextCopy <> SourceText) Then MsgBox "Finished replacing all occurrences.", vbInformation + vbOKOnly, "Replaced All" Else MsgBox "No matching strings found. No text replaced.", vbInformation + vbOKOnly, "No Replacement" End If Unload Me Exit Sub End If ' Else Restart from henceforth Wend Exit Sub ErrHandler: Response = MsgBox("An error ocurred while searching. Inform the developer with details.", _ vbExclamation + vbOKOnly, "Error Searching") End Sub Private Sub Form_Load() ' Default SearchTerm must be the one selected by the user in ' MainForm If Len(txtClient.SelText) <> 0 Then txtSearchTerm.Text = txtClient.SelText End If End Sub Function ReplaceFun(Source As String, FromPos As Integer, _ Length As Integer, StringTBReplaced _ As String) As String ' Replaces a source string with new one appropriately Dim ResultStr As String ResultStr = Left(Source, FromPos - 1) ResultStr = ResultStr & StringTBReplaced ResultStr = ResultStr & Right(Source, Len(Source) - FromPos - Length + 1) ReplaceFun = ResultStr End Function Private Sub txtReplaceWithString_Change() Call EnableDisableReplaceButton End Sub Private Sub txtReplaceWithString_GotFocus() ' Select the contents of the textbox If Len(txtReplaceWithString.Text) <> 0 Then txtReplaceWithString.SelStart = 0 txtReplaceWithString.SelLength = Len(txtReplaceWithString.Text) End If End Sub Private Sub txtSearchTerm_Change() Call EnableDisableReplaceButton End Sub Private Sub EnableDisableReplaceButton() If Len(txtSearchTerm.Text) <> 0 _ And Len(txtReplaceWithString.Text) <> 0 Then cmdReplace.Enabled = True Else cmdReplace.Enabled = False End If End Sub Private Sub txtSearchTerm_GotFocus() ' Select the contents of textbox If Len(txtSearchTerm.Text) <> 0 Then txtSearchTerm.SelStart = 0 txtSearchTerm.SelLength = Len(txtSearchTerm.Text) End If End Sub |
公用接口SearchnReplace的"查找替換"算法帶有普遍性,使用這種方法,"查找替換"功能可以在任何應(yīng)用程序中實(shí)現(xiàn),而不用更改frmFindReplace的代碼。只是在調(diào)用公用接口的地方需要做一些小的改動(dòng)。
"查找和替換"代碼在cmdReplace_Click()過(guò)程與ReplaceFun() 函數(shù)下,讓我們從cmdReplace_Click()過(guò)程開(kāi)始。
首先,變量CaseSense用于跟蹤用戶(hù)的選擇,是否是大小寫(xiě)敏感,如果用戶(hù)選擇大小寫(xiě)敏感的話(huà),它的值設(shè)置為0,否則,設(shè)置為1。變量SourceText和SourceTextCopy用于保存frmMainForm窗體中 txtClientArea的內(nèi)容(或者是用戶(hù)在主窗體中為T(mén)extBox提供的內(nèi)容,對(duì)于本程序來(lái)說(shuō),它就是txtClientArea),兩個(gè)變量保存同樣的內(nèi)容將在下面進(jìn)行解釋?zhuān)ㄒ粋(gè)是臨時(shí)變量)。變量Cnt用來(lái)跟蹤文檔的結(jié)束,當(dāng)重新從文檔開(kāi)始處進(jìn)行"查找替換"時(shí)它將非常有用。這個(gè)變量含蓄地說(shuō)明了當(dāng)前光標(biāo)的位置,下次的"查找替換"將從這個(gè)位置開(kāi)始。
主要的算法在while循環(huán)內(nèi),只要替換或是源TextBox被掃描時(shí),這個(gè)循環(huán)都將被激活。
第二步,我使用了InStr()庫(kù)函數(shù),它將從第一個(gè)參數(shù)規(guī)定的位置處開(kāi)始搜索,如果在第二個(gè)參數(shù)字符串中發(fā)現(xiàn)與第三個(gè)字符串相匹配的子串,將返回該子串的位置。最后一個(gè)參數(shù)是大小寫(xiě)敏感選項(xiàng)。
InStr()函數(shù)的返回值傳遞給FndPos,如果在源文檔中發(fā)現(xiàn)了所要查找的字符串,這個(gè)值將大于0,否則將返回一個(gè)0值。在緊接著的IF語(yǔ)句中,首先替換SourceText變量,然后是通過(guò)FndPos + SearchTermLen語(yǔ)句修改Cnt變量,這是必須的, 因?yàn)橄乱淮蔚?quot;查找和替換"要從新的位置開(kāi)始。另一方面,在Else部分,Cnt變量加一,說(shuō)名查找和替換要從下一個(gè)字符開(kāi)始。緊接著的幾個(gè)語(yǔ)句用來(lái)通知用戶(hù)查找結(jié)果,它們非常容易理解,也非常容易實(shí)現(xiàn)?梢灾苯犹^(guò)它們。
最后,ReplaceFun()函數(shù)進(jìn)行實(shí)際的替換工作,它從源字符串的左邊開(kāi)始,挑選出所有的字符,直到發(fā)現(xiàn)所搜索的字符串位置處為止。緊接著,StringTBReplace被添加到結(jié)果的后面,它將代替所匹配的源字符串。最后,所有的保留的源字符(排除所要更換的子字符串)被添加到這個(gè)結(jié)果中去,并作為結(jié)果返回。 五、結(jié)論
有很多方法來(lái)實(shí)現(xiàn)這一功能,但我認(rèn)為我所使用的方法是最簡(jiǎn)單也是最直接的,通過(guò)例子程序,你可以在應(yīng)用程序中實(shí)現(xiàn)自己的"查找替換"功能。