VB.NET字符串?dāng)?shù)組全面分析
VB.NET有很多值得學(xué)習(xí)的地方,這里我們主要介紹VB.NET字符串?dāng)?shù)組,包括介紹將VB.NET字符串?dāng)?shù)組轉(zhuǎn)換成字節(jié)數(shù)組等方面。
大部分的DLL過(guò)程(包括Windows 95 API中的所有過(guò)程)使用LPSTR類型字符串,這是指向標(biāo)準(zhǔn)的以null結(jié)束的C語(yǔ)言字符串的指針,它也被稱為ASCIIZ字符串。LPSTR 沒(méi)有前綴。下圖顯示了一個(gè)指向ASCIIZ字符串的LPSTR。
如果DLL過(guò)程需要一個(gè)LPSTR(指向以null結(jié)束的字符串的指針)作為參數(shù),可以在 VB 中將一個(gè)字符串以傳值的方式傳遞給它。因?yàn)橹赶駼STR的指針實(shí)際指向以null值結(jié)束的字符串的第一個(gè)數(shù)據(jù)字節(jié),所以對(duì)于DLL過(guò)程來(lái)說(shuō),它就是一個(gè) LPSTR。這樣傳入動(dòng)態(tài)連接庫(kù)的字符串,DLL過(guò)程也可以對(duì)它進(jìn)行修改,盡管它是以傳值方式傳入的。只有當(dāng)DLL過(guò)程需要一個(gè)指向LPSTR的指針時(shí),才以傳址的方式傳入字符串,這時(shí)DLL過(guò)程得到的是一個(gè)指向字符串指針的指針(相當(dāng)于C/C++中的char**),而不是通常所用的字符串的首地址(相當(dāng)于C/C++中的char*)。
當(dāng)需要把一個(gè)VB.NET字符串?dāng)?shù)組整個(gè)傳入動(dòng)態(tài)連接庫(kù)時(shí),情況就變得復(fù)雜多了,用傳遞簡(jiǎn)單數(shù)據(jù)類型數(shù)組的方式來(lái)傳遞VB.NET字符串?dāng)?shù)組是行不通的。當(dāng)我們以傳值的方式將一個(gè)VB.NET字符串?dāng)?shù)組的第一個(gè)元素傳進(jìn)動(dòng)態(tài)連接庫(kù)時(shí),DLL過(guò)程得到的實(shí)際上是該元素壓入堆棧段后的地址,而不是數(shù)據(jù)段中整個(gè)數(shù)組的首地址。也就是說(shuō),這時(shí)DLL過(guò)程只能得到數(shù)組的第一個(gè)元素,而無(wú)法訪問(wèn)整個(gè)數(shù)組。而以傳址方式傳入第一個(gè)元素時(shí),DLL過(guò)程只能得到指向該元素在堆棧段中地址的指針,同樣無(wú)法訪問(wèn)整個(gè)數(shù)組。這不能不說(shuō)是VB的一個(gè)不足。因此,在程序設(shè)計(jì)中,如果確實(shí)需要將整個(gè)VB.NET字符串?dāng)?shù)組傳入動(dòng)態(tài)庫(kù),就必須采取其它方法。
我們知道,在VB中,有一種Byte數(shù)據(jù)類型。每個(gè)Byte型變量占一個(gè)字節(jié),不含符號(hào)位,因 此所能表示的范圍為0到255。這種數(shù)據(jù)類型是專門用于存放二進(jìn)制數(shù)據(jù)的。為了將整個(gè)VB.NET字符串?dāng)?shù)組傳進(jìn)動(dòng)態(tài)庫(kù),可以用字節(jié)數(shù)組來(lái)保存字符串。由于Byte是一種簡(jiǎn)單數(shù)據(jù)類型,因此字節(jié)數(shù)組的傳遞是非常簡(jiǎn)單的。首先,需要把一個(gè)字符串正確地轉(zhuǎn)變成一個(gè)字節(jié)數(shù)組。這要涉及一 些字符集的知識(shí)。Windows 95和VB使用不同的字符集,Windows 95 API使用的是ANSI或DBCS 字符集,而VB使用的則是Unicode字符集。所謂ANSI字符集,是指每個(gè)字符都用一個(gè)字節(jié)表示,因此最多只能有28=256個(gè)不同的字符,這對(duì)于英語(yǔ)來(lái)說(shuō)已經(jīng)足夠了,但不能完全支持其它語(yǔ)言。DBCS字符集支持很多不同的東亞語(yǔ)言,如漢語(yǔ)、日語(yǔ)和朝鮮語(yǔ),它使用數(shù)字0-255表示ASCII 字符,其它大于255或小于0的數(shù)字表明該字符屬于非拉丁字符集;在DBCS中,ASCII字符的長(zhǎng)度是一個(gè)字節(jié),而漢語(yǔ)、日語(yǔ)和其它東亞字符的長(zhǎng)度是2個(gè)字節(jié)。而Unicode字符集則完全用兩個(gè)字節(jié)表示一個(gè)字符,因此最多可以表示216=65536個(gè)不同字符。也就是說(shuō),ANSI字符集中所有的字符都只占一個(gè)字節(jié),DBCS字符集中ASCII字符占一個(gè)字節(jié),漢字占兩個(gè)字節(jié),Unicode 字符集中每個(gè)字符都占兩個(gè)字節(jié)。由于VB與WindowsAPI使用的字符集不同,因此在進(jìn)行字符串到字節(jié)數(shù)組的轉(zhuǎn)換時(shí),當(dāng)用Asc函數(shù)取得一個(gè)字符的字節(jié)碼后,需要判斷它是否是一個(gè)ASCII 字符;如果是ASCII字符,則在轉(zhuǎn)換后的字節(jié)數(shù)組中就只占一個(gè)字節(jié),否則要占兩個(gè)字節(jié)。
下面給出了轉(zhuǎn)換函數(shù):GetChar Byte得到一個(gè)字符的高字節(jié)或低字節(jié),它的第一個(gè)參數(shù)是一個(gè)字符的ASCII碼,第二個(gè)參數(shù)是標(biāo)志取高字節(jié)還是低字節(jié);StrToByte按DBCS或ANSI格式將一個(gè)字符串轉(zhuǎn)換成一個(gè)字節(jié)數(shù)組,第一個(gè)參數(shù)是待轉(zhuǎn)換的字符串,第二個(gè)參數(shù)是轉(zhuǎn)換后的一個(gè)定長(zhǎng)字節(jié)數(shù)組,若該數(shù)組長(zhǎng)度不足以存放整個(gè)字符串,則截去超長(zhǎng)的部分;ChangeStrAryToByte 利用前兩個(gè)函數(shù)將VB.NET字符串?dāng)?shù)組轉(zhuǎn)換成字節(jié)數(shù)組,第一個(gè)參數(shù)是定長(zhǎng)的VB.NET字符串?dāng)?shù)組,其中每個(gè)元素都是一個(gè)字符串(各個(gè)元素包含的字符數(shù)可以不同),第二個(gè)參數(shù)是一個(gè)變長(zhǎng)的字節(jié)數(shù)組, 保存轉(zhuǎn)換后的結(jié)果。
- Function GetCharByte(ByVal OneChar As Integer,
ByVal IsHighByte As Boolean) As Byte- ' 該函數(shù)獲得一個(gè)字符的高字節(jié)或低字節(jié)
- If IsHighByte Then
- If OneChar >= 0 Then
- GetCharByte = CByte(OneChar \ 256)
- '右移8位,得到高字節(jié)
- Else
- GetCharByte = CByte((OneChar
- And &H7FFF) \ 256) Or &H80
- End If
- Exit Function
- Else
- GetCharByte = CByte(OneChar And &HFF)
- '屏蔽掉高字節(jié),得到低字節(jié)
- Exit Function
- End If
- End Function
- Sub StrToByte(StrToChange As String, ByteArray() As Byte)
- '該函數(shù)將一個(gè)字符串轉(zhuǎn)換成字節(jié)數(shù)組
- Dim LowBound, UpBound As Integer
- Dim i, count, length As Integer
- Dim OneChar As Integer
- count = 0
- length = Len(StrToChange)
- LowBound = LBound(ByteArray)
- UpBound = UBound(ByteArray)
- For i = LowBound To UpBound
- ByteArray(i) = 0 '初始化字節(jié)數(shù)組
- Next
- For i = LowBound To UpBound
- countcount = count + 1
- If count <= length Then
- OneChar = Asc(Mid(StrToChange, count, 1))
- If (OneChar > 255) Or (OneChar < 0) Then
- '該字符是非ASCII字符
- ByteArray(i) = GetCharByte(OneChar, True) '得到高字節(jié)
- ii = i + 1
- If i <= UpBound Then ByteArray(i)
- = GetCharByte(OneChar, False)
- '得到低字節(jié)
- Else
- '該字符是ASCII字符
- ByteArray(i) = OneCha
- End If
- Else
- Exit For
- End If
- Next
- End Sub
- Sub ChangeStrAryToByte(StrAry()
- As String, ByteAry() As Byte)
- '將字符串?dāng)?shù)組轉(zhuǎn)換成字節(jié)數(shù)組
- Dim LowBound, UpBound As Integer
- Dim i, count, StartPos, MaxLen As Integer
- Dim TmpByte() As Byte
- LowBound = LBound(StrAry)
- UpBound = UBound(StrAry)
- count = 0
- ReDim ByteAry(0)
- For i = LowBound To UpBound
- MaxLen = LenB(StrAry(i))
- ReDim TmpByte(MaxLen + 1)
- ReDim Preserve ByteAry(count + MaxLen + 1)
- Call StrToByte(StrAry(i), TmpByte) '轉(zhuǎn)換一個(gè)字符串
- StartPos = count
- Do
- ByteAry(count) = TmpByte(count - StartPos)
- countcount = count + 1
- If ByteAry(count - 1) = 0 Then Exit Do Loop
- '將每一個(gè)字符串對(duì)應(yīng)的字節(jié)數(shù)組按順序填入結(jié)果數(shù)組中
- ReDim Preserve ByteAry(count - 1)
- Next i
- End Sub
這樣,VB.NET字符串?dāng)?shù)組就全部轉(zhuǎn)換成了字節(jié)數(shù)組,然后只要將字節(jié)數(shù)組的第一個(gè)元素以傳址的方式傳入動(dòng)態(tài)連接庫(kù),DLL過(guò)程就可以正確地訪問(wèn)數(shù)組中的所有字符串了。但是,使用這種方法,當(dāng)DLL過(guò)程處理結(jié)束返回VB時(shí),VB得到的仍然是字節(jié)數(shù)組。如果需要在VB中再次得到該字節(jié)數(shù)組表示的字符串,還要把整個(gè)字節(jié)數(shù)組重新以0為分割符分成多個(gè)子數(shù)組(每個(gè)子數(shù)組都對(duì)應(yīng)原來(lái)字符串?dāng)?shù)組中的一個(gè)元素),然后使用VB函數(shù)StrConv將每個(gè)子數(shù)組轉(zhuǎn)換成字符串(轉(zhuǎn)換時(shí)第二個(gè)參數(shù)選vbUnicode),就可以顯示或進(jìn)行其它操作了。例如,其中一個(gè)子數(shù)組的名字是SubAry,則函數(shù)StrConv(SubAry,vbUnicode)就返回了它所對(duì)應(yīng)的字符串。
總之,VB應(yīng)用程序和動(dòng)態(tài)庫(kù)間字符串參數(shù)的傳遞是一個(gè)比較復(fù)雜的過(guò)程,使用時(shí)要非常謹(jǐn)慎。同時(shí)應(yīng)盡可能避免傳遞字符串?dāng)?shù)組類型的參數(shù),因?yàn)檫@很容易引起下標(biāo)越界、堆棧溢出等嚴(yán)重錯(cuò)誤。
【編輯推薦】