|
|
2002-12-05· ·丁有和··yesky
" k4 z; t9 T. k+ p5 d- N" o % x0 ^4 d* d. o# F# L; W! ?
7 O. O( z5 T4 o4 N Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。5 W) s' i* k- h* E( q4 Q8 a
. l0 R2 n' ]/ c" o! m% z. Y
一、BSTR、LPSTR和LPWSTR: o7 }2 K G$ S" i! G" M
- v! Y* g8 R A8 O& I4 ?4 P
在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
* Z/ i: r" q( Y; s/ ~5 s0 |2 l9 O& V% H* m/ y t
那么什么是BSTR、LPSTR以及LPWSTR呢?
# @5 e6 A, R- C; Y' `9 B0 q7 Q( e* F- Q5 J. Z) x2 K
BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。- k Y: Z: w7 _
! L& s4 O7 `+ ~- r M, c$ U C1 k
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。# _0 w& l; F- a, g5 K
& \: r U. S2 a3 y! c
例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
# F7 C2 q; Z) E% ]3 w0 {: i2 e9 J2 Y5 K6 m
一般地,还有下列类型定义:
7 J* h" H! ^. t- `0 s: H& _% A& J. i( }) { S8 q* o
#ifdef UNICODE
2 x+ R; k6 N ]' x0 |3 F* w+ o typedef LPWSTR LPTSTR;
. r% D# y/ _+ T8 A typedef LPCWSTR LPCTSTR; 6 V& c0 U% M2 I! P2 e0 D
#else $ ^- E( q+ G3 v
typedef LPSTR LPTSTR; ( R3 ~2 O3 a& F4 ]" ?9 N
typedef LPCSTR LPCTSTR; 7 Q. e) e0 w: j* ~# C* ?
#endif
( D0 a2 v( q$ U
8 z; I. e8 l3 M7 F 二、CString、CStringA 和 CStringW
: j+ B2 W) x! D/ ^5 e4 p. X" C+ ~$ r3 f
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应用程序中经常用到,这里不再重复。/ m" ?( y d- T( X1 {
1 j9 k. s+ z+ O" x; q7 O0 \
三、VARIANT、COleVariant 和_variant_t
3 n# @! k& G" ]# v. |/ k s; U: U
在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
5 g% y# Z! K5 w X+ {, C' j& ?0 \' H* b
/ T6 f* G0 t; c& H$ H' h: g" istruct tagVARIANT {4 V7 V' m9 z& z4 |5 d/ k
VARTYPE vt;
. @% k/ ~) r/ ?2 o; H8 } X/ { union {
4 [! s' C* t/ ]8 V: s- ?& u short iVal; // VT_I2.
. \* I2 E# j8 x# z long lVal; // VT_I4.
) C7 N& r$ i) {# \! |5 ~5 q% C float fltVal; // VT_R4.6 f, |& k% v O8 N5 m* a
double dblVal; // VT_R8.8 @2 g2 W2 V& t/ S4 E
DATE date; // VT_DATE." ^& Y. x+ `" n# W; Q6 h
BSTR bstrVal; // VT_BSTR.- ~/ d" j# X% Q0 D: R1 a
…
0 [% q9 v+ t8 i: M8 B short * piVal; // VT_BYREF|VT_I2.( O/ _! ~. }- U- L* o& r
long * plVal; // VT_BYREF|VT_I4.
! @1 U S" m. R. t float * pfltVal; // VT_BYREF|VT_R4./ V; |: K) ~( n# F% [
double * pdblVal; // VT_BYREF|VT_R8.
9 R( T% l9 j+ q% b8 r9 s DATE * pdate; // VT_BYREF|VT_DATE.
c- |; a4 q' P) C+ h- D BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
" H8 w$ I5 L. f; U* ?' c };* B2 N: `0 @- T" Z6 i& S
};
- U1 F0 P7 \% z1 n5 Z
/ C3 k8 g3 C4 i, C- r 显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
* L/ I' D- K7 Z H( U6 o6 I) b7 t4 U
VARIANT va;
8 A& i# X9 K" e:: VariantInit(&va); // 初始化2 A1 q: w2 W! b6 I+ F* h
int a = 2002;: H8 c# l5 R9 e' h- f- T" N/ {
va.vt = VT_I4; // 指明long数据类型) {, w( `0 C7 G
va.lVal = a; // 赋值 . E9 P1 `7 R! F4 K C
9 j: |+ ~* r3 F9 ]0 I9 t1 Q: M
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:" c" {" K( L( P' R
! g( O, i5 o _' Z
VariantInit —— 将变量初始化为VT_EMPTY;
1 ?1 f/ U( ~ D- i2 D: ]) s0 L/ x* [1 S3 M5 x
VariantClear —— 消除并初始化VARIANT;) g# s$ Z0 e0 q6 V2 Y
# O( V: u% v6 _/ o, w! R VariantChangeType —— 改变VARIANT的类型;
5 n5 W: W! V6 t4 u2 r1 i# y4 q1 d! `+ l$ }
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。( i0 `" u$ c, J6 U) y, E# H, D
# z* j& U, X( K" B1 Q- a# K
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:" m" B1 y1 G3 w$ r( S- r( F
+ C F& g& K+ q2 ?) ^$ m
COleVariant v1("This is a test"); // 直接构造
4 M1 l; m( x" ~" R! }COleVariant v2 = "This is a test";
% b; g9 V- o4 {% f. ?6 y// 结果是VT_BSTR类型,值为"This is a test". t2 _9 E% b* L7 l
COleVariant v3((long)2002);
) v7 t; E+ s5 c$ g& I4 n- CCOleVariant v4 = (long)2002;
7 H D4 g! c! i. Q5 l// 结果是VT_I4类型,值为2002
* p; |& c2 d j9 q2 V: a5 G$ M$ q e) u) b
_variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:5 [+ [! _% s2 M B* }4 T! [
) j, H$ T: i+ Q" {4 b2 t# q& @ #include "comutil.h"
. x! u3 N# P- X) ]0 C
; J3 L3 G& u; o5 I #pragma comment( lib, "comsupp.lib" )
% s6 q6 ?! ]6 H2 [$ g$ u
& m5 c2 X7 z4 H. i7 V+ F1 u 四、CComBSTR和_bstr_t
$ W# {! L8 V2 a) v
7 R. M( a0 N6 }( Q/ o CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:& }! m' n. n- i, g
, l' T+ ^1 ?2 x- g e9 JCComBSTR bstr1; 0 v" h6 g O4 y+ \
bstr1 = "Bye"; // 直接赋值+ q/ _) D$ j. o( k
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符" y2 e- X2 d+ \$ G4 @
CComBSTR bstr2(wcslen(str)); // 定义长度为55 ?: Y7 x& U, o; @7 c: g
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
% V- M4 S$ M- K; d; S3 iCComBSTR bstr3(5, OLESTR("Hello World")); , @+ V% z3 K! A2 S) {
CComBSTR bstr4(5, "Hello World"); ! J2 R6 }: B" K# |( X8 k% n$ x
CComBSTR bstr5(OLESTR("Hey there"));
4 m0 ]2 h5 i$ Y7 a/ ] u2 h1 _* qCComBSTR bstr6("Hey there"); 8 a- `5 h0 ?% F7 _
CComBSTR bstr7(bstr6); 6 |, f- G, t/ j
// 构造时复制,内容为"Hey there"
; {& d @" l9 [4 C, Y" B; T- W l* q
_bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。0 L' c' N! k4 Z m' C3 B5 M
) o- g% n( @2 R9 J) n
五、BSTR、char*和CString转换
6 w% V" _8 Q: T T8 d8 W
$ `5 E2 U5 J# t (1) char*转换成CString
) ]* \9 ?" c/ D, w& x
1 G* U- U* x+ b1 d2 o 若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
% v( B& I3 N3 {- x9 h, A! c7 l0 B
char chArray[] = "This is a test";
& e B# z. B, x+ c: ~char * p = "This is a test"; , H( \0 m# b$ O( b
6 k5 s( X1 z9 H3 d- Y
或
0 H: S+ O/ N2 I3 R
' Q" g/ [& s' d: R4 }/ P5 lLPSTR p = "This is a test";
7 M/ r" P c% w( v
+ q3 c _# V8 l$ `3 O 或在已定义Unicode应的用程序中
, g W X v# C) G; V' V
& X/ h2 O2 f0 Y- QTCHAR * p = _T("This is a test");
% ] a' F' R7 i. m/ F
7 [. _" x$ f, o& i' K3 ~1 U 或
. H7 \/ s& {+ ~) f' t! j- U2 n" b2 |& _" A8 H& d2 z7 Y
LPTSTR p = _T("This is a test");+ O' |) M, N% s3 F+ z& W0 ]. O
CString theString = chArray;* g6 [/ \% |( @7 V% x, O, p
theString.Format(_T("%s"), chArray);
* {* v5 F: E, N) ztheString = p;
- c/ l' a/ ^# \, ^" J3 p
" s4 X$ K6 L# \% R4 g (2) CString转换成char*
& _5 G( q/ h/ U# J( K* F7 M
) ]5 Q/ h0 \* L) S 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
/ l+ Y6 X! U' N6 s0 I
7 F9 `6 R. B% {& X4 A& j 方法一,使用强制转换。例如:8 \. V8 j% t/ p5 s; ?& b H- q) `
; e/ r& Q3 g: `# kCString theString( "This is a test" );
/ G. `$ p ~1 ~/ MLPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
9 @! [# I8 [+ z' h
3 T7 E0 g/ ~! n8 N+ r 方法二,使用strcpy。例如:
( g" X2 M" _; j6 _3 \9 m* S' S, g G
CString theString( "This is a test" );% v7 _) A0 U. {2 M0 E$ f/ t9 M# Y
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];$ x' e; j) I4 T0 h3 s0 S5 E
_tcscpy(lpsz, theString);
5 P9 q- p/ e, Q9 i8 S/ L. }2 a F/ y5 M- A1 S, q
需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
6 ]0 ^( y/ l- C
! d9 _9 [- X$ @# W* {, E 方法三,使用CString::GetBuffer。例如:, j) R2 j+ |3 I" x7 {
% {! ~& H2 a) Y* ~CString s(_T("This is a test "));
# l4 `, G) I! ~$ M+ }LPTSTR p = s.GetBuffer();9 S9 h! n) A3 E, O
// 在这里添加使用p的代码
0 p- K4 k2 w% q: \" \if(p != NULL) *p = _T('\0');' s. a1 x! D/ G/ ~5 D
s.ReleaseBuffer();
* R5 d5 {% U- R' e1 u// 使用完后及时释放,以便能使用其它的CString成员函数
; _) j: A# q& I3 U6 ?% O( o3 ~, R" ?" g: q& s8 k/ c
(3) BSTR转换成char* k- A1 c' ?# f+ Q: b
! w3 A4 ^( s* t" `# k1 {- ` 方法一,使用ConvertBSTRToString。例如:3 ], ], C/ z- R$ M
! q! N- N0 |. r. [" d
#include $ }% N8 X0 O! Z6 T8 k( `5 q; o
#pragma comment(lib, "comsupp.lib")
5 p( d) _! y! j; h4 g5 Sint _tmain(int argc, _TCHAR* argv[]){1 d+ J9 @4 [3 b! F2 A+ c
BSTR bstrText = ::SysAllocString(L"Test");
0 \& i& y) ]+ g1 w) [. _. n% Uchar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);( w# _! B5 ], Z$ P: t
SysFreeString(bstrText); // 用完释放
2 C& w+ R7 x; W3 U8 cdelete[] lpszText2;
$ {' n7 E4 [' F. creturn 0;
8 Q- M2 k, m2 b; m} 2 j4 d' P9 a5 p9 w* V
9 r8 H$ v* g- P( i 方法二,使用_bstr_t的赋值运算符重载。例如:
$ |4 p1 k4 E% `( C+ K: a6 Z
' \1 u7 Z2 d) r2 L2 e! I_bstr_t b = bstrText;
/ t' d' x1 v, ~* ]- z5 dchar* lpszText2 = b;
' e* ?/ U( L& M3 X6 v$ |) T* W0 l6 \. F2 h1 G; n7 w, \( r
(4) char*转换成BSTR
3 L8 {( a. f* @, ?
, x$ c2 F- I; [4 t v 方法一,使用SysAllocString等API函数。例如:0 m* `0 H' h) G0 z Y& r
* r2 R7 P9 D! B6 xBSTR bstrText = ::SysAllocString(L"Test");
3 [, k+ F5 b7 \% V. l6 b7 B* fBSTR bstrText = ::SysAllocStringLen(L"Test",4);! F- P9 u9 t6 U8 n8 K3 I
BSTR bstrText = ::SysAllocStringByteLen("Test",4); : ^. W! J5 f3 f! W; X% q
$ q, n( w \3 @: P/ c3 S 方法二,使用COleVariant或_variant_t。例如:. X8 e* `! F' Y2 M* k
& y" P0 M% y% n
//COleVariant strVar("This is a test");
( T2 U* f/ O" h. k0 A* U_variant_t strVar("This is a test");# A8 ]7 K3 I' M! i; N" k* [
BSTR bstrText = strVar.bstrVal;
3 O8 T5 V0 M' b% W7 Y! d# O
9 w: C+ h/ i4 i b T/ {( Y$ W2 m0 A 方法三,使用_bstr_t,这是一种最简单的方法。例如:2 }% n* j U" w2 Y% _# }4 G
2 ~+ ~. b* ]7 ]& q. w
BSTR bstrText = _bstr_t("This is a test");
4 T0 w% }( z; o" b" B5 y$ W: x. o' y5 ?4 U. |; k
方法四,使用CComBSTR。例如:% o: I( I3 j, r, W0 S% W0 C; E/ |
?7 A( W- D9 R6 F" TBSTR bstrText = CComBSTR("This is a test"); ' B( ?" e5 B6 w0 y# Z3 p) ?4 }3 r
% |4 | x9 |2 O6 ^3 `0 J( h
或
; a/ D. o' s/ J1 @3 B D* j
3 N* |& |' a. G1 h$ CCComBSTR bstr("This is a test");
+ ^1 ~( L& ~( \ J/ Z" U, gBSTR bstrText = bstr.m_str;
) {+ J4 O3 n! p0 u! u3 H# W
) U" q+ R2 b2 z1 h 方法五,使用ConvertStringToBSTR。例如:
0 n2 S4 L' H+ t8 z( m/ h( y5 w% P/ z$ @+ A2 A! b, a
char* lpszText = "Test";
: ?6 Y6 e& @2 f& LBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); 7 Y8 S& i( P" R2 E; @
- S7 F! J P9 n+ X) @
(5) CString转换成BSTR( l: v0 P. ~& i; b0 l/ E
# v7 F% z( U* P- D6 z) R
通常是通过使用CStringT::AllocSysString来实现。例如:! v y$ i2 z% D7 l
3 B2 D) g2 t' A& KCString str("This is a test");
" x" } U$ c y5 h- BBSTR bstrText = str.AllocSysString();
i2 m9 C4 Y3 v# Z% X5 h…
: e8 q8 c- `$ X; F1 o0 ASysFreeString(bstrText); // 用完释放 1 p- \* G4 D3 Z" [. H5 P) [0 t; n
, j$ Z, e- \5 X: k% q& l
(6) BSTR转换成CString
" ^5 y% k7 p0 V, F
. ?4 c) ?; c& c2 A 一般可按下列方法进行:4 k4 x1 `8 E2 m
) v7 c- [2 n. Q2 A
BSTR bstrText = ::SysAllocString(L"Test");
3 ]$ B. U6 r( N# x4 e0 rCStringA str;
7 `, v. i! P2 J- g( Z" Ustr.Empty();+ A$ L3 U, F& Y# \
str = bstrText; ! t$ b% l- W: V& e3 T
3 y; L% t/ z* C3 S8 @ 或 E% d; A. K6 B
3 K: y2 r; D' U: C" V( kCStringA str(bstrText);
( Y( O: D2 F; |' s+ A6 s+ H- ^; [4 T3 p7 c' k% B
(7) ANSI、Unicode和宽字符之间的转换
6 D$ Z( r3 _* U$ G8 Z$ n8 T2 e! r
' B( ^2 S7 C8 f1 T 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
& R- D/ Z. o+ [ `
. F, I/ k; {' z; i( z2 q 方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:$ x) i5 u/ o9 S+ K2 A; {4 L8 u" X
- M1 P% y% H( I: F7 Y% h- ?TCHAR tstr[] = _T("this is a test");
4 k3 Z/ `2 q- r) s9 awchar_t wszStr[] = L"This is a test";
3 Z/ W+ [& w$ _( U$ p/ Y( zString* str = S”This is a test”;
( U o0 S7 A' [- @) X J: h) l7 C' e$ q V! m
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:" m) j3 Y/ J1 Y
9 `$ J' `0 t: K7 z- e0 m0 P2 i' o) K
其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:' L7 I) V5 {7 L
8 A6 Y7 u) S0 |' `+ {- J+ k
LPTSTR tstr= CA2TEX<16>("this is a test");$ C: Q# r9 ^, K+ H" o% K# V1 v
LPCTSTR tcstr= CA2CT("this is a test");/ W8 z) v1 i7 t7 V0 K% M
wchar_t wszStr[] = L"This is a test";2 q( F+ R c+ Q& |9 [, c0 ^- ?6 X$ v
char* chstr = CW2A(wszStr); / Z+ v7 l) d+ c8 Z! S
% a7 S4 Y1 V2 g/ [1 X* N 六、结语
5 q$ e5 c, c2 m+ A, H+ _$ i) [( [' R, ?; J9 @/ i3 n
几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|