日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

我学Delphi心得及笔记----字符串操作(第六讲)

發(fā)布時(shí)間:2025/6/15 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我学Delphi心得及笔记----字符串操作(第六讲) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Delphi 中字符串的操作很簡單,但幕后情況卻相當(dāng)復(fù)雜。Pascal 傳統(tǒng)的字符串操作方法與Windows 不同,Windows吸取了C語言的字符串操作方法。32位Delphi中增加了長字符串類型,該類型功能強(qiáng)大,是Delphi 確省的字符串類型。

字符串類型

在Borland公司的Turbo Pascal和16位Delphi中,傳統(tǒng)的字符串類型是一個(gè)字符序列,序列的頭部是一個(gè)長度字節(jié),指示當(dāng)前字符串的長度。由于只用一個(gè)字節(jié)來表示字符串的長度,所以字符串不能超過255個(gè)字符。這一長度限制為字符串操作帶來不便,因?yàn)槊總€(gè)字符串必須定長(確省最大值為255),當(dāng)然你也可以聲明更短的字符串以節(jié)約存儲(chǔ)空間。

字符串類型與數(shù)組類型相似。實(shí)際上一個(gè)字符串差不多就是一個(gè)字符類型的數(shù)組,因?yàn)橛肹]符號(hào),你就能訪問字符串中的字符,這一事實(shí)充分說明了上述觀點(diǎn)。

為克服傳統(tǒng)Pascal 字符串的局限性,32位Delphi增加了對(duì)長字符串的支持。這樣共有三種字符串類型:

  • ShortString 短字符串類型也就是前面所述的傳統(tǒng) Pascal 字符串類型。這類字符串最多只能有255個(gè)字符,與16位Delphi中的字符串相同。短字符串中的每個(gè)字符都屬于ANSIChar 類型(標(biāo)準(zhǔn)字符類型)。
  • ANSIString長字符串類型就是新增的可變長字符串類型。這類字符串的內(nèi)存動(dòng)態(tài)分配,引用計(jì)數(shù),并使用了更新前拷貝(copy--on-write)技術(shù)。這類字符串長度沒有限制(可以存儲(chǔ)多達(dá)20億個(gè)字符!),其字符類型也是ANSIChar 類型。
  • WideString 長字符串類型與ANSIString 類型相似,只是它基于WideChar 字符類型,WideChar 字符為雙字節(jié)Unicode 字符。

使用長字符串

如果只簡單地用String定義字符串,那么該字符串可能是短字符串也可能是ANSI長字符串,這取決于$H 編譯指令的值,$H+(確省)代表長字符串(ANSIString 類型)。長字符串是Delphi 庫中控件使用的字符串。

Delphi 長字符串基于引用計(jì)數(shù)機(jī)制,通過引用計(jì)數(shù)追蹤內(nèi)存中引用同一字符串的字符串變量,當(dāng)字符串不再使用時(shí),也就是說引用計(jì)數(shù)為零時(shí),釋放內(nèi)存。

如果你要增加字符串的長度,而該字符串鄰近又沒有空閑的內(nèi)存,即在同一存儲(chǔ)單元字符串已沒有擴(kuò)展的余地,這時(shí)字符串必須被完整地拷貝到另一個(gè)存儲(chǔ)單元。當(dāng)這種情況發(fā)生時(shí),Delphi運(yùn)行時(shí)間支持程序會(huì)以完全透明的方式為字符串重新分配內(nèi)存。為了有效地分配所需的存儲(chǔ)空間,你可以用SetLength 過程設(shè)定字符串的最大長度值:

SetLength (String1, 200);

SetLength 過程只是完成一個(gè)內(nèi)存請(qǐng)求,并沒有實(shí)際分配內(nèi)存。它只是把將來所需的內(nèi)存預(yù)留出來,實(shí)際上并沒有使用這段內(nèi)存。這一技術(shù)源于Windows 操作系統(tǒng),現(xiàn)被Delphi用來動(dòng)態(tài)分配內(nèi)存。例如,當(dāng)你請(qǐng)求一個(gè)很大的數(shù)組時(shí),系統(tǒng)會(huì)將數(shù)組內(nèi)存預(yù)留出來,但并沒有把內(nèi)存分配給數(shù)組。

一般不需要設(shè)置字符串的長度,不過當(dāng)需要把長字符串作為參數(shù)傳遞給API 函數(shù)時(shí)(經(jīng)過類型轉(zhuǎn)換后),你必須用SetLength 為該字符串預(yù)留內(nèi)存空間,這一點(diǎn)我會(huì)在后面進(jìn)行說明。

看一看內(nèi)存中的字符串

為了幫你更好地理解字符串的內(nèi)存管理細(xì)節(jié),我寫了一個(gè)簡例StrRef 。在程序中我聲明了兩個(gè)全程字符串:Str1 和 Str2,當(dāng)按下第一個(gè)按鈕時(shí),程序把一個(gè)字符串常量賦給第一個(gè)變量,然后把第一個(gè)變量賦給第二個(gè):

Str1 := 'Hello'; Str2 := Str1;

除了字符串操作外,程序還用下面的StringStatus 函數(shù)在一個(gè)列表框中顯示字符串的內(nèi)部狀態(tài):

function StringStatus (const Str: string): string; beginResult := 'Address: ' + IntToStr (Integer (Str)) +', Length: ' + IntToStr (Length (Str)) + ', References: ' + IntToStr (PInteger (Integer (Str) - 8)^) +', Value: ' + Str; end;

在StringStatus 函數(shù)中,用常量參數(shù)傳遞字符串至關(guān)重要。用拷貝方式(值參)傳遞會(huì)引起副作用,因?yàn)楹瘮?shù)執(zhí)行過程中會(huì)產(chǎn)生一個(gè)對(duì)字符串的額外引用;與此相反,通過引用(var)或常量(const)參數(shù)傳遞不會(huì)產(chǎn)生這種情況。由于本例不希望字符串被修改,因此選用常量參數(shù)。

為獲取字符串內(nèi)存地址(有利于識(shí)別串的實(shí)際內(nèi)容也有助于觀察兩個(gè)不同的串變量是否引用了同一內(nèi)存區(qū)),我通過類型映射把字符串類型強(qiáng)行轉(zhuǎn)換為整型。字符串實(shí)際上是引用,也就是指針:字符串變量保存的是字符串的實(shí)際內(nèi)存地址。

為了提取引用計(jì)數(shù)信息,我利用了一個(gè)鮮為人知的事實(shí):即字符串長度和引用計(jì)數(shù)信息實(shí)際上保存在字符串中, 位于實(shí)際內(nèi)容和字符串變量所指的內(nèi)存位置之前,其負(fù)偏移量對(duì)字符串長度來說是-4(用Length 函數(shù)很容易得到這個(gè)值),對(duì)引用記數(shù)來說是-8。

不過必須記住,以上關(guān)于偏移量的內(nèi)部信息在未來的Delphi版本中可能會(huì)變,沒有寫入正式Delphi文檔的特性很難保證將來不變。

通過運(yùn)行這個(gè)例子,你會(huì)看到兩個(gè)串內(nèi)容相同、內(nèi)存位置相同、引用記數(shù)為2,如圖7.1中列表框上部所示。現(xiàn)在,如果你改變其中一個(gè)字符串的值,那么更新后字符串的內(nèi)存地址將會(huì)改變。這是copy-on-write技術(shù)的結(jié)果。

第二個(gè)按鈕(Change)的OnClick 事件代碼如下,結(jié)果如圖7.1列表框第二部分所示:

procedure TFormStrRef.BtnChangeClick(Sender: TObject); beginStr1 [2] := 'a';ListBox1.Items.Add ('Str1 [2] := ''a''');ListBox1.Items.Add ('Str1 - ' + StringStatus (Str1));ListBox1.Items.Add ('Str2 - ' + StringStatus (Str2)); end;

注意,BtnChangeClick 只能在執(zhí)行完BtnAssignClick 后才能執(zhí)行。為此,程序啟動(dòng)后第二個(gè)按鈕不能用(按鈕的Enabled 屬性設(shè)成False);第一個(gè)方法結(jié)束后激活第二個(gè)按鈕。你可以自由地?cái)U(kuò)展這個(gè)例子,用StringStatus 函數(shù)探究其它情況下長字符串的特性。

Delphi 字符串與 Windows PChar字符串

長字符串為零終止串,這意味著長字符串完全與Windows使用的C語言零終止串兼容,這給長字符串使用帶來了便利。一個(gè)零終止串是一個(gè)字符序列,該序列以一個(gè)零字節(jié)(或null)結(jié)尾。零終止串在Delphi中可用下標(biāo)從零開始的字符數(shù)組表示,C語言就是用這種數(shù)組類型定義字符串,因此零終止字符數(shù)組在Windows API 函數(shù)(基于C語言)中很常見。由于Pascal長字符串與C語言的零終止字符串完全兼容,因此當(dāng)需要把字符串傳遞給Windows API 函數(shù)時(shí),你可以直接把長字符串映射為PChar 類型。

下例把一個(gè)窗體的標(biāo)題拷貝給PChar 字符串(用API 函數(shù)GetWindowText),然后再把它拷貝給按鈕的Caption 屬性,代碼如下:

procedure TForm1.Button1Click (Sender: TObject); varS1: String; beginSetLength (S1, 100);GetWindowText (Handle, PChar (S1), Length (S1));Button1.Caption := S1; end;

你可以在例LongStr 中找到這段代碼。注意:代碼中用SetLength函數(shù)為字符串分配內(nèi)存,假如內(nèi)存分配失敗,那么程序就會(huì)崩潰;如果你直接用PChar 類型傳遞值(而不是象以以上代碼那樣接受一個(gè)值),那么代碼會(huì)很簡單,因?yàn)椴恍枰x臨時(shí)字符串,也不需要初始化串。下面代碼把一個(gè)Label(標(biāo)簽)控件的Caption 屬性作為參數(shù)傳遞給了API函數(shù),只需要簡單地把屬性值映射為PChar類型:

SetWindowText (Handle, PChar (Label1.Caption));

當(dāng)需要把WideString 映射為Windows兼容類型時(shí),你必須用PWideChar 代替PChar進(jìn)行轉(zhuǎn)換,WideString常用于OLE和 COM 程序。

剛才展現(xiàn)了長字符串的優(yōu)點(diǎn),現(xiàn)在談?wù)勊谋锥恕.?dāng)你把長字符串轉(zhuǎn)換為PChar 類型時(shí)可能會(huì)引發(fā)一些問題,問題根本在于:轉(zhuǎn)換以后字符串及其內(nèi)容將由你來負(fù)責(zé),Delphi 不再管了。現(xiàn)在把上面Button1Click代碼稍作修改:

procedure TForm1.Button2Click(Sender: TObject); varS1: String; beginSetLength (S1, 100);GetWindowText (Handle, PChar (S1), Length (S1));S1 := S1 + ' is the title'; // this won't workButton1.Caption := S1; end;

程序編譯通過,但執(zhí)行結(jié)果會(huì)令你驚訝,因?yàn)榘粹o的標(biāo)題并沒變,所加的常量字符串沒有添加到按鈕標(biāo)題中。問題原因是Windows寫字符串時(shí)(在GetWindowText API調(diào)用中),Windows 沒有正確設(shè)置Pascal 長字符串的長度。Delphi 仍可以輸出該字符串,并能通過零終止符判斷字符串何時(shí)結(jié)束,但是如果你在零終止符后添加更多的字符,那么這些字符將被忽略。

怎么解決這個(gè)問題呢?解決方法是告訴系統(tǒng)把GetWindowText API函數(shù)返回的字符串再轉(zhuǎn)換成Pascal字符串。然而,如果你用以下代碼:

S1 := String (S1);

Delphi 系統(tǒng)將不予理睬,因?yàn)榘岩环N類型轉(zhuǎn)換為它自己的類型是無用的操作。為獲得正確的Pascal 長字符串,需要你把字符串重新映射為一個(gè)PChar 字符串,然后讓Delphi 再把它轉(zhuǎn)回到字符串:

S1 := String (PChar (S1));

實(shí)際上,你可以跳過字符串轉(zhuǎn)換(S1 := PChar (S1));, 因?yàn)樵贒elphi中Pchar轉(zhuǎn)換到string是自動(dòng)執(zhí)行的,最終代碼如下:

procedure TForm1.Button3Click(Sender: TObject); varS1: String; beginSetLength (S1, 100);GetWindowText (Handle, PChar (S1), Length (S1));S1 := String (PChar (S1));S1 := S1 + ' is the title';Button3.Caption := S1; end;

另一個(gè)辦法是用PChar 字符串的長度重新設(shè)定Delphi 字符串長度,可以這樣寫:

SetLength (S1, StrLen (PChar (S1)));

在例LongStr中你可以看到三種方法的結(jié)果,分別由三個(gè)按鈕執(zhí)行。如果只想訪問窗體標(biāo)題,僅需要用到窗體對(duì)象本身的Caption 屬性,沒有必要寫這段迷糊人的代碼,這段代碼只是用來說明字符串轉(zhuǎn)換問題。當(dāng)調(diào)用Windows API 函數(shù)時(shí)會(huì)遇到這種實(shí)際問題,那時(shí)你就不得不考慮這一復(fù)雜情況了。

格式化字符串

使用加號(hào)(+)操作符和轉(zhuǎn)換函數(shù)(如IntToStr),你確實(shí)能把已有值組合成字符串,不過另有一種方法能格式化數(shù)字、貨幣值和其他字符串,這就是功能強(qiáng)大的Format 函數(shù)及其一族。

Format 函數(shù)參數(shù)包括:一個(gè)基本文本字符串、一些占位符(通常由%符號(hào)標(biāo)出)和一個(gè)數(shù)值數(shù)組,數(shù)組中每個(gè)值對(duì)應(yīng)一個(gè)占位符。例如,把兩個(gè)數(shù)字格式化為字符串的代碼如下:

Format ('First %d, Second %d', [n1, n2]);

其中n1和n2是兩個(gè)整數(shù)值,第一個(gè)占位符由第一個(gè)值替代,第二個(gè)占位符由第二個(gè)值替代,以此類推。如果占位符輸出類型(由%符號(hào)后面的字母表示)與對(duì)應(yīng)的參數(shù)類型不匹配,將產(chǎn)生一個(gè)運(yùn)行時(shí)間錯(cuò)誤,因此設(shè)置編譯時(shí)間類型檢查會(huì)有利于Format 函數(shù)的使用。

除了%d外,Format 函數(shù)還定義了許多占位符,見表7.1。這些占位符定義了相應(yīng)數(shù)據(jù)類型的默認(rèn)輸出,你可以用更深一層的格式化約束改變默認(rèn)輸出,例如一個(gè)寬度約束決定了輸出中的字符個(gè)數(shù),而精度約束決定了小數(shù)點(diǎn)的位數(shù)。例如

Format ('%8d', [n1]);

該句把數(shù)字n1轉(zhuǎn)換成有8個(gè)字符的字符串,并通過填充空白使文本右對(duì)齊,左對(duì)齊用減號(hào)(-) 。

表 7.1: Format函數(shù)的占位符

占位符說明
d (decimal)將整型值轉(zhuǎn)換為十進(jìn)制數(shù)字字符串
x (hexadecimal)將整型值轉(zhuǎn)換為十六進(jìn)制數(shù)字字符串
p (pointer)將指針值轉(zhuǎn)換為十六進(jìn)制數(shù)字字符串
s (string)拷貝字符串、字符、或字符指針值到一個(gè)輸出字符串
e (exponential)將浮點(diǎn)值轉(zhuǎn)換為指數(shù)表示的字符串
f (floating point)將浮點(diǎn)值轉(zhuǎn)換為浮點(diǎn)表示的字符串
g (general)使用浮點(diǎn)或指數(shù)將浮點(diǎn)值轉(zhuǎn)換為最短的十進(jìn)制字符串
n (number)將浮點(diǎn)值轉(zhuǎn)換為帶千位分隔符的浮點(diǎn)值
m (money)將浮點(diǎn)值轉(zhuǎn)換為現(xiàn)金數(shù)量表示的字符串,轉(zhuǎn)換結(jié)果取決于地域設(shè)置,詳見Delphi幫助文件的Currency and date/time formatting variables主題

領(lǐng)會(huì)以上內(nèi)容最好的辦法是你親自進(jìn)行字符串格式化試驗(yàn)。為了簡便起見,我寫了FmtTest 程序,它能將整數(shù)和浮點(diǎn)數(shù)轉(zhuǎn)換為格式化字符串。從圖7.2可見,程序窗體分為左右兩部分,左邊對(duì)應(yīng)整型數(shù)字轉(zhuǎn)換,右邊對(duì)應(yīng)浮點(diǎn)數(shù)轉(zhuǎn)換。

各部分的第一個(gè)編輯框顯示需要格式化為字符串的數(shù)值。第一個(gè)編輯框下方有一個(gè)按鈕,用來執(zhí)行格式化操作并在消息框中顯示結(jié)果;緊接著第二個(gè)編輯框用于輸入格式化類型串。你也可以單擊ListBox 控件中的任一行,選擇預(yù)定義的格式化類型串,也可以自行輸入,每輸入一個(gè)新的格式化類型串,該類型串就會(huì)被添加到列表框中(注意,關(guān)閉程序就失去了添加的類型)。

本例只簡單使用了不同的控制文本來產(chǎn)生輸出,下面列出了其中一個(gè)Show 按鈕事件代碼:

procedure TFormFmtTest.BtnIntClick(Sender: TObject); beginShowMessage (Format (EditFmtInt.Text,[StrToInt (EditInt.Text)]));// if the item is not there, add itif ListBoxInt.Items.IndexOf (EditFmtInt.Text) < 0 thenListBoxInt.Items.Add (EditFmtInt.Text); end;

這段代碼主要用EditFmtInt 編輯框的文本和EditInt 控件的值進(jìn)行了格式化操作。如果格式化類型串沒有在列表框中列出,那么輸入的串會(huì)被添加到列表框中;如果用戶在列表框中進(jìn)行點(diǎn)擊,代碼會(huì)把點(diǎn)擊的串移到編輯框中:

procedure TFormFmtTest.ListBoxIntClick(Sender: TObject); beginEditFmtInt.Text := ListBoxInt.Items [ ListBoxInt.ItemIndex]; end;

轉(zhuǎn)載于:https://www.cnblogs.com/youshan/archive/2011/07/09/2101822.html

總結(jié)

以上是生活随笔為你收集整理的我学Delphi心得及笔记----字符串操作(第六讲)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。