|
2002-12-05· ·丁有和··yesky3 H- l! C/ I0 S* O8 V
& T6 k$ H" c% P3 v7 b
; G. d4 T1 M7 n' i
Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。6 h' R. A) O/ a' k9 W4 ]
" |! U6 K3 W7 W 一、BSTR、LPSTR和LPWSTR) G* c& C7 N0 W9 w3 o* x
5 t! \2 f$ U i+ q# P' I
在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。( x1 ?1 i; O' x1 M% H" f6 i
( ~. A' l% ?( t9 |. L 那么什么是BSTR、LPSTR以及LPWSTR呢?
Z! E) `& U/ t: }5 }* g+ L2 F$ }+ e4 q t
BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
- v( [2 M! R1 {0 a0 b; s/ f8 W# Q. ^- N: Z/ ], r
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。1 u1 g$ p! v, u; u
4 L1 m" ^8 O6 D& O2 b: ?
例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
& t; V! f8 f4 n( J& j
# F& X9 d' i6 d5 L: i, R 一般地,还有下列类型定义:2 n+ f) b2 R# [ y
0 Y' B ]; o" m2 w
#ifdef UNICODE 2 f$ o1 x+ P0 h8 j. x$ e
typedef LPWSTR LPTSTR;* }. k' R/ M* _& [6 C/ r4 }0 K
typedef LPCWSTR LPCTSTR;
0 \! Z) r: [( g0 y; f( {#else
4 M" d7 V4 {! n n2 E typedef LPSTR LPTSTR;
2 E# G. K: A! C5 ]& n6 c- [* Y typedef LPCSTR LPCTSTR; 5 {8 o, `& k. | } @! }; K, ~4 c
#endif # f$ L/ E( `: ?( v
; s% O/ K9 w5 X% l
二、CString、CStringA 和 CStringW$ z2 f, D+ t* P! ]
/ H$ W* R) K6 W' \1 ^ 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应用程序中经常用到,这里不再重复。* E! {. l1 I) R3 h' J* q
1 @# t$ ~" A3 f4 |5 p5 \9 N
三、VARIANT、COleVariant 和_variant_t# C) _. ?# \, }
$ t L8 J+ B, x) ~/ j O: V8 H 在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:! b8 v' i" I. J
- l9 [9 m$ p2 ^% _
struct tagVARIANT {
: y) I) y Q! P5 Y, l% p7 A VARTYPE vt;
- v+ n0 `3 F. G' ?( O1 D union {9 l) ]2 m4 q9 J: U: c0 [
short iVal; // VT_I2.
0 k6 S% ]- S7 c! `& G7 a) e) i long lVal; // VT_I4.
, ~1 Y7 ^6 C. K( z float fltVal; // VT_R4.
& ?! }# I4 |! u4 J double dblVal; // VT_R8.
9 s/ |: `; @' J3 B& {7 T4 S- k DATE date; // VT_DATE.: N: d5 z' h; K, N' y4 n
BSTR bstrVal; // VT_BSTR.2 r( Z/ T% ~8 H! W( }' J
… x- Q9 @) X0 u: Z+ [7 _
short * piVal; // VT_BYREF|VT_I2.2 j# T: u5 ?' L) Q) d4 F
long * plVal; // VT_BYREF|VT_I4.
' d( N8 p; n9 b float * pfltVal; // VT_BYREF|VT_R4.1 w+ x* C0 r( X! c5 }
double * pdblVal; // VT_BYREF|VT_R8.0 V1 G: \2 P2 M
DATE * pdate; // VT_BYREF|VT_DATE.
# G" Y3 d: B' H7 L: v BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
4 o/ L8 {! t1 G/ E };* e- K" ~$ ?2 R3 N+ V$ R/ h
};
' J3 D1 J d$ {" ^0 g0 M2 p( C. m
显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
5 s# Q. ?1 ~! R8 d& z7 C/ w2 A' }- Y' y
VARIANT va;' G0 |' E5 v8 f4 p
:: VariantInit(&va); // 初始化
/ S: d) C6 j( N7 l* Qint a = 2002;' }$ o9 b; }" f* u5 Q, e# B
va.vt = VT_I4; // 指明long数据类型
# J' C" j8 F- J* I Tva.lVal = a; // 赋值 : G4 i& r3 r [% n
! S- V3 m5 C3 ~: w, n# |% i- t 为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:4 A0 k, D2 i# |1 K, |8 K
( W9 |8 y) m8 `5 D9 n VariantInit —— 将变量初始化为VT_EMPTY;. z- o) [4 G* X9 C1 h- F( p3 W- ?+ |
6 ^ Q; k0 O4 C VariantClear —— 消除并初始化VARIANT; R* b8 F R X/ ?% Z
/ q" P. _- I. k VariantChangeType —— 改变VARIANT的类型;
+ O5 w( z" k" e( S7 w/ ]0 e. ~6 b* S Z) j4 T* W! k
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。& X( X! ]% C% }* E
1 j" C9 T' q, ~$ g
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:5 e0 r+ e: P. [" o
( f t Q0 c% k* Z v7 ^
COleVariant v1("This is a test"); // 直接构造
% R1 \( `: s, ECOleVariant v2 = "This is a test";
1 q) d6 g1 e$ z6 i. I// 结果是VT_BSTR类型,值为"This is a test"
" N4 e% ^4 f ^+ j' X7 s/ dCOleVariant v3((long)2002);' G7 P+ y: q4 Y% h0 a+ u
COleVariant v4 = (long)2002;5 ?' {. R; J, e% }! a* {
// 结果是VT_I4类型,值为2002
2 |; G, S% [/ @: S
5 @3 i( h/ Q1 J$ t8 n _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:
* s/ J! ~8 w3 n/ n& b
( D4 d' r S* _ @ #include "comutil.h"
* b6 h1 O3 w+ u# o5 @9 Q# L
3 T5 I6 \8 m6 Q T# p! J #pragma comment( lib, "comsupp.lib" )
6 g; g# J4 i. }! }& l/ u/ v
1 O! p9 ]' b* ? K 四、CComBSTR和_bstr_t0 m; |6 y6 J! r9 P1 }/ {0 \3 K
; a( k6 a/ S4 I* A) _& T7 [! w CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:/ r8 l$ E$ z0 C+ ?7 `# M
6 S0 e6 t* i: D/ N
CComBSTR bstr1;
" M* K' o* C! a2 X6 xbstr1 = "Bye"; // 直接赋值9 x3 m% k$ j) r* D: U
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符
/ q; D! U0 E% ^; T5 ?CComBSTR bstr2(wcslen(str)); // 定义长度为5, S/ l; n) m1 c' L( g
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中 u% ]* W# G) r. i' a
CComBSTR bstr3(5, OLESTR("Hello World"));
/ H& C' D4 M9 ]5 }CComBSTR bstr4(5, "Hello World");
, Z ^$ L8 D# D2 Q% O+ oCComBSTR bstr5(OLESTR("Hey there"));
$ g; y6 O9 I. q6 R5 D: HCComBSTR bstr6("Hey there");
" j$ L& ]5 Y) O1 }4 @4 gCComBSTR bstr7(bstr6);
* N$ J% i& f; x& x: w// 构造时复制,内容为"Hey there"
6 i1 Y$ e: s0 h6 b0 E) F4 {' X6 F
2 u7 C% d6 A- x/ L/ X! f _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
( V; B* P4 d( q* N9 v7 J5 P1 r9 K5 Q) \) G1 Z4 J, T, Q7 q
五、BSTR、char*和CString转换
; M0 L* }5 W6 B, C _% a8 P
; r: K: G8 ~& G (1) char*转换成CString
# {, U6 [" d" Y W" W" p) n+ Q
1 I9 b' f: c4 \$ M1 k" e3 b 若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
5 [/ c% R+ z. t6 k6 M2 q
% `8 O" c* b9 g9 A# Bchar chArray[] = "This is a test";0 r2 ^/ n& m( X
char * p = "This is a test";
6 g( x% y O5 t4 a$ p/ [7 K3 w. a, G5 m; s c: Z1 S7 v
或; E2 q# k" c" I0 X2 N1 @4 ~: `
; i4 P' y) ]1 v8 FLPSTR p = "This is a test";
, o( y+ H% i8 ~. o& }/ |( x
7 A8 ^$ Z# {- _1 S! ], \2 n 或在已定义Unicode应的用程序中) L# i$ K) C' q4 x2 F+ E; Y0 t& |2 E
1 `7 Y7 w% F. t/ `TCHAR * p = _T("This is a test");
) F! \1 g( y7 q
; M. W6 `( O; C6 \5 O' z G 或
' u$ Y3 l j# B: W" b& t6 s3 T8 T
LPTSTR p = _T("This is a test");2 H9 G4 _0 L' j
CString theString = chArray;* k; X! b5 w- u( `1 t
theString.Format(_T("%s"), chArray);2 W1 m9 p1 H5 w7 h# h1 E5 h
theString = p;
. t% Y+ t9 p6 M# N7 `! p
- t5 q& B" K. ?) K6 C- A (2) CString转换成char*
/ C0 v7 @4 Q9 r& @, X7 K! R
& q; X0 @% \6 L/ V: c 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
/ P1 L9 X& i6 v" s
( e9 P, p6 R% o F+ k8 ^3 B8 W; ~ 方法一,使用强制转换。例如:
9 H' a3 Z" Y$ m( S1 S: T1 Y1 s
& A: u- ?: u3 Q8 n' iCString theString( "This is a test" );% p; G& \5 y4 x$ K3 e% @
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
9 w& i% F2 c5 |& R
( ?. n, _1 B+ x1 @' ]- | 方法二,使用strcpy。例如:
, ?+ G6 a* S0 r4 ?
2 i2 R( \& [2 ]. b" _# o0 p4 cCString theString( "This is a test" );
) P& c2 j3 h( wLPTSTR lpsz = new TCHAR[theString.GetLength()+1];
/ U9 e: g' P0 @% U& r: v* o& r_tcscpy(lpsz, theString);
5 _" y. o+ Q& k9 T0 j1 M3 u- c; {
需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。7 z- m+ M9 |0 @3 c) X
; c3 |6 l( }9 f; o3 C
方法三,使用CString::GetBuffer。例如:1 m% P/ p: f: C5 n4 Z
, u3 o* l$ S' HCString s(_T("This is a test "));
# \% P# X( b1 ?2 Q" ~- Y5 P6 gLPTSTR p = s.GetBuffer();2 `, Z# E0 g$ V7 q
// 在这里添加使用p的代码/ ]+ x5 }' n8 [0 B) j
if(p != NULL) *p = _T('\0');( a1 K5 J" h5 N _
s.ReleaseBuffer(); 4 W: _- e0 |& _ t# }5 J* ?
// 使用完后及时释放,以便能使用其它的CString成员函数
# A" w a, m9 J+ [
* {' t( a" k, @$ f- V0 u8 ^8 Q! K \' g4 n (3) BSTR转换成char*
! w& g; m3 ]& i( z8 Z/ z( R" ~" G+ ~' X3 M0 K! K' x
方法一,使用ConvertBSTRToString。例如:0 U7 C; T! ]- O* G: Z
* u- h- }6 f. O& l# o0 O#include 8 K; o" {# D% y8 @$ x2 ^+ g
#pragma comment(lib, "comsupp.lib")
) r+ R1 h6 R! _. |: c0 oint _tmain(int argc, _TCHAR* argv[]){ x1 b% M6 _. O/ p0 {
BSTR bstrText = ::SysAllocString(L"Test");+ m3 X3 o" F* Q5 I. e8 ]
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
2 I4 _- a6 g1 T* kSysFreeString(bstrText); // 用完释放
, p, E: W# \2 [+ udelete[] lpszText2;0 Y" [/ x. I( u v5 f7 l2 C. D4 O
return 0;
. N# a& k0 e6 F" i} ! s( Z2 J h a
+ u. Q9 q0 `' a4 s3 ?
方法二,使用_bstr_t的赋值运算符重载。例如:
( Z5 G, c2 |2 w: F8 Q& U/ h
" |0 K/ v( w* d2 T/ c_bstr_t b = bstrText;
) v6 ^! E X* N: s+ P4 d3 J3 Achar* lpszText2 = b;
! w' [; J' J Z; j7 y
0 z' W% `' b* V8 p) k1 M6 c# j (4) char*转换成BSTR
5 T* n4 h5 v3 N- o: L
( Y* z, d/ F3 D7 Y. U 方法一,使用SysAllocString等API函数。例如:
" H9 {0 H4 I. p8 s5 k( @* R# [. ~; X( L1 u+ {- f c
BSTR bstrText = ::SysAllocString(L"Test");+ a; h- _; Y/ b7 a8 ?" M
BSTR bstrText = ::SysAllocStringLen(L"Test",4);1 |4 E1 @( |2 G! ?/ H
BSTR bstrText = ::SysAllocStringByteLen("Test",4); / _" B, t' ` [7 F
) g0 I8 P! s9 b
方法二,使用COleVariant或_variant_t。例如:* K( F9 j' _& z( P8 F0 o, h
0 N, t# |9 [7 F/ m& c7 k, _2 u- d
//COleVariant strVar("This is a test");
2 ?* J. P% e! ], q5 K_variant_t strVar("This is a test");
, L1 k& ?/ s9 u* CBSTR bstrText = strVar.bstrVal;
. L& m& k) A8 V
+ W( B( n+ _/ s' g% B) k0 p; Q 方法三,使用_bstr_t,这是一种最简单的方法。例如:
6 n: i5 f$ M0 {; F: @# o' W& {$ l9 I( J+ P# T& ?; A9 k$ i; l/ F' o$ B
BSTR bstrText = _bstr_t("This is a test"); ) T- q( B8 w: s5 i5 h$ o) ]
" p. R# l( _6 _! g `3 n 方法四,使用CComBSTR。例如:
6 b! |, B0 D+ n4 V
) o) \6 K+ j5 t! [2 P; @6 LBSTR bstrText = CComBSTR("This is a test"); 8 I, |' w7 f- I! @& l) I
3 Z4 W5 s! F. s/ r) \% l! p. D
或' Z, k6 o% x: }1 X0 h+ F
2 \2 y$ g& q9 _' e, |# D3 B
CComBSTR bstr("This is a test");
/ l3 T! t c$ S: P# b- T- F* bBSTR bstrText = bstr.m_str;
$ s$ H6 \0 e& Z2 o( f" x% y5 x& K
& q6 J' N6 x x9 N+ I# u2 L" J 方法五,使用ConvertStringToBSTR。例如:
7 S6 Z2 t3 k8 a1 h* [; i- t3 I$ C7 T% u' x1 m! }& w
char* lpszText = "Test";
( C: r& F, v8 {" |$ ^BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
) n# u- d/ l; T# ~' p. k8 a- E/ P% t
(5) CString转换成BSTR
6 r; X( }' z6 P$ u6 N' N# J7 U- _$ Q) ]% n+ V, w6 q
通常是通过使用CStringT::AllocSysString来实现。例如:
- L' s0 Y% z! j" e6 K: B( G
( f& C# O- u. GCString str("This is a test");( ]5 {7 f4 T. J2 Y
BSTR bstrText = str.AllocSysString();% ^/ c" q; D7 c- F7 P/ e- F a
…. I' `5 n% {" M
SysFreeString(bstrText); // 用完释放
4 z' i# p+ ^3 W+ ?
# \4 [0 g7 r' x (6) BSTR转换成CString- n; Q: ?$ u0 \. n& k* n2 m( C
* y( h3 ?6 Q, K
一般可按下列方法进行:
$ t- I/ ? ~& Z# l0 U# M7 ^8 U" c( x
; Q3 m8 r( n- J* t( K. S" X: I lBSTR bstrText = ::SysAllocString(L"Test");
2 Y. E* g& {0 _+ b- _CStringA str;1 @: }7 n; F0 f/ [3 g/ a( X5 b
str.Empty();7 q8 O0 O1 W: q3 P3 H# j. [
str = bstrText; ( Z( J: z6 T/ x& z6 d
- Y8 J: w. [ Z' N- k c6 L% m 或
+ g+ B' j% g& \ p; c: r4 v0 q% \" S8 {* o e
CStringA str(bstrText); 8 F/ I9 `* N7 _! a! ?; P9 ~- V* ]
' J) u; u0 W7 ]
(7) ANSI、Unicode和宽字符之间的转换 R& M+ p6 G/ Z9 Q# [2 B
0 [+ W4 V) U, p! Q( g, L 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
6 |/ U; e' K/ v j& e; e* k4 D4 F+ m
方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
# R% ]; |+ x3 D6 {( R1 k6 Y- H8 d6 P& s/ P" @! q! G0 `
TCHAR tstr[] = _T("this is a test");
( G$ q7 b' Z0 B7 Qwchar_t wszStr[] = L"This is a test";7 o/ B' r2 G7 ^2 M5 r
String* str = S”This is a test”;
% c4 K# b! u+ _. J( B, B! \
/ \. X! Z6 m8 Q" Y6 D 方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:5 s( Y* Z+ t. ?# Z4 N* `: _3 E
8 z; @ q4 |" G% X0 F" p 其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
1 x3 E: e F/ [6 f/ V
3 v V# C6 k6 H+ cLPTSTR tstr= CA2TEX<16>("this is a test");1 }* B" F& P8 R4 c7 A" w7 i, L
LPCTSTR tcstr= CA2CT("this is a test");$ e, w7 n$ R2 H( ~
wchar_t wszStr[] = L"This is a test";* y/ J+ u% t' R& A/ l# f& D$ o9 h
char* chstr = CW2A(wszStr);
5 s4 r/ r; t- ?. h1 v" B$ ]" R! ^0 d( C. d
六、结语% d5 ^" E! w/ S5 ~7 U
) ^" z, ^' k, c6 s" I1 O. n 几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|