|
2002-12-05· ·丁有和··yesky
T2 D1 \1 t; ~6 m , H# ]; |8 R1 d& V |2 }
1 m7 \- c$ a8 C, ~( O5 ?- N
Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。- ?7 ~: l# o) s
$ k+ I/ L# v" F2 F$ \ 一、BSTR、LPSTR和LPWSTR- V9 i2 C2 ^4 B& t, Q: O: L
. n) }1 i( M6 C# G+ `% T
在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。8 V6 q5 z0 y; v9 M
; N; c# F0 z( W, i1 K
那么什么是BSTR、LPSTR以及LPWSTR呢?, h6 N! u Y8 n2 d7 h
1 @) O8 |" L/ x) C* x: s- b, _" w BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
: Q5 [4 ]+ H( j1 N8 H. `) p0 |6 J( c& T) O( p K2 E5 b
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。1 T4 o& e$ _, g, _) _4 v
! @- u2 h) A0 U4 g4 M 例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。" R4 U# @! q+ r* T2 C8 \3 O
5 p( t% H. v) I; z! A 一般地,还有下列类型定义:
5 s" n/ M6 _; b$ m5 i0 Y% q, a+ i9 [
#ifdef UNICODE 8 O! m) m0 N6 W( h3 f) h& u9 ?
typedef LPWSTR LPTSTR;# G5 V2 U" P) k+ e/ @
typedef LPCWSTR LPCTSTR;
7 x* w% k" u; l3 U3 I#else , ^0 M: x8 }: ~# r. p1 h
typedef LPSTR LPTSTR; 1 U6 q( C G. q5 v4 z
typedef LPCSTR LPCTSTR;
4 v F$ H% \; h' {#endif / A0 e8 B( a# O3 @* ~! O. R
6 X+ S5 B! t8 Y2 J6 Y' }4 G1 Y- s
二、CString、CStringA 和 CStringW% w% T$ i% q7 f" h' Z
3 u% M. p% n: o9 q1 u2 L2 ?' G 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应用程序中经常用到,这里不再重复。
7 n% _. r9 l4 ^: t6 h( m( H# e1 X- i1 b: J( v0 X1 k/ r
三、VARIANT、COleVariant 和_variant_t% u6 w1 g: T1 P2 P
) e" }+ z& \. j
在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:2 \% `% @/ j9 W! `$ p
# r! @* C' {2 T$ e
struct tagVARIANT {
! Y3 b5 [* `; C1 H VARTYPE vt;6 ^ |: `% c2 f8 P' Z- j; R2 u
union {
9 P* }) C! i& m: \ short iVal; // VT_I2.
) D2 R* o4 g' w4 S4 v A7 J% { long lVal; // VT_I4.
8 ?1 \2 G* P* a4 Z float fltVal; // VT_R4.7 F" E+ ^& u4 X h0 _3 D2 L3 M
double dblVal; // VT_R8.5 Z* c. l- W( c W
DATE date; // VT_DATE.4 }# }9 u ^+ B; U I, W( E; |4 F+ A
BSTR bstrVal; // VT_BSTR.2 y; ~* I" O$ l; I5 X. B* Q
…
% [9 O+ C4 h3 ^, }1 _( T short * piVal; // VT_BYREF|VT_I2.
0 i* X/ N& m1 y long * plVal; // VT_BYREF|VT_I4.
# x! u9 A* d% d float * pfltVal; // VT_BYREF|VT_R4.
& [1 u: A8 J! N# e4 z) V, P8 N3 D double * pdblVal; // VT_BYREF|VT_R8.
; g$ y& F& ]2 v3 }- n/ }" i DATE * pdate; // VT_BYREF|VT_DATE.
" |' f% r5 b' j* W BSTR * pbstrVal; // VT_BYREF|VT_BSTR.- f! V, v0 H. h% c7 A7 g* a' L
};
/ R, I. F. t7 ~; ^& \* p& ]}; & y1 `0 H9 X" O d
6 o3 M1 d4 P0 V0 p% Q6 \ 显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
/ w9 B. q/ L7 n! g% f3 B9 }% r/ [) q1 |( H
VARIANT va;
; r9 e4 w5 [6 l7 e; D, F:: VariantInit(&va); // 初始化9 R. U8 t; c: P* @
int a = 2002;
5 A( x6 y! O! `( mva.vt = VT_I4; // 指明long数据类型. m) J w" X. M5 @" q# A. `- j
va.lVal = a; // 赋值
) \& F6 l1 C7 \( z# B5 l+ m- E% f' f0 D
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
/ s" p( T' k1 c$ a% k. E
! D M. y6 H5 W1 B, K1 L VariantInit —— 将变量初始化为VT_EMPTY;
9 `# A9 w `' q+ I$ B+ f; X& y; J0 g3 Q# w5 h; ]1 J
VariantClear —— 消除并初始化VARIANT;
+ Z' C6 }2 C, q; i& x7 m# { D9 o! ^3 A! M5 M
VariantChangeType —— 改变VARIANT的类型;* m! C1 N# l/ |4 j3 d; N
: l) {! I) L+ \ H
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
`+ x3 V. v% T5 B8 ~. b$ j- l7 _7 {0 @
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
0 ?8 C- c0 ~0 \9 B. X& ]( q# }* q1 x' S8 f$ _" h# |, A0 u
COleVariant v1("This is a test"); // 直接构造$ |' Y! e: n! M3 P7 v
COleVariant v2 = "This is a test"; 4 v; ?) S% Y4 ~ _3 N
// 结果是VT_BSTR类型,值为"This is a test"
( ?1 { q$ W. Q3 jCOleVariant v3((long)2002);' v( G+ B8 s0 T( t2 _4 Y
COleVariant v4 = (long)2002;$ W, D* E7 s; P
// 结果是VT_I4类型,值为2002
G! M/ Q2 l9 v8 A, R
( o. d5 U( Z, {- O1 d: L, y _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:: I+ y* Y" S% K1 P
5 [' Z* h& w2 O( X+ w" _' j' O& a
#include "comutil.h"
% N* ?: ]: a* A( d0 r0 X/ _, f
. A$ D3 } k* X; a4 A% s& s" T #pragma comment( lib, "comsupp.lib" )
9 M7 q2 S! W6 G9 u" X" \
A6 K% O; j/ C1 a! g- {0 ~, e: g8 r 四、CComBSTR和_bstr_t
" P' V/ S/ X. f" y8 Y
% @; g+ M4 h+ K) C& z CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
4 L8 U$ o: j% q" a% q
1 {* T# [/ M( X6 a8 O/ T0 ~; _$ ^CComBSTR bstr1; 8 h7 A. n. H, Z- U3 `9 C3 o) I0 R
bstr1 = "Bye"; // 直接赋值" u" k8 j% E! O& W# E& p( n: c3 I
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符8 n' O7 c9 U- v, B3 k
CComBSTR bstr2(wcslen(str)); // 定义长度为5
5 P* W1 v/ ?2 L$ K- Fwcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中* T1 w: ?0 E8 K; G% \) F
CComBSTR bstr3(5, OLESTR("Hello World")); - p) N. a o' I+ o$ b- P8 ^
CComBSTR bstr4(5, "Hello World"); " \$ b% Y% u8 {$ K( |
CComBSTR bstr5(OLESTR("Hey there")); 7 I3 d. {! V |) `0 O
CComBSTR bstr6("Hey there"); 7 ]0 r- w1 F& e
CComBSTR bstr7(bstr6);
! ?/ Z' v7 \. s- n// 构造时复制,内容为"Hey there"
2 s, ^7 v; f* j5 `3 H
' \$ H5 M: J0 J0 I2 ` b' G- { _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。" D. I! h2 w% O
2 A" c8 X! }; a
五、BSTR、char*和CString转换
/ W3 b* ?. w" a$ _$ k, L4 t$ b" T! ]& O' w, o% c
(1) char*转换成CString
+ e# H9 c$ ~5 Z ^
& x# Y% U; F8 a+ @7 B9 s" h" i3 o4 [ 若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:2 U+ u: C- y! v- x
/ m0 c1 n7 a1 Y* \( dchar chArray[] = "This is a test";
% ~3 L* A" J) }$ W1 X1 Echar * p = "This is a test";
7 ^. b) i% {" g) x( w. n0 {0 \) C) `: j) ^$ l" x$ A
或# c& E. `: z/ b! @5 V: f
- N7 q5 }) `* u0 gLPSTR p = "This is a test"; " q I/ W8 Y* n: ]
' ~: e7 }7 \' g; m+ k, Y 或在已定义Unicode应的用程序中. C; I+ j" [% }* [" W( b
/ ~2 ]+ n$ M E: c) Z$ o6 t
TCHAR * p = _T("This is a test");
, C: r+ M- |( ]* I; O7 d& |* d- l6 k. n3 Y0 J1 I
或
, V W6 s4 |4 R4 u( K* j s, p6 a( Q6 F' L% ^& W) _
LPTSTR p = _T("This is a test");% U+ ?& e) u; e* q+ r& ^* s
CString theString = chArray;
2 J+ u) @. L) L! G WtheString.Format(_T("%s"), chArray);9 E) l! P2 e1 F
theString = p; & u# t5 w* \+ L
) I, g z5 M, }) ~" e! o( n0 V2 m; y
(2) CString转换成char*# {2 s _ _! N; }+ q& K8 a; c
7 K Q- m' \; b8 E! T2 H1 J9 L 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
3 [$ L% E# v8 K. T' h+ ]
0 `5 b. D# e0 W# R0 P5 R0 y2 ? 方法一,使用强制转换。例如:& E; M7 Z% d5 e, ^; q
3 C! }8 ?, i5 k% Q
CString theString( "This is a test" );0 W' Q0 ^3 Q" N' T
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString; $ W* b# _6 o1 g5 ~
3 ~+ U! k. n1 b+ V7 P- @/ I8 j3 n8 o ^ 方法二,使用strcpy。例如:
" X+ ^7 ?* D) K1 c
! P5 O8 n8 `! ~! e/ m) a, ACString theString( "This is a test" );
1 C; g: D( ?+ v. {& _' YLPTSTR lpsz = new TCHAR[theString.GetLength()+1];
# b9 k) q- U2 s0 T+ L. v_tcscpy(lpsz, theString); 9 M! m* C1 N" i1 d0 h
y. ]' E2 r3 m5 u, U, _ 需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。9 |) y7 n& h {- P( H
" R3 l2 m. M- o; c1 ], D6 Z2 f 方法三,使用CString::GetBuffer。例如:
: }& J/ d8 {+ w n7 |! X6 A# }
' B* S% s2 C2 K1 D8 K6 f, uCString s(_T("This is a test "));
: ~' N: ~2 e5 zLPTSTR p = s.GetBuffer();- ~9 B! {* H3 A- q' l2 y
// 在这里添加使用p的代码
- t9 @% y# a* \0 v: wif(p != NULL) *p = _T('\0');
$ ^$ }" W7 h3 D+ J$ J, j! hs.ReleaseBuffer();
2 A ?% n/ L1 x9 l# t// 使用完后及时释放,以便能使用其它的CString成员函数 , H4 K( x+ L! ]. A
4 _4 l. A! Q @8 I1 ^2 Y
(3) BSTR转换成char*
7 \# d# W& c2 T4 Z( r) v& ?/ [# B3 V
方法一,使用ConvertBSTRToString。例如:
" {: q' D' X+ b+ d: k; ?4 Z0 j: N
#include 9 e' g$ n7 b) M3 Q
#pragma comment(lib, "comsupp.lib")5 I$ O W5 H- W
int _tmain(int argc, _TCHAR* argv[]){. I H2 }+ F/ ^6 F) G0 a3 \2 H' C
BSTR bstrText = ::SysAllocString(L"Test");
/ u9 u0 A* O6 y* \ v1 [+ achar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);: ?% ~# e2 [0 I3 X9 U
SysFreeString(bstrText); // 用完释放* W) m7 h) \5 Y6 X6 s# ?& S1 R
delete[] lpszText2;
7 g5 p1 I$ @# e/ \9 L% Ireturn 0;1 `$ I& E8 W+ W. H+ Z
}
, E- y" b" C8 _& v( F; D: S1 e* U6 a( b3 O( O) Z
方法二,使用_bstr_t的赋值运算符重载。例如:
) u7 {" B9 P8 Q2 Y+ o. A
( l" b6 f2 w* z- j& a# Q, }* U_bstr_t b = bstrText;
* _3 B5 V6 p$ A' G* x( z# F0 F. Echar* lpszText2 = b; : R' m8 p8 ]* Y- s. }: o( ^7 u g
, x8 H9 e1 M0 c7 E9 a (4) char*转换成BSTR9 x5 J* h; [4 U- ^4 T
. ~; Q! K8 z# d* j) n( x/ Q 方法一,使用SysAllocString等API函数。例如:6 f" ]$ R5 o/ f5 F; F% _( j
& {; [1 K/ o: r4 ~BSTR bstrText = ::SysAllocString(L"Test");
' H4 p- s' v: ~ ~) Z* ~BSTR bstrText = ::SysAllocStringLen(L"Test",4);
& E8 p3 W8 Z& O F( | q& VBSTR bstrText = ::SysAllocStringByteLen("Test",4);
+ u' u: @% Z# d: A/ _; h" G& M% p6 l b# V8 h$ L' ?6 M
方法二,使用COleVariant或_variant_t。例如:3 v3 p' j$ ]7 Q3 A* I7 @2 \, U7 l
$ q9 W- y# H: u% S
//COleVariant strVar("This is a test");5 [, ~, a) ]+ y* ^, ?0 ?/ d5 R w
_variant_t strVar("This is a test");1 Q: k$ N, Q! Q- p! T; Q, m
BSTR bstrText = strVar.bstrVal;
/ E* k1 t+ { k6 A
% s4 S7 r' r8 i' N8 ~% N 方法三,使用_bstr_t,这是一种最简单的方法。例如:
& @: F \& U r7 A+ k3 q0 b7 \+ a4 b! a5 p2 B5 F& q
BSTR bstrText = _bstr_t("This is a test");
$ i2 M" F+ R' H+ \7 r5 I% ^- ]
; t t8 f4 k! C* v& O! [ ~ 方法四,使用CComBSTR。例如:9 P; P0 [ E' R
$ v/ _3 [9 c9 ]( b. }' |% y7 yBSTR bstrText = CComBSTR("This is a test"); ) D' L+ O$ w. M$ A' h g2 ]8 H
4 x. C! o; S# C: L8 v! h* [0 c
或
# M7 O4 f8 x8 h' Q8 L) x2 i! A# \+ ^ ]& B/ D8 \# N6 P" Q3 g: n) O
CComBSTR bstr("This is a test");8 I8 E8 U+ c$ { M9 N$ d3 x8 F
BSTR bstrText = bstr.m_str; * r5 g1 u, m& m" {( k
$ C& H' j1 B+ X, C6 ^1 {+ r1 L9 ~. ^
方法五,使用ConvertStringToBSTR。例如:
1 D7 b, z5 d! q$ y+ d2 \2 _+ m
& H; I+ K, G# e1 Q5 cchar* lpszText = "Test";
, {! E. r% y) D' M! B! B* m% y! eBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
% p% C0 K9 u n! _
/ \" ~2 @: R/ t) l6 @ (5) CString转换成BSTR N2 P. H1 ~& @, {: g
' {4 H8 i" c7 O. F4 B
通常是通过使用CStringT::AllocSysString来实现。例如:
" I6 j e. u2 J" r6 Y* Q
% n5 C/ M [' b9 g0 P* g5 HCString str("This is a test");& t6 f4 ], g8 M, D' r# S
BSTR bstrText = str.AllocSysString();
: p' I- r7 p a' o…) w# z1 _8 T! w3 T5 }
SysFreeString(bstrText); // 用完释放 9 t# r4 p- I, T# U+ P4 F
8 r. v* m9 {% C; e' s/ A# O
(6) BSTR转换成CString$ u' i" s# {3 `3 p/ S' z9 ?3 @
7 ~/ ^5 D A* ^5 \! y$ [; M/ D
一般可按下列方法进行:
9 Q4 v6 o9 [9 m% F2 G5 {9 n# l! k( |; {; `( a
BSTR bstrText = ::SysAllocString(L"Test");
1 t" ?& _7 R K- z( q% ICStringA str;: T0 P" X( ~$ g {
str.Empty();3 S5 g7 c {) s" @% ]
str = bstrText; " n9 S% D) z" n8 T& [8 ^0 t9 P [
' e( m- M* i) d5 v0 T+ B 或3 |: R; v, u2 ^. t
& }( Q# f! ^: \) x2 T
CStringA str(bstrText); 9 W# g7 ? V. u- c1 L. v
% f( Q7 I- C+ g |& X (7) ANSI、Unicode和宽字符之间的转换" L5 v8 H" u4 @/ l/ Z
5 n U( y" F: D2 J6 C4 Y9 b 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
* H8 @5 c, r$ m% \0 E
8 Z3 k4 p. D" [, Q9 r3 p- b( l B 方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
; G% ^, y. E9 L' Z# P' |# r4 }
w/ }- D$ ~0 ^8 V% Y9 b: @4 L' kTCHAR tstr[] = _T("this is a test");8 ] N1 r" N! r/ w" E* k
wchar_t wszStr[] = L"This is a test";
" B! W" ~2 S: J9 \% \String* str = S”This is a test”;
7 F5 {3 R8 P6 V' w, {1 @( k9 p4 M0 [% b7 J
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:1 N1 j' f% q) y' z# \, d
0 u" g' n/ O4 m
其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:+ Z3 c: [% `4 ^5 g' g* |3 H1 x
3 ~9 W0 N& v; `6 W p! U+ ?8 H
LPTSTR tstr= CA2TEX<16>("this is a test");; w- d, _) O( F- f! L7 u g
LPCTSTR tcstr= CA2CT("this is a test");. @, D3 ^) j3 t7 Q
wchar_t wszStr[] = L"This is a test";
% [. S+ y9 ~* l* Y+ u) echar* chstr = CW2A(wszStr); & j o2 N4 L2 Y& f
L$ _( n5 v# l1 Y: g
六、结语
5 R( ~3 ]9 G' ^6 z/ z: [! f2 J# @" f! V4 U, D2 @! Y$ Y
几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|