|
|
2002-12-05· ·丁有和··yesky
" {/ l/ l; u- X" q5 E3 b. F9 ~8 o 4 }) W/ D) I0 z
5 C( S: }; y0 s$ K6 z/ ^ Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。3 J# R( N. K3 Q; d# k; ^$ d a0 G0 s8 ]
/ U7 T, I7 E8 E! s% c" F& q0 e 一、BSTR、LPSTR和LPWSTR
! N/ i9 j. g5 F% l
! f8 ]0 q l7 L! K, `: ]0 p0 U" B 在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
* h y: N) P) q& W" j! L; k' E7 z: c% z% g$ p/ ~
那么什么是BSTR、LPSTR以及LPWSTR呢?
6 b! f2 i3 n. `4 j0 g. E5 D/ t. B8 N; K& A' A
BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
, T. {1 l6 ~5 j# V0 ]
) Y( B: Z8 i" O$ H" e% e: ]4 C* [ LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
8 S) O4 k5 _/ V% P; A; w$ p8 @# R8 ]
j1 f( n1 B3 ]7 W8 H' _ 例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。" I+ h D+ h+ w
- B% a8 F' F9 y6 B8 G5 N* @
一般地,还有下列类型定义:
3 W6 x4 b) j( R, p5 Q) j9 d8 ]6 ~1 o
#ifdef UNICODE ! J* Q q: F' f
typedef LPWSTR LPTSTR;
) T% `1 }: R: h$ k1 N, J typedef LPCWSTR LPCTSTR;
+ G4 v. p" G4 A) Z#else
% |+ M+ \" f. \* C typedef LPSTR LPTSTR;
" i2 A: J- Y* T/ U# E! e) p typedef LPCSTR LPCTSTR;
. H5 p* q; t( }# s s#endif 7 u1 N/ C' E' t# w$ I; V! H
# N \. l, n3 _1 | 二、CString、CStringA 和 CStringW' O& d+ j2 ?1 r$ J4 i( j
4 a- U# @8 B# J% {2 d% l. o Visual C++.NET中将CStringT作为ATL和MFC的共享的“一般”字符串类,它有CString、CStringA和CStringW三种形式,分别操作不同字符类型的字符串。这些字符类型是TCHAR、char和wchar_t。TCHAR在Unicode平台中等同于WCHAR(16位Unicode字符),在ANSI中等价于char。wchar_t通常定义为unsigned short。由于CString在MFC应用程序中经常用到,这里不再重复。
% f5 ^, }. ^6 [$ p7 o$ h! i, P
三、VARIANT、COleVariant 和_variant_t3 ~6 ?( F I6 K8 T5 O! g! @
' K/ i! w) y, D3 J+ m ?: E) ~
在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:; D1 u5 A1 f) Q: f3 T$ {) \
! J0 @* @; C2 l5 ystruct tagVARIANT {
( F% H( Y4 A% E& } VARTYPE vt;% F: H* }4 r/ @# v5 A2 O
union {
. {8 N( t+ b5 _- E short iVal; // VT_I2.$ p7 {" A# c# z9 Q& D
long lVal; // VT_I4.* {4 f; W4 M3 P- Z8 Z B
float fltVal; // VT_R4.
! x/ j* }2 K9 H- b9 k double dblVal; // VT_R8.
) }. G9 t; o; B6 v DATE date; // VT_DATE.
' j6 e# g0 r/ _& U BSTR bstrVal; // VT_BSTR.# P& @+ ^1 V0 f5 P% \
…0 F0 C% |" u+ t* t+ z
short * piVal; // VT_BYREF|VT_I2.
/ E J$ R6 O1 j long * plVal; // VT_BYREF|VT_I4.5 B) O8 L4 b6 j; {
float * pfltVal; // VT_BYREF|VT_R4.
* d( N& p# {0 ?8 l+ w double * pdblVal; // VT_BYREF|VT_R8.
/ ]" Y% H2 S) n, @( P5 r/ k& h0 c DATE * pdate; // VT_BYREF|VT_DATE.8 }; D9 H& R B" f+ r
BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
7 a/ {% p$ @/ p7 m) l* y };
+ V. F1 g% i# m1 [0 l* `}; A7 L8 a3 d4 I) h1 Z9 t; Q" u
6 j# E `, R+ x/ ~ 显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
6 y& O' D6 e' f% B V& @2 I
. G4 C* V6 r$ |5 hVARIANT va;
* _/ i ]2 |1 j; \- f/ S2 ~:: VariantInit(&va); // 初始化0 Q' g" ?( f' J( E% w
int a = 2002;
# A' s! ^& e! A t1 E3 R, M$ m$ Vva.vt = VT_I4; // 指明long数据类型& D* s2 T' c8 r% a7 Z: O
va.lVal = a; // 赋值 : L/ j9 n$ x+ z
7 u2 o, D) [5 K& z7 G2 u- X
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
- a. V3 j2 h, ]: _
% [2 s1 @5 q$ E* |( J VariantInit —— 将变量初始化为VT_EMPTY;& v2 ]- U& ?) \: q
4 e0 _7 o" }5 k1 Z- K VariantClear —— 消除并初始化VARIANT;/ T; \1 z0 c1 v6 u7 R- T6 d, K
1 T3 k7 X( d) a VariantChangeType —— 改变VARIANT的类型;6 x7 b* M& }# k9 t
2 V! O; e Y5 A2 [( `
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
+ Y9 [1 V- b0 T+ h3 e7 P/ v
: c \4 a9 F R* X+ _ COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:% Q( N; H2 ^; E5 y
4 z9 K) j* V; l5 a. LCOleVariant v1("This is a test"); // 直接构造
; o, i! A4 u% K2 bCOleVariant v2 = "This is a test"; % V9 S" c4 j& a* F, @- Q( |
// 结果是VT_BSTR类型,值为"This is a test"
: D" V. _+ D5 R' l+ [COleVariant v3((long)2002);5 u- ^2 F9 r. a) U5 s4 D
COleVariant v4 = (long)2002;
$ c- C) @* ^8 J// 结果是VT_I4类型,值为2002
- [8 B' ]* Q5 @/ g0 w0 |2 ?
7 J' x( I. o" q5 s8 _ _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:# U: z- ]& |: G! n) m, k
7 \; P1 z2 s7 t* `+ ?
#include "comutil.h"
& }9 O& b* _+ S3 N* H5 U
& J2 m7 l, v2 L/ M8 R( x \ #pragma comment( lib, "comsupp.lib" )! g( L+ ^6 y; R
' {6 n/ Y( b' J* h4 a0 p 四、CComBSTR和_bstr_t
% ?& s+ F# ]" W3 B8 m j( l8 ]. d% ?2 `- f# b0 H
CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
( y* u+ F3 P) @' Y* J) b( ^& r+ `6 D4 ?- x5 Y3 b4 `) }
CComBSTR bstr1;
. h4 q9 B3 M- Xbstr1 = "Bye"; // 直接赋值; p( w* K4 `) `7 e3 y7 G# q
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符/ |. X' M9 a+ y: Q" G& Q j
CComBSTR bstr2(wcslen(str)); // 定义长度为5
3 `" G( s$ F. {4 j* s, `wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
. o0 F# Z6 @, t j1 Z* dCComBSTR bstr3(5, OLESTR("Hello World")); . @9 Q3 ]1 f6 N, T
CComBSTR bstr4(5, "Hello World"); ) c/ B* X4 T& ~7 [) w4 a
CComBSTR bstr5(OLESTR("Hey there")); & y( F4 U4 S* C1 z' l9 B. S
CComBSTR bstr6("Hey there"); 5 i, h7 X# T$ [, J/ G; ^/ c
CComBSTR bstr7(bstr6); + n; _, b. z% e- `* R2 X: E8 _9 N3 r$ y
// 构造时复制,内容为"Hey there" ) {( f B4 B' ^
. k+ w, g8 m* z! Z0 Q5 H7 | P' F
_bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
2 a% m0 G0 M$ U1 G2 \
v7 o, @8 J# _7 q/ u 五、BSTR、char*和CString转换3 A' l- G# P" k8 E$ W# Q
4 y/ W4 W, D { (1) char*转换成CString
; X8 f1 c/ ]' Q9 h& L7 l
3 s5 T: a. K% C2 Y; J" I+ s$ J 若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:' e; [8 L0 Q! e/ C* v# |
& W5 E9 a- N. I5 P) w
char chArray[] = "This is a test";
" p( w3 Y! s& H9 n- q) \char * p = "This is a test";
1 `$ u1 | T8 O( c9 Z5 c3 e/ Z# | e8 R' T) S( S* S* d
或; q% N4 `0 p$ X) T
& y" O% e& J% P7 w! HLPSTR p = "This is a test"; - M$ q( D6 b9 X' s3 _! H8 e
) w2 g4 R6 p5 t: K 或在已定义Unicode应的用程序中8 e$ M+ C4 ^; i; S8 O
' ^; R1 O6 y0 v6 ~- G
TCHAR * p = _T("This is a test");
1 \1 A5 P0 [3 {, X! h3 Q2 w' ]9 g3 W1 D# v$ H
或% H/ F" }" g& ]; m
3 A' Z# g. o. M$ P
LPTSTR p = _T("This is a test");
( K7 m `* [0 J* Q# YCString theString = chArray;
$ f+ w7 P9 A% F4 m, q. m" ktheString.Format(_T("%s"), chArray);8 S4 e: c) O3 i: l) ~
theString = p; ! Z( \, @3 I0 q/ M. `. Y1 V
. r. J) h. ?7 C8 Y: `# G: ` (2) CString转换成char*
$ s1 y6 G6 B6 W: A
6 i+ a1 @+ G; p9 p3 ?& X 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
% \2 ]0 X( u6 n, J9 C! T9 a+ {! h/ `8 W: }8 L1 r- q3 s$ t2 B
方法一,使用强制转换。例如:
- X5 Y, X, h" a( I
) O" p1 R8 I% S3 t9 u1 x# iCString theString( "This is a test" );) ?6 c& Z# F$ r _# s, p
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
6 P2 |7 Z: U. t: A) u" A* T( U4 J; W/ L5 R. q& P! w" V
方法二,使用strcpy。例如:2 q, A0 u; C* Y/ v$ P, |4 C- t
$ }: b. Y! U/ B' p" h$ D8 PCString theString( "This is a test" );3 _ C$ Z( @( r! m
LPTSTR lpsz = new TCHAR[theString.GetLength()+1]; U1 c3 I% F% L6 Y% P
_tcscpy(lpsz, theString); : a# ?8 E+ d) E3 h/ b
; ?$ G. K1 L0 a: s0 E! Q
需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。9 ]6 H8 B0 y7 C) {
9 m0 M' V) s) _9 u; I% o 方法三,使用CString::GetBuffer。例如:
- F9 o+ E! |- h0 v& g1 R0 J
% T: `, v, S3 j( v J8 B( j% s1 k' eCString s(_T("This is a test "));1 u+ D, `, h8 Q" z) ]6 B
LPTSTR p = s.GetBuffer();0 w6 E( t: R$ A
// 在这里添加使用p的代码8 g8 `& A8 l& b Q3 j" I" _( o( L
if(p != NULL) *p = _T('\0');% E+ x! U$ d' R6 C' H7 u, M- T ^
s.ReleaseBuffer(); 4 s1 H0 m1 c/ t$ M" `' b
// 使用完后及时释放,以便能使用其它的CString成员函数
Q' E) A) a: C$ b7 y' P
8 X6 `3 Y: u6 M4 Z- |) j (3) BSTR转换成char*
7 M" P% {9 y9 y; }2 \; a! F
& ^( G' Y9 I% A* A 方法一,使用ConvertBSTRToString。例如:( M; n7 B0 X2 F, F" C) o
. `4 y9 X. w& d* G- r2 D M
#include
/ T1 N# ~: C. ]: ]: l; V6 u" x#pragma comment(lib, "comsupp.lib")% m+ r3 @0 y8 w, r( P
int _tmain(int argc, _TCHAR* argv[]){; G1 K. x! o5 Q9 `; c) H e" u
BSTR bstrText = ::SysAllocString(L"Test");; K, B1 }3 z p
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
! J1 y* o4 Z9 kSysFreeString(bstrText); // 用完释放6 |; \- F- C I
delete[] lpszText2;& N5 V* @( D1 P( x
return 0;8 z& @9 i" j' d" M$ k
} & G! i; R' \+ _: }% N9 b" X
' l: E+ J5 X7 Z
方法二,使用_bstr_t的赋值运算符重载。例如:
1 x0 |9 j/ p5 W) k0 X! _7 B# F& F( ~/ q. O0 f
_bstr_t b = bstrText;/ V3 E$ R5 A: F$ z
char* lpszText2 = b; ; w3 Y$ v. g6 p( o# k# Z4 W
5 u! u4 \; L5 M3 l (4) char*转换成BSTR
. K6 K' M6 ?* z' P( m; m3 u) q$ |; @/ A+ t
方法一,使用SysAllocString等API函数。例如:: }, C4 t. O; @$ V3 B3 e% b
- F) b0 z! W' X' V( R
BSTR bstrText = ::SysAllocString(L"Test");
# i; N' b0 p# a, p6 nBSTR bstrText = ::SysAllocStringLen(L"Test",4);
! M( F3 T7 u; R' H% ^BSTR bstrText = ::SysAllocStringByteLen("Test",4);
: D5 x& |: z6 c7 l p" o& n. `7 i7 {) R! O. W2 C& M: F
方法二,使用COleVariant或_variant_t。例如:
" l; \: ?, _" I E) t
9 g6 x7 o2 E* ?* w//COleVariant strVar("This is a test");
/ l9 u9 N, a3 e H, c! `_variant_t strVar("This is a test");
2 K' U) x! g# X# a `6 ?BSTR bstrText = strVar.bstrVal;
: b. @1 D( Z2 g6 _) x% |
/ f/ P# R" M p 方法三,使用_bstr_t,这是一种最简单的方法。例如:
2 S* u* f6 r$ j" z3 d
+ Y- J6 ^: c2 \7 TBSTR bstrText = _bstr_t("This is a test"); . E7 i8 [8 q/ A0 Z1 L
3 L8 P2 O- r& |3 G: s( A
方法四,使用CComBSTR。例如:- @) @: b- A9 z" X
: |* p% [2 f. Q+ vBSTR bstrText = CComBSTR("This is a test"); , o. a$ k+ {) x+ v
: r3 O- O6 B. u9 t 或
, E" q* `# j: O" P* R) T1 A S5 P& c7 _
CComBSTR bstr("This is a test");
& @; B1 Q6 F3 EBSTR bstrText = bstr.m_str;
|- q2 Q; r' q5 a9 y" @6 M! k4 p& I& g
方法五,使用ConvertStringToBSTR。例如:
* T, W1 P# d8 _- n, n V9 }
2 { M" L5 h! j5 [0 C4 O& \, q2 _+ pchar* lpszText = "Test";% s: ^$ N+ B' Q$ F% v+ V' E
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
* @" b a* s; W' d* z8 O3 \/ c$ u) I3 Q
(5) CString转换成BSTR- K( r+ R/ R; Y; [
0 O0 H$ o( ^1 j" m; o 通常是通过使用CStringT::AllocSysString来实现。例如:2 B9 m0 h3 ~' L7 D j j) J h
: j( z( p- C# d% c( T& O; F t; ?CString str("This is a test");
9 @- R) d Q5 S4 c7 E5 }; W* \BSTR bstrText = str.AllocSysString();
! ?+ Y$ c5 u. o1 n…9 p8 c% a# C% K+ y5 ]' s/ u
SysFreeString(bstrText); // 用完释放
' ], F- Q1 _! l8 C" K: g% U9 K# `6 o# o* I8 E$ ^! c
(6) BSTR转换成CString
. P& C1 L' J, [6 ~' U( i6 J( L {
一般可按下列方法进行:
( ] B+ [ [. [" p+ l* G0 k% O8 v# l: f8 c! q B/ z7 L
BSTR bstrText = ::SysAllocString(L"Test");% p* H2 t8 F( Q! s. F z; l
CStringA str;
# N. o, s2 I* [& }str.Empty();+ y" z6 ], S* w I4 d
str = bstrText;
. |% U, [/ b9 d/ D2 [; Y8 }- a, w- }, e/ P3 V2 R
或, G- U# m' I5 ?* G% r
+ r1 h; d! [6 h* {5 z: t
CStringA str(bstrText);
' V V2 W; v! ]- J5 ^; z* o
3 _" Y$ r2 |; O8 \5 I+ { (7) ANSI、Unicode和宽字符之间的转换& s) m! u9 v$ u; c3 J+ j
( Z$ y, F- g e
方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
0 r: Y0 T ^) A) K8 m# C9 w o. N" Z9 z- X% L
方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
( c' p. `9 }& K" q( b0 Z* Z# J, x6 [5 I Z
TCHAR tstr[] = _T("this is a test");! Y: s% ?+ g1 \) v# [1 I
wchar_t wszStr[] = L"This is a test";; m6 p7 S( C' ~* U9 q" [9 q# l
String* str = S”This is a test”;
' B7 v! Y) p8 j+ k( [8 ^( F3 L; e' I+ W0 i, ^7 Q
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:& W% [1 O8 v" [8 I# z
9 t i, Z" f: f( e 其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:2 T! M) |' h5 g* m% f& j9 c4 i5 L
; W* A2 d4 N5 `$ \LPTSTR tstr= CA2TEX<16>("this is a test");4 T n. @. K# k/ Q& F
LPCTSTR tcstr= CA2CT("this is a test");& S: j8 @1 e ~% |7 u7 v: j
wchar_t wszStr[] = L"This is a test";
6 t( q2 Q1 u( s* zchar* chstr = CW2A(wszStr);
2 Z7 S8 F& |; r5 x
1 v9 E8 W: o( i: v5 j/ H) |0 ] 六、结语% r" w, ]8 c! `! \; }- `
5 s+ o7 j9 e9 D. ^$ q 几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|