|
|
2002-12-05· ·丁有和··yesky
c; B* @- N: x 3 M$ @& Y8 Q& z0 ~ s: s
6 T: i% ]( y. q: }0 w5 z1 R Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。' k# k1 m, i9 c8 }- C
) |$ H6 H2 `/ F6 E5 t 一、BSTR、LPSTR和LPWSTR2 J; ^4 b; B0 H! H
& [6 l" ?( z8 @" |2 R
在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。) c% F; B4 D' v9 F7 ?
7 B& f7 a- L4 \9 n7 o( e! S Q 那么什么是BSTR、LPSTR以及LPWSTR呢?
. |" c: [. q" `8 l( A M3 M
1 z6 W& c Z! }) u BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
9 y$ G" I4 f% a6 v! a, p' q; {" e
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
6 Z ~4 I# n$ e' `; Y) i
) ^4 c( t2 ~- }( v' o 例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
@% \& F# c% P. w6 U4 `
; Q7 ?. D% }4 z* L$ x3 f 一般地,还有下列类型定义:
' b3 L4 P( N8 n4 `1 m! `$ Q: P" z* p) ?
#ifdef UNICODE
+ E3 P n. V* A1 q; M% r l; g5 ` typedef LPWSTR LPTSTR;
( Y) }; j/ G) E* P typedef LPCWSTR LPCTSTR;
0 N0 I' c: l& v$ D! a#else # A B& Q; |2 c, R; M x/ x' C$ P2 o
typedef LPSTR LPTSTR; ) d* H! i0 X) g4 m# t3 j
typedef LPCSTR LPCTSTR;
1 l) `% E1 x6 z; J9 C* L" f#endif 9 v+ l) D" `! u
, f. I4 ~2 K* {8 B& a
二、CString、CStringA 和 CStringW
* H% P& _" R/ y( t& o c2 p: f; c/ _9 t" r6 k6 e- T- e
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% O& j& A0 Q& {6 c/ T
' Z! f+ [' b" c. \) y& B 三、VARIANT、COleVariant 和_variant_t8 M( r' z" v; G' q
5 E O6 b0 y! {+ v: G 在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
3 x: [5 p; j2 z2 S% S" m2 L6 C
0 r; N# {2 N* z" a! o: ~struct tagVARIANT {; E$ W& C% L, |8 P/ b
VARTYPE vt;
- F" K1 M' K5 t0 Q+ q union {
* u# @+ y2 N1 N/ ^4 b* U short iVal; // VT_I2.+ N* M# D* I$ a& u$ E/ ?% D
long lVal; // VT_I4.
8 K2 B& ]1 p2 Q! g+ }6 y) B$ x# ^: w float fltVal; // VT_R4.) @- G5 Q3 E0 N$ Z7 Q
double dblVal; // VT_R8.
: M( C- \9 g. j* b' I# G% M DATE date; // VT_DATE.( e- X" T J. W# ^7 [
BSTR bstrVal; // VT_BSTR.
# a) n7 q. r/ y9 { …
' Q8 Q. w- }- w% s! J7 L8 w short * piVal; // VT_BYREF|VT_I2.
) n# W3 f: V2 V% X* F# r: S3 n+ F long * plVal; // VT_BYREF|VT_I4.- J7 `6 P5 N! B0 R4 I
float * pfltVal; // VT_BYREF|VT_R4.1 v3 ?4 J: Y! P
double * pdblVal; // VT_BYREF|VT_R8.* Y3 i! }. Y' Q5 g; W
DATE * pdate; // VT_BYREF|VT_DATE.( `& a! G) N( a/ G
BSTR * pbstrVal; // VT_BYREF|VT_BSTR.4 G6 R @: k' {2 b i, @
};3 `6 v2 o( s- K7 p# y) Z; [
}; ' n+ n9 b: ^8 v+ E- p7 F
& H& \' J, T5 q# i: o0 e5 V
显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:0 Y8 O) o+ _- H9 G6 j
( P- o: M" `$ b; c, JVARIANT va;+ Q7 E7 `: x4 _0 O+ K; p
:: VariantInit(&va); // 初始化
2 Y0 B. y! Q0 i: B: s7 x! Q8 Sint a = 2002;) u3 m9 M. |- I
va.vt = VT_I4; // 指明long数据类型7 v) U+ g7 [1 T
va.lVal = a; // 赋值 ' |0 P( w+ j1 g3 T+ j
7 d1 b0 U# h- ^ 为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
- X- f2 K: Y# f3 W$ `% C
% d8 m6 q& c7 D VariantInit —— 将变量初始化为VT_EMPTY;
% ~$ ~/ E4 L/ _: f" T$ ?, \7 ]' t3 Q
VariantClear —— 消除并初始化VARIANT;
) S* P5 u' D# R$ t2 D4 Q1 _! j0 H8 j" I$ [8 x; n, y; J
VariantChangeType —— 改变VARIANT的类型;4 J) o0 y/ C8 Q
8 C" b4 F' @. M! H8 L2 R1 h& Z1 ^
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。$ h( Q L* X$ M: {8 k z% c
7 J# n& m# p+ k; R4 ~8 E
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
1 x% D" r7 A/ k5 f3 v" Y# s/ a! b( L3 U T+ e+ ?) Y
COleVariant v1("This is a test"); // 直接构造
; T Q4 K) J$ s6 Y8 wCOleVariant v2 = "This is a test"; $ U$ k% C; t- H( ]7 E0 x1 `
// 结果是VT_BSTR类型,值为"This is a test"
: T. `1 I/ X% V7 wCOleVariant v3((long)2002);
( N9 r- r9 E( V8 I" d2 [! m$ i& JCOleVariant v4 = (long)2002;) C7 A2 b# ]& o( u$ Q
// 结果是VT_I4类型,值为2002 ( m+ g* }; X0 R0 t. }6 P' \
1 m& J* b& \/ V$ t _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:! q" a: W; [7 G. q: X0 V# r
( m0 ]- T9 x/ y7 r
#include "comutil.h"
- e* S: Z8 B% a7 M u( B3 b
3 Q6 K2 l* _9 v$ k9 v: k" g #pragma comment( lib, "comsupp.lib" )
) j0 G( U+ m) l" h7 i! i
7 q4 S: ?; N ~2 Z3 l 四、CComBSTR和_bstr_t- e+ h3 R! R/ u8 d& J) |" M
+ t, @, J/ v6 e& S/ O CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:# N! p0 w7 T3 h) v m
3 W. M7 t7 }+ M9 ^! m& n* jCComBSTR bstr1;
7 ?. X+ u; Z! V+ ^ e5 y1 Ubstr1 = "Bye"; // 直接赋值
4 x; _0 ?2 w: iOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符1 S6 ^1 J+ B- V3 B5 \! G6 f
CComBSTR bstr2(wcslen(str)); // 定义长度为5# `/ L e. l' B; j6 G* t" w1 A. Z
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
4 Q8 E6 Z6 Q; mCComBSTR bstr3(5, OLESTR("Hello World")); / c% a0 A! ?; \; N0 q- V
CComBSTR bstr4(5, "Hello World");
5 o8 ?7 Q0 ]8 ?$ \+ X& N7 PCComBSTR bstr5(OLESTR("Hey there")); 9 P2 T' L* D; m/ ]" ?
CComBSTR bstr6("Hey there"); ) {' }* x) a! m" v1 I
CComBSTR bstr7(bstr6);
8 n+ b% B3 ]9 l// 构造时复制,内容为"Hey there" 1 }8 C8 r0 x4 B7 h! ?6 g8 v
% W, H1 T, X) ?
_bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。% n' L, o2 t0 q! C
) l n/ x4 f W7 f6 N) g
五、BSTR、char*和CString转换- Z' e" z& t$ @/ ]4 ~) m
1 y ]( |2 H5 L6 a" m, i! { (1) char*转换成CString
- `# p7 a/ y" q5 \% v0 U% V5 T" B5 K
1 I2 z+ u7 Y1 e4 V: l1 e 若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如: z, h' |7 W) \2 [2 @
' ^( V( s8 }4 J1 h& x" \
char chArray[] = "This is a test";1 l) g3 S% Z* I' b: e1 T
char * p = "This is a test"; 4 _* C6 ]/ w1 C/ r5 L/ w+ O
% f) L6 Y: y9 x' Q! q7 J3 ]9 _0 ` 或1 Q0 i3 m [/ }8 K4 ^
' G! n( f) q0 W- ?4 f- P
LPSTR p = "This is a test"; + p3 n2 l' O0 u
7 w: u6 G$ Z( y4 z4 G/ ~1 Y: Y# _
或在已定义Unicode应的用程序中5 [0 ^& t& C# u( ^' F
7 l0 m& ~9 O2 ~" C" w, iTCHAR * p = _T("This is a test"); 1 `" {7 O [- M0 T
/ t& `1 c( n" o2 _" u" m
或
! M6 s) g4 {4 M2 p
' d4 g X% C5 c" d% h7 p3 ]LPTSTR p = _T("This is a test");
- l& X$ f4 }- N w) i/ S PCString theString = chArray;1 k6 R/ }8 `& d w6 W4 d" z! C0 x
theString.Format(_T("%s"), chArray);
3 k1 ?: _4 H+ z2 S5 @# H, C& WtheString = p; : }9 @4 S" E( x/ i2 ?, K$ c
) z* F" Y$ a! \; Q4 h& G6 U
(2) CString转换成char*
0 X) d$ R, u" O# P! `9 U0 b
, c5 P u' d, d: y% C: Q 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:( L) M; I# N6 P- s" M; V* F
/ T; L4 L7 S: J1 h4 [2 X
方法一,使用强制转换。例如:
2 K" m5 ?6 S% F' {4 f9 G: m4 f# G3 ]2 M) H2 n$ \" G
CString theString( "This is a test" );
w& h a0 ?0 X, ELPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
- \1 l( x9 Q5 {4 ^) Y* O
1 Z; c" R! y% L7 t' I5 E 方法二,使用strcpy。例如:$ `8 `9 K0 I- I
' U! @3 ~/ ], o2 M9 z9 \) L) R
CString theString( "This is a test" );# d. ?0 I) n o% j6 O6 C& y, S2 e
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
) e3 M5 d% y: T# e_tcscpy(lpsz, theString); ' j* t# Y1 o8 s* e0 V, U8 t
9 J8 `6 \& |$ H8 B4 Y
需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
3 a+ L0 E( Z4 k$ ?1 w7 Z9 e" s2 t% ]& w* }5 b1 g
方法三,使用CString::GetBuffer。例如:
( `/ i5 w, {1 D: r6 L! i
/ [/ y7 z- X0 l, A% k( S" R) vCString s(_T("This is a test "));
/ A4 J: }8 r% L" ELPTSTR p = s.GetBuffer();
8 m# [: L0 j6 U% j// 在这里添加使用p的代码
5 X, K4 r. \: M! `2 I% ]! h- Pif(p != NULL) *p = _T('\0');7 {6 l8 r5 a/ _0 X0 `# p
s.ReleaseBuffer(); p9 P6 `1 N) v: u5 {% u
// 使用完后及时释放,以便能使用其它的CString成员函数 W) t- l1 U* U8 x& I. q
' a8 G' x4 b6 @7 }6 P& j (3) BSTR转换成char*0 E" D' n) O! c
6 r0 m7 A2 N/ M
方法一,使用ConvertBSTRToString。例如:
$ q/ H* H& E [# a' h L2 h) d# f! _3 C1 ~/ X5 M
#include ( l$ i) k3 A1 J& a7 M% B5 }: T
#pragma comment(lib, "comsupp.lib")2 C ?4 S. G! H2 e2 A7 W
int _tmain(int argc, _TCHAR* argv[]){: `& s! a' f- w
BSTR bstrText = ::SysAllocString(L"Test");& c0 V2 J& @( i% F% C
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
1 P( ~( M+ I& [, e7 y, ^* XSysFreeString(bstrText); // 用完释放
& V. G) h Q g) Sdelete[] lpszText2;3 `' C# p6 _) V
return 0;5 V) [) _/ u# g( j
}
0 R( e7 J) [/ C& Q/ R8 w8 q1 X$ f
方法二,使用_bstr_t的赋值运算符重载。例如:
; M4 X5 m, {& w" w4 O2 ]6 k% ]4 t' i
( L! x; Q& s( z3 ?7 d: |_bstr_t b = bstrText;
6 h. b1 | s. ?0 ichar* lpszText2 = b; 7 d7 W+ o% g$ |
2 r, N( t, Y' P3 I (4) char*转换成BSTR* A8 G4 \: r. ]8 ~
_9 W& X! m3 w! c- o4 D 方法一,使用SysAllocString等API函数。例如:
9 \$ i3 I2 h8 w4 p+ q/ T
& L% v8 j4 H5 A5 a) J) _- F! A9 ZBSTR bstrText = ::SysAllocString(L"Test");# j, p8 m+ `( z7 _
BSTR bstrText = ::SysAllocStringLen(L"Test",4);
& z& O; _$ k) I% g; e6 yBSTR bstrText = ::SysAllocStringByteLen("Test",4);
0 c% I0 o! ^; V* f. Z0 q5 S( {/ ^: b( i$ t3 P' Q
方法二,使用COleVariant或_variant_t。例如:
" |# u6 H# b! L0 x' h& U j7 z4 @1 E/ Z% w% ]& n7 I' v: T; }$ Z
//COleVariant strVar("This is a test");# k) u0 o; t; S
_variant_t strVar("This is a test");
1 [. L8 A- J9 F+ s- `0 L9 KBSTR bstrText = strVar.bstrVal; - ?6 V8 U2 T* }/ B. Q
7 o" U; R! @3 J1 P& ^* F 方法三,使用_bstr_t,这是一种最简单的方法。例如:, v( J* h# H7 e9 i" f- e
% n. L" A& ^/ R' p
BSTR bstrText = _bstr_t("This is a test"); , t! C& g$ J4 u6 Z* q
+ }+ j) y4 }% R4 k; r- m8 t& f! T 方法四,使用CComBSTR。例如:. B- X( Z2 H U8 i
: T' f+ |5 s4 ^3 [2 N1 lBSTR bstrText = CComBSTR("This is a test");
. ? i3 i2 [0 K4 O& G& @" T( M
: v2 C5 w9 p/ g1 G( S) x: i 或
4 S% N* P6 j$ ]8 ~
% r9 ~6 @# o" r$ P. V& m- KCComBSTR bstr("This is a test");
; ]7 j; k% K6 A4 qBSTR bstrText = bstr.m_str;
) K) ~# S# m6 Y2 r) Q. J t& Z1 R# @
方法五,使用ConvertStringToBSTR。例如:
; W& r- {. J9 a# W+ A9 j5 J9 m% b% Q* l. j2 [* {, K" B3 R
char* lpszText = "Test";. a; R6 g& i' U- j( |! b$ `
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); 6 n7 |" q5 A6 b
1 U2 _ V9 o4 [6 M; f
(5) CString转换成BSTR
& F9 F) c9 l* s7 o0 I7 {: U) H) A! @4 L ?9 K' N- L2 _! o3 G
通常是通过使用CStringT::AllocSysString来实现。例如:
; M; {, R2 R( k& q7 `& ^: _, F! H L6 y0 Y3 o4 J5 H5 f( k9 o- ]
CString str("This is a test");
& ^, G+ |9 J- G8 s4 l0 zBSTR bstrText = str.AllocSysString();9 y& v1 @: X1 \3 j; U0 \( R
…
/ K9 |& S- r$ eSysFreeString(bstrText); // 用完释放
& J/ M F6 n3 R$ A* F# B) o3 C% ?6 R/ P' I4 T( U) ?
(6) BSTR转换成CString
2 W n1 T$ ]8 U c& ~3 T6 V) G2 Y; k7 x
一般可按下列方法进行:
7 {9 ~( p, }% \( u. I6 T; z0 \* y( u) K2 [. F& X3 f
BSTR bstrText = ::SysAllocString(L"Test");
0 ?, K; I: d; d% D& _8 [% zCStringA str;2 h2 ^ b( Y/ S4 V% R
str.Empty();& y; D+ V/ \3 q0 O
str = bstrText; % D6 r7 g8 l9 }1 d; c, w @
; t r& n- B7 Q
或
; Y! N- m5 ^: e" c3 v- g# |+ y- K u, }" n6 g
CStringA str(bstrText);
' g+ Z. [1 n( P a4 q* r+ O- K* p8 o
(7) ANSI、Unicode和宽字符之间的转换
3 q; J8 `0 {0 \/ X! K. H" v) l0 n0 k9 ^
方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。1 h$ A" \. k( o
- j) Y. ~- m) Y) i- |0 {/ B
方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
/ P. ^( Z7 Z6 J- b0 q1 Q. r
. v+ |7 ]2 e; mTCHAR tstr[] = _T("this is a test");) p8 D1 @) }: I) D4 J
wchar_t wszStr[] = L"This is a test";
& E# ^0 f* h; l9 A, T$ f6 YString* str = S”This is a test”; 9 \* W7 K$ X6 h
. y8 J% q% ^- b& h/ q, ` 方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:" ^2 J1 O3 U H
5 u" `0 w- L0 {) V x. W% H
其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:. ^. ~' |" n# u& N: K: y
" ~5 R8 t7 i8 m! JLPTSTR tstr= CA2TEX<16>("this is a test");* B" Z" Z- f/ J" |
LPCTSTR tcstr= CA2CT("this is a test");
# ]& d! m' c8 hwchar_t wszStr[] = L"This is a test";
/ W' h0 `% ]0 v/ i3 T9 f. [4 p0 c: m& ichar* chstr = CW2A(wszStr);
- U/ _" ?$ M8 ?8 c, Q% g& Z# c. ^
0 Y& u# [" ?$ |" a/ x5 T 六、结语1 U* z9 k: e. F9 s6 u, i
0 i2 U" k+ u8 ~1 s 几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|