|
|
2002-12-05· ·丁有和··yesky
/ z, u3 n7 q7 q N- I, ^ % ~. o. b' f3 Q' u9 ?) ^1 @
/ B: J- `& F+ m4 F) H/ a I Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
8 ^- {+ P3 j1 `6 a3 I) c' h; d- E' N5 z. q J- G
一、BSTR、LPSTR和LPWSTR I) \/ B2 M3 `
1 H# D- ~1 H0 |2 w, h 在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。8 s) P# {7 n: z4 j9 D- T
9 y7 U% x7 c' X" v" Y7 z7 p6 H, G 那么什么是BSTR、LPSTR以及LPWSTR呢?" {9 X V# l8 g) d& D; L
* o- M5 T+ M8 v. }6 `) \" V2 U6 d BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
- \4 F. _' h, n ]9 r! I* h. z, @ L! h7 X- o( Q) X6 s
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
6 `. T# t' h: q+ }* d3 w8 L# l$ Q. q0 Z2 Y0 R* O9 O
例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
* S4 N3 O9 L( N1 ?, d `3 G `8 u6 p
一般地,还有下列类型定义:
/ b1 D) I- i/ P$ y) K+ c
: n; P* a) J8 S) `& s8 R4 @( h! F#ifdef UNICODE
, m9 u& M1 v& ?4 k typedef LPWSTR LPTSTR;$ ~& L+ X, s6 U$ z6 `
typedef LPCWSTR LPCTSTR; ( W8 ]$ o" O0 ^( l
#else , v: _* |- p0 r5 ?
typedef LPSTR LPTSTR;
! Q9 E6 ?( J: k5 m2 f& _9 D# P5 G typedef LPCSTR LPCTSTR; 9 Q) l, f# }6 p, {& A! f
#endif
0 S5 m" e o# I' F' j6 |) O4 f# Q P, H% w1 y( h0 e
二、CString、CStringA 和 CStringW# `4 }% p3 y# T! m. I
! I. [# D: ?. J* @$ n5 M. |9 u
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应用程序中经常用到,这里不再重复。
) Y. J; g5 }4 g
; Z! G" Z" s# g4 \/ d 三、VARIANT、COleVariant 和_variant_t
8 t# r0 M; |0 {% |0 q, x5 T {$ i/ H" b9 }; I& G/ C2 V
在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:1 z2 U+ ` }. T" k+ T" [
7 g: R* I0 X) G: K0 u9 I
struct tagVARIANT {, K5 h; u" E& E) J0 M* }6 O4 z
VARTYPE vt;, X: v) k" b3 t4 F/ ~1 D8 l5 P
union {" N! B8 v8 Q8 k" u3 h
short iVal; // VT_I2.+ s3 L% G* G! ?) b) J9 j
long lVal; // VT_I4.. w: j9 }& C+ d% }0 _7 \, \
float fltVal; // VT_R4.
; ~( }( q$ p& k6 {$ U. P" L double dblVal; // VT_R8.6 M: O! P$ U2 h
DATE date; // VT_DATE.: ~) Y9 m3 Y/ J# _2 ?& i: V/ v
BSTR bstrVal; // VT_BSTR.
) n' o- {. z3 d" E …
F2 b0 i5 l7 J! @ short * piVal; // VT_BYREF|VT_I2.
4 j; u: |$ c$ Y, M0 j2 i, z+ q long * plVal; // VT_BYREF|VT_I4.
, _/ a+ ^% Y' J c7 J float * pfltVal; // VT_BYREF|VT_R4.
! z( P+ y) _# x. V; y# {% i& \7 @ double * pdblVal; // VT_BYREF|VT_R8.
# e& `: s0 M2 } \: `% h DATE * pdate; // VT_BYREF|VT_DATE.4 n7 A9 d; a w5 C( X* u
BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
2 e% _1 K4 p! I5 F' Q& i };
% H; K u! w) R& k}; ; a( S, k$ X; H5 C$ k
: D+ J* _( P. v/ p$ W' a
显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
5 s8 o* y- ^/ n* v5 K b! @& ?" f
% G1 a& {& I0 O$ E7 C: e8 u2 z/ C2 i o' dVARIANT va;
( u8 P9 d6 i/ d1 H4 F, w:: VariantInit(&va); // 初始化
7 H8 V, F9 B" ]) r0 zint a = 2002;* N7 @7 s7 _ F' ^; |
va.vt = VT_I4; // 指明long数据类型; A3 m0 g8 B4 r# z, ?) `) I( n
va.lVal = a; // 赋值 9 l& s `+ e* [- \9 f% i
" i6 C, z+ V: D+ L& a: C
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
1 `3 l9 }2 f2 R' ?
8 E' ]. D/ P: E! s" v2 {( h VariantInit —— 将变量初始化为VT_EMPTY;
+ I, |7 v. r/ l3 |+ d5 x3 ^$ M: H
3 m0 q6 q6 |- r" c* B VariantClear —— 消除并初始化VARIANT;
( R T- c2 E0 I. [. \% m8 q2 K6 j4 r3 o: t0 [
VariantChangeType —— 改变VARIANT的类型;
" r; d$ B3 X/ V5 y. {/ l* Q0 ^5 _' H$ Y, L
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。% S6 c8 O I# j7 {9 d
$ W2 ]) p1 }6 @" M" g! d COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:% g; ^) I7 Y# X- X4 h0 _
" T! M- E2 P$ U t8 G
COleVariant v1("This is a test"); // 直接构造" q/ q) g1 p& U3 N
COleVariant v2 = "This is a test"; 2 y# G! v8 i6 a( q7 \
// 结果是VT_BSTR类型,值为"This is a test"
2 D0 \3 ?/ P$ }9 J7 }$ b6 ]COleVariant v3((long)2002);
3 h+ P e1 M8 ~* f# s3 WCOleVariant v4 = (long)2002;
) d8 ?- p& u- o8 L9 `5 z# C// 结果是VT_I4类型,值为2002 % ?' Z8 b% t+ M6 [
5 }, Y1 m0 ?3 B( f
_variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:8 l% L7 e# A% }' H; T
6 G- f, D) l, e3 H* G, _$ r r G: l' _
#include "comutil.h"
' r; ]. X% e) S% j% F) h
% ^$ L2 V: R2 I9 Y! f) Q1 N5 G8 c #pragma comment( lib, "comsupp.lib" ): U* E. H5 {( ?' r/ L
! v7 J5 V3 Z3 Z, y
四、CComBSTR和_bstr_t+ `# }" d+ B. S% O: E* U! U' M) i
$ F, B7 U5 b; a+ G h" N3 Y. l CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:: ^6 R+ |2 T @- h! N- ~
2 x: T& ?- a/ e, O: P* e7 VCComBSTR bstr1;
' y, b$ a/ q( W- L4 k; nbstr1 = "Bye"; // 直接赋值) W% q# P# u# R S
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符$ V$ I; s& L% v
CComBSTR bstr2(wcslen(str)); // 定义长度为5
/ c2 }7 }) d7 xwcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
# X3 r" G9 ^* }0 C5 `' OCComBSTR bstr3(5, OLESTR("Hello World"));
7 ?) J& V* ?: X! r0 x& y) HCComBSTR bstr4(5, "Hello World"); * ^3 `0 u' P* v- R5 `# ]4 y! q. G
CComBSTR bstr5(OLESTR("Hey there"));
" a1 B, J1 O( M% [1 d7 hCComBSTR bstr6("Hey there");
" K) L. S9 Y: s: zCComBSTR bstr7(bstr6); : ?& a, j; l/ c+ d
// 构造时复制,内容为"Hey there"
+ E5 f/ M" x% o( T- }. y8 |
; v8 o$ }) u: A( U _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
/ @4 _& x* B, l; p7 d. d. d5 L0 E
5 Y) y7 |9 p( _: J" a( b9 m 五、BSTR、char*和CString转换' V0 r0 d) c0 R) w
* Y% z. z6 m# v% B6 ^0 V9 g e
(1) char*转换成CString
c: O0 j5 q" e2 F' X g( c1 J1 {& j1 t8 X5 p
若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:0 t& o; _, ?/ }# O6 Y2 v3 ?2 C; }- j
6 N& G1 I b @5 t4 F8 Y+ Cchar chArray[] = "This is a test";/ X9 Q* I; o2 H; U" ^
char * p = "This is a test";
- H7 A$ C8 _& \; y1 r/ x% O- g: @0 ^+ D/ N6 S% e
或
6 c2 b0 h/ m" d, J
% d2 @5 r. {4 G% h" iLPSTR p = "This is a test"; 7 W8 w, ?" C; N8 p1 Y( f5 P
7 l) S" y! U. c
或在已定义Unicode应的用程序中
0 e% Y5 B( ^6 L+ V. S! d, z0 s% O$ B" C3 G
TCHAR * p = _T("This is a test"); 7 a' S( i" f [- S. ]
; V* W/ W" p7 d# [
或
9 R9 b) F5 Y! q3 i7 }0 ]5 h. ]! X$ m9 N8 z x" X$ D5 e3 d
LPTSTR p = _T("This is a test");
# v1 y. x+ W4 [+ Z: F+ Y% T gCString theString = chArray;
8 L5 u) L; O7 J. l j& atheString.Format(_T("%s"), chArray);
# Z$ i9 Q* O; B m0 E/ s |% _( GtheString = p; 4 K- o( a# K3 U
" F- J9 v5 V2 h- `
(2) CString转换成char*$ F- Q1 v) L* Y, u" |7 f
% T0 f3 c( `. q 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
( r/ u; C9 T2 P; G4 U+ g' j& `% U! O5 g/ a
方法一,使用强制转换。例如:6 V+ x1 f# f" c8 }
8 s! H$ R8 w& d) E' _1 vCString theString( "This is a test" );4 {# y+ _. s6 b' F$ c z% J+ Q! x
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString; 6 R$ t+ r! t5 Y1 x1 ], G6 C
% p2 h( t) b6 k, h4 k
方法二,使用strcpy。例如:! \' _+ h* s7 V. p$ p/ ^4 x3 T7 n/ { c
, B( H+ P+ J, ?: X+ n3 c% K; L
CString theString( "This is a test" );6 m1 J F& v1 d6 Q1 R
LPTSTR lpsz = new TCHAR[theString.GetLength()+1]; m3 W+ P( c* c" S* h
_tcscpy(lpsz, theString);
, n7 s6 ?* `' ~8 y
8 [$ M# k4 S" t% \: @6 s4 j 需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
. Z8 _* ?6 G8 {0 o& O# L! w, o5 Q4 G
方法三,使用CString::GetBuffer。例如:
% A c. m# F; w6 Z+ r
% Z0 q& O& S# c' D2 S# d. j, KCString s(_T("This is a test "));. d4 F, K: J$ _, h. h; d
LPTSTR p = s.GetBuffer();
0 d8 F/ v. f0 |3 ?1 s4 | q. I, Z// 在这里添加使用p的代码
( S) \6 O w( l( g A+ Uif(p != NULL) *p = _T('\0');# w1 E: t/ i) R+ x# E8 L3 `
s.ReleaseBuffer();
2 W# h; w" t2 u" ^- K3 X// 使用完后及时释放,以便能使用其它的CString成员函数
- T ]) _ H6 Q$ U
7 r/ X I4 ^: S( {# O0 ` (3) BSTR转换成char*% u; k* X" g' u( u- ~, F/ h: @1 _
! D4 n9 m: }1 S, I
方法一,使用ConvertBSTRToString。例如:
) \) Z. E K3 r, D: b* o+ C
# K. I' C! P1 y9 `: C, c#include
( I8 q' |, H2 Y+ ^! o#pragma comment(lib, "comsupp.lib")
7 R- V/ P d3 D% d- }9 c' e& E, `int _tmain(int argc, _TCHAR* argv[]){1 W) }. }# j0 H
BSTR bstrText = ::SysAllocString(L"Test");) R; o2 \/ i4 z ~5 C( e# v P7 L8 |. r
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
; a2 p$ z1 D- JSysFreeString(bstrText); // 用完释放+ K" `6 ], b( E8 f( }
delete[] lpszText2;
# |5 Q# F4 L: `& C+ u$ L# Treturn 0;
' E7 o7 M% l& [ d% Z. x} : I8 X1 j. k8 l8 r$ L- i, n6 y
! a" m3 Y. d1 g5 ~: x9 P
方法二,使用_bstr_t的赋值运算符重载。例如:5 P1 b* h9 F# P' I8 ^
3 n: x0 U- w* {8 Q
_bstr_t b = bstrText;
7 i. U8 ^' {8 N1 w) ~char* lpszText2 = b;
3 l$ ]" X# N1 Z" E. K) ^
7 d- n! J$ Y/ N. I( l& [7 A+ l/ x. ~ (4) char*转换成BSTR( U6 E- C9 R$ o/ E4 E% o
. Y. L- |' F$ p7 X/ g( k
方法一,使用SysAllocString等API函数。例如:, s! E+ r& B$ u5 A7 y4 e$ i) J
; ]) C& u3 ?( F5 m: I- K
BSTR bstrText = ::SysAllocString(L"Test");
7 O" o1 E, x4 D$ ^1 aBSTR bstrText = ::SysAllocStringLen(L"Test",4);
: u$ O f7 s# X0 |# z1 a( XBSTR bstrText = ::SysAllocStringByteLen("Test",4);
6 a0 _1 k0 |- ]# R! t) S3 K
8 i/ H" k; E! {" X9 B 方法二,使用COleVariant或_variant_t。例如:
* s$ l6 w+ F! l' j4 G2 F5 M L, W5 S: W0 `+ M: Y4 T
//COleVariant strVar("This is a test");
3 n9 ]/ l' I1 z* a_variant_t strVar("This is a test");
8 h8 W. H; P5 |/ }" HBSTR bstrText = strVar.bstrVal;
/ j( d9 u: v' h6 R6 [* f+ @9 }6 D8 m( p8 }
方法三,使用_bstr_t,这是一种最简单的方法。例如:% |0 q" Z9 ]$ u9 t" Z! d$ T
" k# Z) x$ `- rBSTR bstrText = _bstr_t("This is a test"); ; W+ R( Z- _4 x8 d
. I w8 ^6 y" H: e
方法四,使用CComBSTR。例如:
7 }- A/ E8 z& h1 _, d( l
" G7 X8 {9 w0 k5 h3 j- U# w4 fBSTR bstrText = CComBSTR("This is a test");
$ Q& M. C1 {$ R2 d- K
O: h2 [, d7 ~! K 或$ x- O) e3 i0 _) q: W' y" a
2 K% Z8 b q/ H4 d# h$ f
CComBSTR bstr("This is a test");
% r8 Z+ q3 N; J7 d- uBSTR bstrText = bstr.m_str; 0 ]0 _5 ?. Z! V; T8 f% H% L, R% f
7 T! [. Q* `/ i% c( R: {
方法五,使用ConvertStringToBSTR。例如:
8 @8 A* T% {' g" y, Q6 x; D
8 K# b; V: X7 E* `char* lpszText = "Test";
( T$ G* L7 G9 ]2 J* R" [, H' b; xBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); * ~! t. p* i( b$ d& ^- c
# n' K& c9 |) I$ j (5) CString转换成BSTR
8 z/ j1 C M. G+ I* ^/ n, Z7 L
1 m X! O1 s0 X$ _; j/ B 通常是通过使用CStringT::AllocSysString来实现。例如:
* J- U9 s) ~ Y L& o
% \& j/ D8 d' g8 X+ \CString str("This is a test");
7 F, ?/ S) w8 E$ i- eBSTR bstrText = str.AllocSysString();
8 M+ s6 B( B3 l) A2 `4 v( f0 ~…# f& @% s. v% _6 ]
SysFreeString(bstrText); // 用完释放
1 [. E1 \5 ^6 ^8 H* K' j
/ U y5 I! K2 ~ (6) BSTR转换成CString/ ^2 [" Z* q- a# i
& W" _" D8 U$ r: H- Z1 Y3 G 一般可按下列方法进行: _! }) O ^5 M: ?3 w
1 H m5 W5 p3 p) pBSTR bstrText = ::SysAllocString(L"Test");+ ?- O+ r4 D$ I4 R' q9 I* i
CStringA str;. b1 p Y! C5 k9 K
str.Empty();1 u2 Z7 L+ g6 g [/ M
str = bstrText;
# S0 k2 s) E; S0 w
9 F3 a# S6 W5 n: q ~$ k" B! B 或
' K g `9 M6 R( k5 U7 g- U" K/ l7 l8 _' M! ?9 F M
CStringA str(bstrText); ( |$ m! h- T5 }+ a
b1 P3 V, E6 r# Z, s8 Q% W5 W; c (7) ANSI、Unicode和宽字符之间的转换
( z' Q. A9 r. x8 w" Q" w6 C0 ^8 N; f% J
& L" e! j w. a" X6 W0 O+ b 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
5 a) p: y( A. @* N, G; K, z) u2 Y
方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:" ^' q. T0 L$ K6 g
+ j+ k! y5 F g" l- F: g: H
TCHAR tstr[] = _T("this is a test");
0 g" X. f. [; E: f: I! Mwchar_t wszStr[] = L"This is a test";
* Q/ T" g9 d* K8 W8 aString* str = S”This is a test”;
6 }8 E U4 a+ W( i! A' Q5 d) l+ I3 ^$ i' ~2 L
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
, d3 \4 \; g% P. H
$ ] a3 k _9 R- ^+ _! b 其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
0 Y) K* W* t5 o& Z9 S
- O; ~& [, v ?5 y+ r+ w4 FLPTSTR tstr= CA2TEX<16>("this is a test");$ d9 [; }7 }& p( v; R% n W
LPCTSTR tcstr= CA2CT("this is a test");$ s' T5 q& H1 V7 w" \
wchar_t wszStr[] = L"This is a test";6 I/ ^! n/ Q. o( q" o h/ t( v
char* chstr = CW2A(wszStr);
8 \+ S' f' z- m& w
6 j" k f& u- j7 H: W- V+ z: A 六、结语2 M4 v ^! f" O
7 s ~4 C$ [& }2 @0 K+ F# Y; ? 几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|