1. 程式人生 > >VB 位元組陣列和字串的轉換問題 (StringByte)

VB 位元組陣列和字串的轉換問題 (StringByte)

一、 前言

   資料型別轉換在程式設計中經常用到,VB6提供了一整套型別轉換的函式。但是,在進行型別轉換時,有時候僅僅依靠VB提供的函式是不能達到自己的目的的。因此,需要考慮用其他的方法來完成資料型別轉換。本文僅談VB6中位元組陣列和字串的相互轉換過程中應注意的問題及其解決辦法。

   在Visual Basic中使用Byte陣列主要是為了32位API函式的引數傳遞和函式的返回。在32位的Visual Basic版本中,字串被假定為Unicode字元,其中每個字元佔用兩個位元組。系統自動地將Unicode的兩個連續位元組轉換成1個位元組的ANSI字元。但是,如果該字串包含二進位制資料,其內容將變得很難理解。例如,一個漢字是兩個位元組,在Visual Basic 6.0中的長度就只是1,這將給我們計算單個漢字的國標碼帶來一些麻煩。有了Byte陣列,這些問題就將迎刃而解。

   另外,Visual Basic中的字串和C語言中的字串有一些不同,本文將給出一個函式,把C字串轉換成Visual Basic字串。

二、 用Byte陣列代替字串



   Byte陣列包含的是0-255之間的ASCII碼字元,它不會象字串那樣被系統作預處理。你可以在很多API函式中用來Byte陣列代替字串。

   例如,下面的程式碼中用GetSystemDirectory這個Windows API函式來取得Windows的系統路徑。一共有兩段程式碼,一段程式碼是傳遞一個字串來儲存函式返回的系統路徑,另一段程式碼是傳遞一個Byte陣列來代替字串。

   為了更好地比較,兩段程式碼的不同部分都用黑體標出。讀者可以仔細比較這兩段程式碼的差異,這樣您會更深入地理解Byte陣列和字串的差別。

   把這兩段程式碼的任何一段放入一個窗體中執行,但擊窗體的空白區域,你將會在窗體中看到Windows的系統路徑。

   下面是使用字串的程式碼:

Private Declare FunctionGetSystemDirectoryLib"kernel32"Alias"GetSystemDirectoryA"(ByVallpBufferAs String,ByValnSizeAs Long)As Long

Private Sub
Form_Click()
 
DimnAs Integer
DimstrAs String

  str = Space$(256)
  n = GetSystemDirectory(str,
256)
  str = Left$(str, n)
  Print str
End Sub


   在上面這段程式碼中,字串引數lpBuffer返回Windows的系統路徑。在函式呼叫之前,將變數預定義成256個字元,並在函式返回時清除多餘的字元。

注意:


   在呼叫API函式之前,通常都需要預先定義一個字串或者Byte陣列以供API函式儲存資料。應該養成這種良好的程式設計習慣。否則,你的程式有可能崩潰,甚至導致你的系統崩潰。

   下面是使用Byte陣列的程式碼:

Private Declare FunctionGetSystemDirectoryLib"kernel32"Alias"GetSystemDirectoryA"(ByReflpBufferAs Byte,ByValnSizeAs Long)As Long

Private Sub
Form_Click()
 
DimnAs Integer
DimBuffer()As Byte
DimstrAas String

  Buffer=Space$(256)
  n = GetSystemDirectory(Buffer(
0),256)
  strA=StrConv(Buffer,vbUnicode)
  strA = Left$(strA, n)
  Print strA
End Sub


   不知道讀者注意到沒有,第二段程式碼中的GetSystemDirectory API函式的宣告已經改變了。第一個引數的宣告由一個ByVal字串變成了一個ByRef的Byte陣列,即由宣告: ByVal lpBuffer As String 變成了 ByRef lpBuffer As Byte

   傳遞字串時,需要一個ByVal修飾符來把字串緩衝區傳遞到API函式中,因為字串變數實際上指示了字串內容所在的記憶體地址。在C語言術語中,這代表了一個指向指標的指標。ByVal意味著被傳遞的是一個指向實際字串內容的記憶體地址。而在傳遞Byte陣列Buffer(0)時,使用ByRef修飾符來傳遞變數,它相當於傳遞了陣列中第一個位元組內容的地址。事實上,這兩種結果是一樣的。

strA=StrConv(Buffer,vbUnicode)

   這行程式碼把Byte陣列的二進位制資料轉換成一個合法的Visual Basic字串。

三、 Byte陣列和字串之間的賦值

   為了簡化Byte陣列和字串之間的資料傳遞,允許你在任何動態Byte陣列和任何字串之間直接互相賦值。例如:
Buffer=strA 和 StrA=Buffer

注意:
   當且僅當Byte陣列是動態的,而不是固定大小時,你才可以把一個字串直接賦給一個Byte陣列。

   宣告一個動態的Byte陣列最簡單的方法是在Dim語句中使用空引數,例如: Dim Buffer() as Byte

   當你把一個字串賦給一個動態Byte陣列時,陣列中的字元數將是字串的字元數目的兩倍。這是因為Visual Basic中字串使用Unicode,並且每個Unicode字元的實際大小是兩個位元組。當把一個ASCII字元轉換成一個Byte陣列時,陣列中的另一個位元組將是0。

   向Unicode的轉換是將每個在緩衝區中的字元轉換成2個位元組,從而實際上加倍了儲存在結果字串的中位元組數目,當你認為函式Len(strA)得到的尺寸大小和Unicode轉換後的Ubound(Buffer)函式所返回的尺寸大小相同時,上述特點就不很明顯了。但是,函式LenB(strA)確實返回一個2倍於Len(strA)返回值的數值。這是因為Len函式返回的是字串中字元的數目,而LenB函式返回的是字串中位元組的數目。一個Unicode串的字元長度僅僅是該串中實際位元組數目的一半,這是因為每個Unicode字元2個位元組。

四、 字串轉換成VB字串

   當我們在VB中呼叫Win32 API函式時,如果函式的返回值是一個字串,那一般有如下三種情況:

   1. 函式預先要求你提供一個有固定空間的字串,以供儲存函式的返回值。

   2. 函式的返回是一個以Null結尾的C字串,而不是正規的VB字串。

   3. Win32 API函式有時候會返回另一種型別的字串。這種型別的字串在單個緩衝區內儲存了多個字串值,每個值之間用Null隔開,結尾的是兩個Null,一個Null是最後一個字串值的結尾符,另一個Null是整個字串的結尾符。這其實就是我們通常在C中遇到的字串陣列。

   第一種情況很好辦,只無原則預先定義好一個空間足夠大的字串,然後把API函式的返回值賦於這個字串就可以了。例如,如果你已經知道函式返回值最多不會走過256個字元,可以這樣編碼如下:

DimsAPIReturnas string
SAPIReturn=Space$(256)
SAPIReturn=API_Function(…)


   對於第二和第三種情況,就必須把返回的C字串成標準的VB字串。下面這個函式CStringToVBString把一個以Null結尾的C字串成VB字串。

Public FunctionCStringToVBString(psCStringAs String)As string
'引數psCString是一個待轉換的C字串
'函式返回Null左邊所有的字元

dimsReturnas string
dimiNullCharPosAs Integer

  iNullCharPos=InStr(psCString,vbNullChar)
 
ifiNullCharPos >0then
   sReturn =left(psCString, iNullCharPos -1)
 
else
   sReturn =pscstring
 
end if
  CStringToVBString=sReturn
End function


   下面這個過程把一個含有多個C字串的緩衝區轉換成一個字串陣列。

Public SubMultiCStringToStringArray(psMultiCStringAs String, psaStrings()As String)
 
'引數psMultiCString是待轉換的多個C字串
'引數psaStrings是返回的VB字串陣列,呼叫之前它必須是一個動態的空陣列

DimiNullPosAs Integer
DimiPrevPosAs Integer
DimiIdxAs Integer

'初始化字串陣列
  iIdx =0
ReDimpsaStrings(0ToiIdx1)
  psaStrings(iIdx
1) =""
Do
   iNullPos = InStr(iPrevPos1, psMultiCString, vbNullChar)
  
IfiNullPos > iPrevPos1Then
'把找到的C字串賦值給字串陣列
    psaStrings(iIdx) = Mid$(psMultiCString, (iPrevPos1), ((iNullPos -1) - iPrevPos))
    iIdx = iIdx
1
ReDim PreservepsaStrings(0ToiIdx)
    iPrevPos = iNullPos
  
Else
'找到了兩個Null字元,去掉最後一個,然後退出
ReDim PreservepsaStrings(0ToiIdx -1)
   
Exit Do
End If
Loop
End Sub


   當呼叫Win32 API函式時,使用這兩個簡單的函式,你可以消除很多冗餘的程式碼,加快開發步伐。

注意:
   當你為API的返回值預先分配字串的空間時,一定不要忘了空間內必須包含Null結束符。另外,建議你在使用API時,最好對每個變數都進行宣告,加上下面這句程式碼: Option Explicit

五、小結

   VB6中位元組陣列和字串的相互轉換是程式設計中,尤其是新手使用中最為頭疼的問題。本文歸納了軟體開發過程中使用二者的典型情況及其應該注意的問題,供參考。不當之處還請讀者批評指正。