分享
 
 
 

VB中String的用法及原理

王朝vb·作者佚名  2006-04-16
窄屏简体版  字體: |||超大  

在各种不同开发语言中,字符串类型显然是最常见,也是最常用的。

常用代表它最易用,是这样吗?未必,越简单,越普通,你会忽视,内里隐藏着的陷井更容易使你中招。它往往是绊脚石,或者程序中性能的瓶颈。

本身,我对VB语言及相关应用并不太熟,只不过近期编码用到,有些体会。

一: 先来总结一下,常用编程语言的字串表达方式:

C: char(wchat_t) * 或 []: 字符数组来表示字符串,以0结尾,无长度标识。

配一堆操作函数,不好记,不好用。

C++: std::string basic_string<>的特化版本. 注意:其内在的数据区由默认的内存管理器分配。

对象哈,提供还算好用的方法。

MFC: CString 标准的C++类. 数据项:LPTSTR 一堆可用的方法。

Windows普通API DLL: LPTSTR 采用C标准的0结束字串

Windows扩展COM中: BSTR OLE标准, 头上带长度标识的字符串.

Java中: java.lang.string

VB中: String 实际上就是OLE标准的BSTR

好了,我们关心的是BSTR。

看看BSTR到底是什么东东。

1.0:看看在Windows SDK中的定义:

typedef OLECHAR* BSTR;

typedef WCHAR OLECHAR;

typedef wchar_t WCHAR;

所以,它实际上是宽字符指针。

1.1:它的内存结构呢?很容易查到资料。

前置四字节,内置字串的长度,后面是字串内容,原则上并不以'\0'结尾,长度由前置值决定。

所以,它又不简简单单就是宽字符指针。但从基本类型定义上来看,它与宽字符指针是可以划等号的。

1.2:那VB中的String也就明白了。实际上是地址,是字串的首地址(注意:不是长度的首地址)

另外,String是可以装载中间带'\0'字符的字串的,只不过,一些显示函数可能将其省略。

如:dim str as string

str = "ab" & chr(0) & "cd"

MsgBox str '输出:ab

Debug.Print str '输出 ab cd

1.3:可以看出,它很特珠,Windows提供几个函数,用来分配和释放它。

分配:SysAllocStringLen SysAllocString

释放:SysFreeString

取长度:SysStringLen

注意:分配得到的BSTR,实际上仍然是以'\0'结尾。SysStringLen似乎有点儿英雄无用武之地,呵。

比如:SysAllocStringLen 的说明文档:

Allocates a new string, copies cch characters from the passed string into it, and then appends a null character.

比如:SysAllocString,我们可以通过例子:

BSTR bstrVal = ::SysAllocString(L"abcd");

for(int n=0;n<::SysStringLen(bstrVal)+1;n++)

{

TRACE(_T("\n%d"),*(bstrVal+n));

}

::SysFreeString(bstrVal);

输出:

97

98

99

100

0

仍然有0。

1.4:VC中的用法:

无论用MFC,还是ATL,实际上,由于BSTR并不是基本类型,而它的相关操作函数也是沿用的以'\0'结尾的函数,所以,虽然它在字串中可以带'\0',但实际上,经过相关操作后,'\0'后的内容会丢掉,这要小心。比如,我们看看CString的构造:

CString::CString(LPCWSTR lpsz)

{

Init();

int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;

if (nSrcLen != 0)

{

AllocBuffer(nSrcLen*2);

_wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);

ReleaseBuffer();

}

}

小结:BSTR有长度标识,但是MS在实现时,为了兼容以前的字串操作,再加上BSTR并不是基本类型,所以,它的分配函数和一些操作函数都是把它当作'\0'结尾来处理。千万要注意。最好也不要用它来装载中间带'\0'的字串,因为,说不定什么时候,它就丢掉了。

二: VBr的应用中将麻烦的地方,应该集中在以下几个方面:

2.1: VB调用Windows API DLL

2.1.1: 字串作为输入参数

这还用说吗?

在API中如果是LPTSTR,在VB中直接转为Byval s as String

原因? 为什么 BSTR可以直接转为 LPTSTR ?

前面其实已经讲了,再说一次吧。

BSTR实际上是指针,指向字串头.所以,采用ByVal指向的刚好是字串的首地址,也就是LPSTR,嘿嘿,刚刚好,符合需要,真是巧啊。

2.1.2: 字串作为输出参数

这个麻烦一点,举个例子吧.

如:

UINT GetWindowsDirectory(

LPTSTR lpBuffer,

UINT uSize

);

使用API Viewer得到声明

Public Declare Function GetWindowsDirectory Lib "kernel32" Alias "GetWindowsDirectoryA" _

(ByVal lpBuffer As String, ByVal nSize As Long) As Long

代码:

Dim sTemp As String * MAX_PATH

Dim lsize As Long

Dim str As String

lsize = GetWindowsDirectory(sTemp, MAX_PATH)

str = Left(sTemp, lsize)

MsgBox "Windows路径:" & str & "长度:" & Len(str)

注意一下:

A: 引用的Windows API是A版,而不是W版,如果是W版会有错误.

原因,我的猜测是: VB的API调用机制中,字串强制做了UNICODE到ANSI的转换.API的DLL没法告知调

用端,它是Unicode版还是ANSI版.如果是OCX或COM就没问题了,因为它们有标识。没办法,只能用一种了, MS选了A版。

B: 参数是ByVal

道理同作为输入时,实际上传的指针,所以,指向的内容是可以修改地,也就可以返回了。

C: 根据API的使用说明,可得到

[out] Pointer to the buffer to receive the null-terminated string containing the path.

需要调用方分配空间,因此, sTemp需要给足够长度的空间.

D: 返回的0结尾的字串,可以用left达到目的.

因为:VB的String对应的是BSTR类型,长度由前置字节决定,而不是0字符决定.

2.1.3: 字串作为Return值

这种API是不是存在呢?系统级的没看见,一般不会这样用,因为这存在内存分配的问题.

但我自已好像写过,处理方式是:

VB声明中可以使用long返回,然后用lstrcpy进行转换,

说起lStrCpy就不得不说说它的作用了,它实在是很关键,它很神奇,它可以把

地址指向的字符串拿出来。

我们来看看原理:

lStrcpy本身只是简单的复制字符串的函数:

Public Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyA" (ByVal lpString1 As String, ByVal lpString2 As String) As Long 如果按照API View中的声明,是不行的.它确实就是字串拷字串,没办法把字址指向的字串拷到另一串.

好了,需要小小改动一下:

首先:目的地没错,确实应该是指向字串的首地址.用Byval String就可以.第一个参数OK。

源串呢? 因为你传入的实际上不是String,而是long啊(上例返回的是long噢),那当然应该将声明改成long,否则,传入的String会变成啥,啥都不是呢?就这么简单。

2.1.4: LPTSTR 都可以用VB的byval String替代吗?

如果在结构体里,可就不是这样了.

如:EnumForms 枚举某个打印机的所有打印纸型。

Public Declare Function EnumForms Lib "winspool.drv" Alias "EnumFormsA" (ByVal hPrinter As Long, _ ByVal Level As Long, ByRef pForm As FORM_INFO_1, ByVal cbBuf As Long, ByRef pcbNeeded As Long, _

ByRef pcReturned As Long) As Long

对于 FORM_INFO_1的定义:

Public Type FORM_INFO_1

Flags As Long

pName As String

Size As SIZEL

ImageableArea As RECTL

End Type

如果按上述的方法调用,会出现异常.估计是在拷贝数据时,String实际上是不同于LPTSTR的,因为它的前面还有四字节是标识长度的,如果定义为String,那在构造pName之前的长度,必将动到它不应该动到的地方,这样,就错了.想来不会异常退出的,但却退出了,不知原因(我没弄明白)

所以,此种情况,应该将pName变为long,然后再用2.1.3提到的方法,将字串拷贝出来使用.

这样,就OK了。

引申一下:

对于一些API中大的结构体中的指针类型,我们完全可以用long来取代它,然后传递0&,因为,大多数情况,我们是不需要传入任何参数的,避免定义很多我们并不需要的类型.这样就降低了API使用的复杂性。

2.2 : VB调用COM组件(或OCX控件)

这似乎没啥可说的,COM组件的接口一般都是标准的BSTR,与VB的String完全兼容.

VB调用端没啥问题,倒是书写COM的程序需要注意:

2.2.1: 应用端可能会传入空指针.比如传入vbNullString,这需要留意.

2.2.2: BSTR的释放与普通LPTSTR可不同,使用前需要初始化,使用后要释放,当然,VB无此烦恼.

3: VB字串的连接操作.

当我们要序列化生成文本文件时,大都喜欢先将内容写入字串,然后再一次写了文件.否则多次写入文件,似乎有效率低之嫌,但如果字串为多次累加而成,那么使用VB字串的连接操作,将是效率极差的做法,这也变成性能低下的重要原因.

分析一下原因:

对于String的连接,系统的做法一定是重分配空间,搬移到新空间,拷贝进新内容.如果连接次数较多,且较细,那将是灾难性的。(类似MFC的CString的做法,假设我们要写一个100K长度的字串,但每次添加一个字符,那你可以试试它的速度)

如何解决?

很简单,采用预分配内存块, 当内存块不够用时,自动增加指定块的增量(当然每次增长的内存块这个阀值需要慎重考虑)

这样,减少搬移操作,效率自然提升。

具体实现:可以使用内存操作API实现.VB会麻烦一点(需要用API生成全局内存,自行完成搬移,拷贝),

VC可以直接使用CMemFile简单搞定.

4: VB字串多语言化

4.1: 一般的做法是: 将原生串与多语言串做成Key.Value Pair结构。多语言信息为Value,然后做一个简单的Hashc ode来索引,或者干脆用Hash Map来实现 (为了更高效,更容易使用已有容器,最好借助于C++编写相关支持)。

4.2: 为了使UI上的字串也能多语言化,需要保证字串信息为动态加载。

4.3: 对于控件的字体编码字符集也需要处理,以保证正确显示。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有