ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 9743|回复: 4

[求助] 如何理解VBA内部的Unicode字符串

[复制链接]

TA的精华主题

TA的得分主题

发表于 2012-2-24 01:15 | 显示全部楼层 |阅读模式
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖已被收录到知识树中,索引项:文本处理和正则
本帖最后由 liucqa 于 2012-2-24 01:20 编辑

看了一篇文章,理解有所加强

有关API字符串--API programmer必看.rar (10.04 KB, 下载次数: 89)

这篇文章来自
Win32 API Programming with Visual Basic 的第六章。这本书我在网上只找到英文版。谁有全书的中文版,共享一下,谢谢!
本书中文译名《Visual Basic Win32 API编程》

贴出来一小段供大家学习
对Visual Basic来说,所有的字符是以2字节的Unicode形式表述的。(译者:在Visual Basic里处理字符是以Unicode格式的,不管所处的操作系统是可以以ANSI格式处理字符的Win98还是以Unicode格式的WinNT,但Visual Basic也支持ANSI格式的字符串传送,例如调用一个需要接收ANSI字符串的API函数时,Visual Basic会自动将其内部以Unicode格式表述的字符串转换为ANSI格式字符串传送。ANSI格式的字符是仅以一个字节来表述的字符。)从另一方面来说,Visual Basic使用Unicode格式在字符串中表述字符。例如,ASCII这样表述字符h:&H68,而Unicode是这样表述的:&H0068,同时在内存中则是这样保存:68 00。

    本来,字符串“help”在内存中应该保存为这样:

    00 68 00 65 00 6c 00 70

    注意,由于字符串在内存中是颠倒保存的,所以,实际上“help“在内存是这样保存的:

    68 00 65 00 6c 00 70 00

    好了,我们现在知道在Visual Basic中字符串类string并非我们原先想象的那样。为了避免任何可能的误解,我们把string类型理解为Unicode格式的字符数组,而实际上它就是这样子的。这也有助于我们区别ANSI格式的字符数组。

    请仔细阅读以下几句话,因为这是理解string类型的关键:

    当我们写下这几句代码时:

    Dim str as String

    Str=”help”

    本质上来说,我们其实并没有定义了一个Unicode格式的字符数组。我们其实定义了一个BSTR类型的变量。一个BSTR类型的变量就是一个指向以NULL结尾的,以四个字节为保留字开头的Unicode格式字符数组的指针。

从Visual Basic 4开始,string类型就不同了。新的数据类型叫做BSTR字符串。

BSTR字符串有以下几个要点要强调一下:

    1.一个BSTR字符串变量实际上是一个指针变量。它占用32bit即4个字节,就像其它的指针一样。而且,它指向一个Unicode格式的字符数组。但是,我们不能把字符串与BSTR字符串等同起来。我们必须用它自己确切的名字DD“BSTR“。

    2.一个BSTR字符串变量指向的字符串数组必须由4个字节的保留字开始(保存字符串数组的字节数而不是字符数),由2个空字符结束。   

    3.由于空字符在Unicode格式的字符串中的任何位置都有可能出现,所以,以空字符声明一个Unicode格式的字符串的结束并不合适。因此,在4个保留字中保存字符串的长度是至关重要的。

    4.我们再强调一下,BSTR字符串指针实际指向的是Unicode格式的字符数组的首地址,而不是开头的4个字节。接下来我们就会看到,在这儿这样不厌其烦地强调BSTR字符串变量的特征是为了与马上就要解释的VC++中的string类型作个比较。

    5.图示中的4个字节的length记录的是字符数组的字节数(注意,不是字符数),包括结尾的空字节。因为数组是Unicode格式的,所以实际字符是length的一半。

    在这儿强调一下,一个Unicode格式的空字符其实是占用2个字节的空间,而不是1个字节。在Unicode格式的数组中测试空字符时要当心这一点。

    我们一般在惯例上说BSTR字符串“help”是“一个BSTR类型的字符串”。一般公认为,一个BSTR字符串变量指向的一个字符数组中包括至少两个空字符。

    就Visual Basic来说,BSTR字符串未尾的两个空字节可能没什么用处,但是对Win32来说,它们却是至关重要的。原因在于,Win32版本的Unicode String(它称为LPWSTR)定义为指向一个空字符结尾的Unicode格式的字符串的指针。

    从这个原因上解释BSTR字符串为什么要以空字符结尾就合情合理了。

有关String这个术语

    为了避免任何可能的误解,以下我们仅使用术语BSTR、Unicode字符数组和ANSI字符数组,我们会尽量避开使用string这个术语。如果必须使用String,我们会把它改为Vusual Basic String(就是BSTR)或Visual C++ String(就是以上说过的LPSTR)。

    然而,在Vusual Basic文档里,你会经常看到String这个字,至于它是以上所说的三种字符串中的哪一种,就要靠你自己的去判断了。

    研究String的工具

    如果我们想继续研究String,那么我们还需要一些工具。让我们来看看以下几个工具。

    Visual Basic的StrConv函数:

    StrConv函数是用来转变字符数组的格式的函数。它的语法为:

    StrConv(string,conversion,LCID)

    其中的String指的是一个BSTR类型的字符串,Conversion是一个常量(接下来马上就要介绍),而LCID是一个可选的标识符(在此我们忽略它)。

    我们感兴趣的Conversion参数是以下两个:

    VbUnicode(其实叫VbToUnicode更合适)

    VbFromUnicode

    这两个参数将BSTR字符数组转换为Unicode格式或ANSI格式。

    但是现在我们有个麻烦。我们没有一个ANSI BSTR类型的字符串。定义中已经说过,BSTR字符数组指向的是Unicode格式的字符数组。

    但我们还是可以想象一下,一个ANSI BSTR的字符串应该是怎样的。如同在图6-2中描述的一样,只须将Unicode格式的字符数组用ANSI格式的字符数组代替就是了。

    现在我们说StrConv至少有两种合法的形式:

    StrConv(an_ANSI_BSTR,vbFromUnicode)

    StrConv(an_BSTR,vbUnicode)具有讽刺意思的是,在第一种情况的时候,Visual Basic居然不认识自己的函数的返回值!请看下面的代码:

    s = "help"

    Debug.Print s

    Debug.Print StrConv(s, vbFromUnicode)

    其输出居然是:

    help
    敨灬

   
    原因是Visual Basic试图把ANSI BSTR字符串解释为BSTR字符串。请再看下面的代码:

    s = "h" & vbNullChar & "e" & vbNullChar & "l" & vbNullChar & "p" & vbNullChar

    Debug.Print s

    Debug.Print StrConv(s, vbFromUnicode)

    输出结果是:

    h e l p

    help

    在这儿我们为了让StrConv正常工作,而仿照Unicode格式填充一个字符串数组并传递给Visual Basic让它处理。而这种结果是:一个ANSI BSTR字符串于是有了一个合法的BSTR的解释。

    这表明,StrConv函数并不真正理解或关心传过来的字符串究竟是BSTR还是ANSI BSTR。它只是设想无论如何你只要传递给它一个字符串指针就成了,它的任务只是盲目地去转换这个字符数组而已。我们以后会发现,很多其它的字符串函数都是这样。这就是说,它们可以接收一个BSTR或是一个ANSI BSTR字符指针,只要这个指针指向的是空字节结尾的字符数组就行了。

    Len和LenB函数

    Visual Basic有两个返回字符串长度的函数:Len和LenB。它们都可以接收一个BSTR或是一个ANSI BSTR,并且返回一个长整型数值。以下的代码说明了一切:

    s = "help"

    Debug.Print Len(s), LenB(s)

    Debug.Print Len(StrConv(s, vbFromUnicode)), LenB(StrConv(s, vbFromUnicode))

    输出结果是:

    4         8

    2         4

    这表明,Len返回的是字符数,而LenB返回的是在BSTR中的字节数。

    Chr,ChrB和ChrW函数

    这几个函数的输入参数和输出结果都不相同。刚接触它们时,你可能会感到无所适从。对此,我的建议是:把它们的定义多读几遍。

    Chr函数接收一个在0到255之间的长整型的整数,返回一个长度为1的BSTR类型字符串。此处,BSTR指向的字符是Unicode格式的。所以,虽然长度为1,但实际占用2字节。从最新的Visual Basic的文档来看,Chr和Chr$没有区别。

    ChrB函数接收一个在0到255之间的长整型的整数,但它的返回值是一个长度为1字节的ANSI BSTR类型的字符串。该字符串是ANSI格式,所以占用1个字节。

    ChrW函数接收一个在0到255之间的长整型的整数,返回值是一个长度为1的BSTR的字符串。该字符串是Unicode格式,所以占用2个字节。

    Asc,AscB和AscW函数

    这些函数就是Chr的反函数。其中,AscB接收一个ANSI BSTR类型的字符串,并返回一个Byte型的该字符串第一个字符的ASCII代码。为了证明返回值确是Byte类型的数据,请看下面的代码:

    Debug.Print VarType(AscB("h")) = vbByte

    输出结果是True。从字面上来看,可能你会认为AscB会接收一个BSTR类型的字符,但是,实际上它只认这个BSTR字符的第一个字节,其它的字节就被忽略掉了。

     Asc函数接收一个BSTR类型的字符串(不是ANSI BSTR),并返回一个该字符串第一个字符的Unicode代码。

    Null字符串和Null字符

    Visual Basic允许Null值的BSTR类型字符串。请看下面代码:

    Dim s As String

    s = vbNullString

    Debug.Print VarPtr(s) ‘有关这两个函数,接下来马上介绍。

    Debug.Print StrPtr(s)

    输出结果是:

    1243948

    0

    这说明,一个Null值的BSTR字符串仅仅是一个指向内容为0的字符指针。在Win32和Visual C++中,这种字符串叫做空指针。让我们再来看看vbNullString和vbNullChar的区别。vbNullChar并不是指针,它是一个值为0的Unicode字符。

    请大家不要把一个值为Null的BSTR和一个空BSTR搞混了。请看下面代码:

    Dim s As String

    Dim t As String

    s = vbNullString

    t = ""

    空BSTR字符串t是一个指向非空内存地址的指针。在那个地址保存的是空BSTR字符串的终止符。而且,它前面的那4个保留字节中保存的该字符串的长度信息为0。

    VarPtr和StrPtr函数

    在微软的文档里并没有VarPtr和StrPtr函数的描述,但它们是非常有用的。特别是VarPtr函数。

    请看,如果参数var为有效变量的话,那么,

    VarPtr(var)

    返回的是变量的长整型地址。

    如果str是一个BSTR字符串变量,那么:

    StrPtr(str)

    返回的是BSTR字符串指针指向的Unicode字符串的首地址。


TA的精华主题

TA的得分主题

 楼主| 发表于 2012-2-24 01:15 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 liucqa 于 2012-2-24 01:17 编辑

    如果代码如下:

    Dim str As String

    str = "help"

    注意,变量str在内存中的地址是aaaa,str保存的内容是字符串的首地址xxxx.。

    请看:

    VarPtr = aaaa

    StrPtr = xxxx

    请再运行以下程序:

    Dim lng As Long

    Dim i As Integer

    Dim s As String

    Dim b(1 To 10) As Byte

    Dim sp As Long, vp As Long

    s = "help"

    sp = StrPtr(s ) ’sp是字符串首地址即字符h在内存中的地址xxxx

    Debug.Print "StrPtr:" & sp

    vp = VarPtr(s) ‘vp是sp的保存的内容xxxx在内存中的地址aaaa

    Debug.Print "VarPtr:" & vp

    CopyMemory lng, ByVal vp, 4 ‘将长整型指针vp指向的内容xxxx拷贝到长整型变量lng,并作比较。

    Debug.Print lng = sp

    CopyMemory b(1), ByVal sp, 10 ‘将sp保存的地址的实际内容拷贝给byte型数组b(),并输出。

    For i = 1 To 10

        Debug.Print b(i)

    Next

    输出结果为:

    StrPtr:1836612

    VarPtr:1243988

    True

    104  0  101  0   108   0   112   0   0   0

    在此我们清楚地看到,在内存中BSTR类型的字符串是以Unicode格式保存的。

    请在这几行代码:

    sp = StrPtr(s)

    Debug.Print "StrPtr:" & sp

    后加入以下几行代码:

    Dim ct As Long

    CopyMemory ct, ByVal sp - 4, 4

    Debug.Print "Length field: " & ct

    运行后可以得到这条输出结果:

    Length field: 8

    在此我们清楚地看到,这四个字节的保留字保存的是字符串的字节数,而不是字符数。

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-2-24 01:16 | 显示全部楼层
字符串和Byte数组

Byte数组理所当然地就是成员是Byte类型的数组,例如:

Dim b(1 to 100) As Byte

我们可以使用VarPtr函数得到这个Byte类型的数组指针。

Dim lpsz As Long

lpsz = VarPtr(b(1))  

数组第一个成员的地址就是数组的地址。

记得我们说过一个LPSTR就是一个指向空字节结尾的字符数组。现在我们把数组全部赋值为空:

For i = 1 To 100
b(i) = 0
Next

(虽然Visual Basic也会自动帮你初始化,但是老是依赖它的这种初始化功能却不是一个良好的编程习惯。)


在Byte数组和BSTR字符串间相互转化

要将一个BSTR字符串转化为Byte数组,我们有两种不同的方法。Visual Basic风格的方法如下:

Dim s As String
s = "help"

Dim b(1 To 8) As Byte

For i = 1 To 8
b(i) = AscB(MidB(s, i))
Next

另一种C++风格的方法可以是:
s = "help"
Dim b(1 To 8) As Byte
CopyMemory b(1), ByVal StrPtr(s), LenB(s)

不管是哪种方法,我们都得到:
104  0  101  0  108  0  112  0

从上可见,在每一个Unicode格式的字符串中,每个字符都是颠倒保存的。
另一方面,如果想把一个Byte数组转化为一个BSTR字符串,Visual Basic提供了几个函数帮助我们做到这一点。如果b是一个Unicode格式的Byte数组,那么我们就可以直接在代码中这样写:

Dim t As String
t = b

而如果是ANSI格式的Byte数组就这样写:
Dim t As String
t = StrConv(b, vbUnicode)  

TA的精华主题

TA的得分主题

发表于 2012-4-1 13:47 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2012-12-10 22:20 | 显示全部楼层
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-24 19:48 , Processed in 0.037790 second(s), 11 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表