|
2002-12-05· ·丁有和··yesky
- T0 ~$ b8 a0 I u$ o2 |: `0 l: e
' z8 Z: @: @$ b& }( r Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
5 L# S, F" K& J$ `8 i# ?* ]
; _" B1 I. K8 A$ `4 a* d$ f6 T 一、BSTR、LPSTR和LPWSTR
1 _ Y. ]# @% O# v; d$ l! L
% @$ b: p d1 C. K/ r' O& ^/ r 在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。9 n/ V" T) D' [- g! e; x: ~
! }, `) _; C L: D/ c* b! I- O7 W2 @9 a
那么什么是BSTR、LPSTR以及LPWSTR呢?" V- h6 h- M$ S+ {# G5 v
8 A$ j7 l" B8 D f& S, }
BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。8 @2 }( Y2 H0 F" F1 [
. d2 M% V0 t9 z! M) N/ k2 R
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。/ \. U R4 C5 ]; Q
1 H( W7 M. @$ n5 k! { 例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
' \* f, {; _, @+ |
) R* q- h1 ~& n; B 一般地,还有下列类型定义:
7 D. o0 i- R: z8 X) ^! R# n
, {( J1 T. B# e3 W6 A$ d#ifdef UNICODE
6 k& e9 m2 K- W+ A' ^ typedef LPWSTR LPTSTR;
/ \" z6 e8 }' u typedef LPCWSTR LPCTSTR;
2 G! ?5 s$ [5 R: O) Q" L, P' s#else
1 Y, I8 m. N: k% P. Z typedef LPSTR LPTSTR;
- @' I) r# {9 M& Y f typedef LPCSTR LPCTSTR; # V$ k( X! J0 e5 [- G
#endif . G- i a8 p3 b) h9 E8 T
$ M) h( E7 L0 d2 A1 x$ A5 a m 二、CString、CStringA 和 CStringW, g4 O. r$ a1 D! I& T
' t! a5 Z" M! X% S( D. u# A 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应用程序中经常用到,这里不再重复。$ D f+ h5 A3 h5 V$ P# p
( [( q" g9 E6 ?7 p: s1 c. l 三、VARIANT、COleVariant 和_variant_t1 {# }, |5 J$ w) W* u2 B
9 e X d+ \. i
在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
& U3 |- v" k# M, ]; j# T* G- v
$ d) R! ^: E% h. U* F$ fstruct tagVARIANT {
4 |* R% U& r$ U9 `# k! j VARTYPE vt;
2 |% O& ?4 w$ O9 R; _) }+ s+ }" s union {* O/ y7 l! ]( M+ T: j5 b) w
short iVal; // VT_I2.
+ v2 x1 O, ?/ | long lVal; // VT_I4.* y# x: p4 _4 F
float fltVal; // VT_R4.
7 F0 r1 z+ X$ E* E3 T: p' h double dblVal; // VT_R8.$ R) ]8 G! O# K: J& j8 e% [
DATE date; // VT_DATE.
6 q% m$ { e! e" D BSTR bstrVal; // VT_BSTR.
8 |$ j3 w6 i8 S" } …2 y& T/ y# W. S9 R+ I$ L6 ~
short * piVal; // VT_BYREF|VT_I2.3 d* K; J, K0 V
long * plVal; // VT_BYREF|VT_I4./ y9 Y: G5 u' V6 d- D5 C
float * pfltVal; // VT_BYREF|VT_R4., y. j' Q L3 E h3 f1 L
double * pdblVal; // VT_BYREF|VT_R8.
. E% ?- x) H4 K# p DATE * pdate; // VT_BYREF|VT_DATE.
H/ j3 h$ D0 C# r5 l1 @$ X BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
5 I: n. ?2 h6 U# |! C3 w( k* f };
2 Q! Q4 e, V: A- `9 r, M- S};
. Y2 p, y2 t1 t
m* L" O2 _7 `3 i! c 显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:, K& L% J- z( B+ V
! i! u! y0 N; f0 S6 [. kVARIANT va;# y2 k; X w8 a+ g& F& ^
:: VariantInit(&va); // 初始化% `/ q8 s+ Z$ F0 M3 X. G5 W4 a5 m7 b" V
int a = 2002;4 t; e% x3 |: o) z8 U
va.vt = VT_I4; // 指明long数据类型
1 A* a0 q" a& A0 A0 W- D0 H1 `3 Nva.lVal = a; // 赋值
, S* \# Z) H# u% N! ~+ y1 }
- }0 F( P5 h4 Z E" Q 为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
" V. E& w, A: T- Y* V' R1 ~* p9 a3 O# A( x6 o: e
VariantInit —— 将变量初始化为VT_EMPTY;
: i& N+ F% n# `! [
( g; C2 q2 m7 j0 N3 [' r1 J3 S VariantClear —— 消除并初始化VARIANT;: q% K- m5 j% l' ?( l8 F2 r
" }' x( G& }6 C* D VariantChangeType —— 改变VARIANT的类型;
8 [- F2 B/ |/ h$ D. d4 a0 O, y8 `# z( I% ^
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
- h- M) _# O k6 E5 b
O1 {& ]. k8 A COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码: x* }. h* C1 A% ^. ]5 r& w& E
0 c$ x) O5 e. Z5 A& Q9 \COleVariant v1("This is a test"); // 直接构造
- r0 Y2 N& c0 M' Z T, fCOleVariant v2 = "This is a test"; 0 M# |8 t! a3 H5 G$ _0 y2 u1 ~
// 结果是VT_BSTR类型,值为"This is a test"
; r! x. T3 J7 J0 `# u- |COleVariant v3((long)2002);
4 {# m* I2 ]4 ~COleVariant v4 = (long)2002;7 z: P; |4 X8 B K% R# }- V9 P
// 结果是VT_I4类型,值为2002 0 V) |" N3 u: p$ c
' ]8 H/ S0 [& w* I; X# p. ^% Y( x' i _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:
$ p/ N5 `0 p1 O) I$ }, i" ]# E& l& J0 G( x6 U6 d0 c. V X; X& ^9 G% h
#include "comutil.h"6 n/ q0 U4 b6 U& u8 q. k5 h
2 A) L! J& S1 ?5 j s7 ]9 C+ x0 ^9 @1 B4 r #pragma comment( lib, "comsupp.lib" )8 i, ~. `5 ?9 @$ k0 R
- D8 s/ L8 s1 u& J) b 四、CComBSTR和_bstr_t" e1 a* H. u4 h# b+ `
9 n# }* t3 F2 l. \: U9 L
CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
5 G4 F3 d3 H e% {
/ m+ ]5 K1 a) C6 t2 `CComBSTR bstr1; # \4 n( A7 ~& I+ i! L5 o) |
bstr1 = "Bye"; // 直接赋值9 u* e' G' q/ v) Y' p& E% t
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符& y' u- o: Q, c& @' w) I
CComBSTR bstr2(wcslen(str)); // 定义长度为5
5 B' T, \3 X+ K9 ~8 {' G* I$ a: gwcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中' h& p" x1 G. g( t: p" A
CComBSTR bstr3(5, OLESTR("Hello World")); 1 Q. {7 O0 ?5 u& F H
CComBSTR bstr4(5, "Hello World");
0 `+ j! }* n }% O( @7 m) gCComBSTR bstr5(OLESTR("Hey there")); # [' } O# A( T8 ~: v
CComBSTR bstr6("Hey there");
* J0 [. p( I7 L: d' k# O! FCComBSTR bstr7(bstr6); ( H# G8 c: W$ X c% K
// 构造时复制,内容为"Hey there"
7 o. E1 V [" }6 v/ M. [: s! E0 [ p j6 `3 Q) e7 e
_bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
0 j: r$ w/ d1 k. L% D% Y. J$ Z: [9 _5 v2 ~. C1 c L' ~! k; S
五、BSTR、char*和CString转换8 I/ ]1 |6 P2 A/ Y8 r
0 d! E$ J9 E5 N" \ (1) char*转换成CString% f) O6 ^" W0 a
7 w; d& f; b2 Z1 h
若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
& f! U" h' o4 x }# S
# ]& S3 x, W6 m% R4 b! A) ~char chArray[] = "This is a test";
, j2 Z( x" p! w0 h z0 m6 U l Y: ochar * p = "This is a test";
6 z2 d/ K% Z' |# y7 U1 f, o0 U" S2 m# v# {7 d4 R C
或% n& w7 M: v% P+ H. ~% a
$ _( _8 X/ l& I: |( l3 w+ X2 XLPSTR p = "This is a test"; $ X$ V0 K' B {& r, w
( \! I- F) |* C5 ^$ B4 ? 或在已定义Unicode应的用程序中* r1 D/ M5 G% x& t3 ^. B7 e* ^
2 V1 |9 [+ X' _& D* f" G6 F* _. M& M
TCHAR * p = _T("This is a test");
6 \) x; \' {$ ^+ G4 i
: h2 {: D. g/ G+ @2 G4 t 或* F3 ?+ q/ G* X4 _
7 p+ R( b. Q$ w0 rLPTSTR p = _T("This is a test");
$ c3 I( @* _( lCString theString = chArray;
: r9 X1 W6 h. w* a' t% R, D$ rtheString.Format(_T("%s"), chArray);
9 y+ K1 }+ K5 F( S# b4 ktheString = p;
8 M6 X B+ }" w2 U+ O
- V1 d4 \: s9 A (2) CString转换成char*
8 V" a8 W7 I0 R1 P- L1 _4 ~& r0 ^2 P# t# e7 h
若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
8 p/ @) ]8 p. p
) C3 b9 `6 u' W# \8 r# g$ ~* U! o 方法一,使用强制转换。例如:
. N! I" H1 L, X1 u0 x
6 ]( R6 |0 p- vCString theString( "This is a test" );1 P* v" E# J1 c, P
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString; 4 G/ g3 k( ]% @
$ e3 x4 n( X; R 方法二,使用strcpy。例如:9 q) z1 u- o' Z( t* @, ~
4 x+ H. P" p$ b/ z0 t s7 [- J
CString theString( "This is a test" );
% }8 L% o- P: o8 qLPTSTR lpsz = new TCHAR[theString.GetLength()+1];
' n! L8 r) z; w8 M( [, o_tcscpy(lpsz, theString);
5 o; i9 P: D; |2 o# j4 t( H& L+ O( Y
需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。4 e# c7 n2 t+ a, v2 J) f% I
" _. d4 M- j3 {
方法三,使用CString::GetBuffer。例如:6 x( \* X; o4 b0 _7 D0 D# B
. M/ Z2 Y% ], {3 y0 I4 h& G* d
CString s(_T("This is a test "));1 c3 s. G# a. ~& G
LPTSTR p = s.GetBuffer();$ E. V# C5 T- s. z# t1 E1 l
// 在这里添加使用p的代码
0 A, [# |, J, B$ |# }: bif(p != NULL) *p = _T('\0');% y* \9 n1 G* U* _; \
s.ReleaseBuffer();
$ J' i) i9 d' X9 I( M7 H// 使用完后及时释放,以便能使用其它的CString成员函数
+ \8 r8 |7 v$ V& m: c; l p4 D
. \9 |+ U. ~+ X4 C+ H' |+ o (3) BSTR转换成char*/ G2 K3 K, A, f* `
: H) R2 V( S2 m
方法一,使用ConvertBSTRToString。例如:
3 |! s7 `' s" K5 |: s# l3 X$ k @2 w) v' [
#include " A% k- `3 T. [; w
#pragma comment(lib, "comsupp.lib") K: B8 y! T4 @
int _tmain(int argc, _TCHAR* argv[]){
, t3 h# k2 |% {# EBSTR bstrText = ::SysAllocString(L"Test");
2 I) l6 m$ O: L2 fchar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);+ I" R" M& S( E9 ]
SysFreeString(bstrText); // 用完释放
! ^+ ^) r4 G9 d: vdelete[] lpszText2;; g: z0 c! }1 ~ v' r: L
return 0;
: V8 Q) d1 `( F- _9 D}
# w- A& x( J% Q$ N2 i9 r
5 a/ |, a' g* m& [: x* N" P H 方法二,使用_bstr_t的赋值运算符重载。例如:
) p9 g; S0 `+ m0 ]* E7 v% U' ^% r
_bstr_t b = bstrText;' L% H9 [9 z+ S9 p* t( _3 W, c
char* lpszText2 = b;
e" d1 P$ }" \$ g! m/ A, B- E0 o3 k. C9 p* L Z9 E/ G. C; i2 G
(4) char*转换成BSTR
1 O' [& R4 @" n9 @( L
. W' z9 E! f( C3 G 方法一,使用SysAllocString等API函数。例如:9 C6 G6 O5 h8 ]+ ~/ i e+ q" v6 \/ k
b! ~! \6 }* ^1 f7 [8 S' e
BSTR bstrText = ::SysAllocString(L"Test");
1 `$ L; m& I- N/ J) k& T* jBSTR bstrText = ::SysAllocStringLen(L"Test",4);: P4 p! ?6 V5 s6 i) ?) u
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
9 }- c! B# ^7 V* ~9 I0 x
7 q" u6 r8 o" z' G 方法二,使用COleVariant或_variant_t。例如:
4 M% s4 d4 d5 L* u @
' K9 b: a6 m6 p1 j% \; d//COleVariant strVar("This is a test"); p2 p7 ^: h, D/ H
_variant_t strVar("This is a test");
1 j9 j& r7 ^" v# t# l; f$ n& vBSTR bstrText = strVar.bstrVal;
7 L; T; L% {# s; ~. I% x
9 R5 H4 l r: a8 B R; b 方法三,使用_bstr_t,这是一种最简单的方法。例如:
9 t' d w, o( c* s7 `4 [& R2 }" {% L+ C
BSTR bstrText = _bstr_t("This is a test");
! `$ s$ ?- w) b1 z& ]& P
+ q- m2 r. n( k$ g' x. Z9 x 方法四,使用CComBSTR。例如:
0 c( D) ?2 }: \/ t% P E. o. B
8 X, U: y, T% y& T& K& O) K4 FBSTR bstrText = CComBSTR("This is a test"); r/ l- g8 T& Q
, y7 O9 V1 J3 \0 q" U 或- W; X: p8 P1 @5 l8 i3 z. r* V
8 t4 t5 L% C5 _' D' N" DCComBSTR bstr("This is a test");
% B! R4 k7 a5 u1 |1 O6 g8 UBSTR bstrText = bstr.m_str; / C# I5 a' j% s& c3 p
% ~+ y% W% `; h! Z 方法五,使用ConvertStringToBSTR。例如:
/ x- U3 e# E) Y1 H. e- Q
, ~& n; j$ }6 G+ O% j. uchar* lpszText = "Test";
7 D: k3 t: }+ h$ E& ABSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); ' f3 T2 X) o, I* ^
1 n: I% j1 k. e9 ^9 h7 f
(5) CString转换成BSTR$ M0 }1 q" B' R& e0 \ {4 ^
j8 J( h% e. u8 P: A 通常是通过使用CStringT::AllocSysString来实现。例如:
. p% H1 ^" w6 s+ e5 c# v
( B2 f! K& X9 V n) pCString str("This is a test");
( a: X9 U- K& z8 i4 CBSTR bstrText = str.AllocSysString();3 V; E4 a0 o0 \. a2 _0 W
…3 U& D1 d) v- m. n1 _2 R a' q
SysFreeString(bstrText); // 用完释放 ' M; A/ @ T; q& g2 p4 A6 U
! C, o0 J" m1 j0 }$ V! P) z* k (6) BSTR转换成CString
+ _- \3 x+ s( j; `% Q& r
6 T \7 {8 b' I! l, \ 一般可按下列方法进行:
# h; ^4 q* m, X" [$ _- l2 c4 A; D* O9 x# I3 j; r
BSTR bstrText = ::SysAllocString(L"Test");
6 r7 t1 j1 d z8 v% [0 L _CStringA str;9 U" b/ C) x4 Q. ]1 K
str.Empty();
# V2 ?2 q" [0 z& E* D: G. O' E* _# ~str = bstrText;
+ M9 D& X* V7 E4 D; P5 s2 P0 [$ d; Z$ d, a% O
或0 S7 m; g G+ X3 W3 [$ S
1 n# y3 g! @3 e4 r1 A3 VCStringA str(bstrText);
! C% W. H- }% Q- v( S6 D
! P! n6 Z1 |( R2 u+ E0 Z8 V( Z (7) ANSI、Unicode和宽字符之间的转换
5 X: W F; {* O3 F6 T
9 p* f k$ f' [3 V0 G8 S 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
' L p$ f0 v% H9 L) p
% W" ^3 r! F- T* N) d/ P/ K Y 方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
4 k% t2 {2 J# R$ g% E/ @3 b2 f" |; G+ {: I3 K; o
TCHAR tstr[] = _T("this is a test");
+ b" h2 `7 B& q/ U: J) xwchar_t wszStr[] = L"This is a test";" j f! j; ?0 Y; k& M' v5 V
String* str = S”This is a test”; 7 R0 r/ Z- n" p
7 s- j( F& |* D: i/ j
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
. ^# r+ @* } b- S8 E+ R$ J3 ]5 M2 n2 ]6 Q# E% t8 r8 a& O* q
其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
) N. t% y& d6 }# [$ x0 l" g5 W/ X
LPTSTR tstr= CA2TEX<16>("this is a test");
3 E% |4 m! @/ {" f5 I( l, |$ Q. NLPCTSTR tcstr= CA2CT("this is a test");3 F- T0 c1 i+ \$ Z9 ?
wchar_t wszStr[] = L"This is a test";
# \. M. h6 r' {& [1 mchar* chstr = CW2A(wszStr); 6 M! S% H+ e/ s0 D% J) D
! E) `, d9 B7 m6 a# c {, A+ v 六、结语+ D3 v! m! z+ U) y' B
# N; k- P; ^9 K( z6 q 几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|