|
2002-12-05· ·丁有和··yesky
3 j E- d: B/ {9 o* P
2 _( T6 [. Q4 B7 d; m) M; W; ?; m8 Z% w
Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
3 G0 I2 H8 g( r( {7 Y) T* k. F8 L2 i+ i7 ]
一、BSTR、LPSTR和LPWSTR+ v" |; @% b2 l5 a, W
2 `% t+ ]7 S! Y+ q4 R' O
在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。/ i) Q- N: F; d! ?4 y. o
K5 X6 [2 P3 d5 E
那么什么是BSTR、LPSTR以及LPWSTR呢?# O- g/ x" N! O9 |
: F( n) x1 H9 E' P
BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。3 D% x& M( I: ?6 ]' N5 v t2 F! z) p4 r" F
& U) g- P2 X5 s* K g$ i* K LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。$ X; O0 x" Z' `/ k+ z
- \* ?. b& Z8 L& S& U
例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。6 D; W" c% H' q: F
& z% t& ?7 ]' H- ^* a: V$ }
一般地,还有下列类型定义:7 R4 K; G& i" K6 r5 z! h5 u
" J, D+ _# T/ O0 p3 H#ifdef UNICODE
! c5 l2 C v& S) {& X, n7 H typedef LPWSTR LPTSTR;8 R9 \) ~$ i+ a: b
typedef LPCWSTR LPCTSTR; ; V# W' L9 O G& G: l$ V
#else / F, k @5 J" ^0 z4 ^' l& m
typedef LPSTR LPTSTR; $ d! R& p! }2 p7 ^$ P9 b3 C0 z/ r3 c
typedef LPCSTR LPCTSTR;
% T' ~! u) G7 n' y8 |5 A) g#endif
e* G+ E" @. w% {( @7 j- h6 k7 @/ ?: H; Z
二、CString、CStringA 和 CStringW
" T! a% \% f( j- T0 C; s9 t7 y7 a `) |; c( Q8 I/ V/ r- S
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' t& Q+ ~# B- O0 v+ `. c. ~
6 ~' c# A$ w m7 D5 n 三、VARIANT、COleVariant 和_variant_t
( A& |/ P, _/ `! v$ ^! l2 r3 b2 @) M# N2 B" u6 \
在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
A( N0 Y ~* {% i. h- F
9 _- N2 a! M/ U, i+ T1 O( z8 Y2 xstruct tagVARIANT {
) ~, D* h( E0 L, U7 Z* d7 l# Q# ] VARTYPE vt;
% g# h4 M! T9 n/ B: Q# [8 E union {
* X6 N; n/ O9 e, I7 n% H4 Y short iVal; // VT_I2.
1 k$ |0 {( x3 ?7 J! [8 m- }0 `% @+ ` long lVal; // VT_I4.9 D' `" U3 ?, j3 x$ ^
float fltVal; // VT_R4." Q- E6 D+ G: C0 v
double dblVal; // VT_R8.
5 z4 H: b4 o0 x% }! j7 P DATE date; // VT_DATE.
; N: V4 |0 n9 i, n0 q c BSTR bstrVal; // VT_BSTR.
3 U2 T$ h: e" M6 r \. q …0 z' K, R; h8 w+ o1 e! T' W2 }
short * piVal; // VT_BYREF|VT_I2.
" F2 q" n! t" ?0 I; d' \$ b long * plVal; // VT_BYREF|VT_I4.
6 n! o$ Z4 ?$ _' Y float * pfltVal; // VT_BYREF|VT_R4.
) r# { l6 b$ E# g+ f double * pdblVal; // VT_BYREF|VT_R8.
- c" }* l! |, X) J3 `& N DATE * pdate; // VT_BYREF|VT_DATE.. E/ V( v8 [4 Y, d9 ?
BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
1 X1 [7 p$ g- s+ R) S };7 N K& P/ T8 F
};
9 S9 [- `5 q# _8 L+ Y. y9 W
4 g6 E; o1 N4 P6 m! e: f 显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如: F1 O: |# k1 b. h
7 b8 x+ I9 J9 w2 R' g; o
VARIANT va;6 {# t3 B' Z8 z( o8 O: j
:: VariantInit(&va); // 初始化0 @$ G7 `) f( @- g- a) M
int a = 2002;1 X! P- k: o% x z' x# o' r5 Z* o- F
va.vt = VT_I4; // 指明long数据类型, ^5 Y5 p( k. o
va.lVal = a; // 赋值 ' N7 r) `! y8 ` P% D
X% q/ L+ N$ P
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:( k, g$ k; w0 H* }! r. ^
# ?/ a3 x2 [# r2 h3 n4 d8 m! I, [
VariantInit —— 将变量初始化为VT_EMPTY;
2 r/ d+ [5 o( r! a: ]' q3 e% ` j1 {/ ^ r. g; Q2 `
VariantClear —— 消除并初始化VARIANT;
, F ~" a3 M& N" k* i/ Q) W l8 w' T! Z
VariantChangeType —— 改变VARIANT的类型;
; k& P5 ?4 e- I8 H; _9 y, \3 A z3 K# U7 t2 H0 r7 M1 L! \ `( ]. K
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。3 Y( b: A& Y' q- M7 ?, k8 V* c
9 N* t% \. n6 e' Y2 }
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:2 y( Q3 s$ r5 k U! e! [
3 J5 B" {/ j0 Y- y5 G% u% F6 h- j
COleVariant v1("This is a test"); // 直接构造6 o$ g) s0 K- J
COleVariant v2 = "This is a test"; 3 C6 Y; ]$ n+ H
// 结果是VT_BSTR类型,值为"This is a test"7 h" e# M% b% n, H' k: X
COleVariant v3((long)2002);
! r {% Y. s0 D7 \" NCOleVariant v4 = (long)2002;7 d" g. C$ ?, z5 o
// 结果是VT_I4类型,值为2002
2 \9 h; ]7 V0 {* G- F6 y2 K/ G5 ^
2 i6 d u0 L$ a2 y- A! G+ k _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:2 J$ f! |+ Y$ ]) [
( b/ e1 H4 K4 t) U# `+ `0 I8 G$ r
#include "comutil.h"
" a" K, f& J O- g# F6 {, U: y/ O& s; K- v% ^
#pragma comment( lib, "comsupp.lib" )$ m5 U; A0 S$ b; U4 s
& U% T: b- [' f" T7 z* A1 | 四、CComBSTR和_bstr_t
7 O7 ? X# j2 h7 S/ S$ E- D+ ~% s, q) C8 B0 T @
CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
/ w6 Y2 c8 W, E9 E) Q# v( P- x- g0 J2 f
CComBSTR bstr1; - Z1 N5 q# U5 _3 j. o
bstr1 = "Bye"; // 直接赋值
5 x8 b5 `0 D6 \; EOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符5 ~# R3 _8 r4 [
CComBSTR bstr2(wcslen(str)); // 定义长度为52 l/ A$ t9 |, m$ y- v1 G! K. j1 N
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
$ Q3 {+ ?" k+ R% [/ jCComBSTR bstr3(5, OLESTR("Hello World"));
$ t3 t N( v: s1 M1 A& tCComBSTR bstr4(5, "Hello World"); % J/ o0 U3 |# ?% S: r
CComBSTR bstr5(OLESTR("Hey there"));
8 V7 {, e3 W8 e5 rCComBSTR bstr6("Hey there");
- x& {0 e" C* g* NCComBSTR bstr7(bstr6); 1 C. ?. C8 e3 C
// 构造时复制,内容为"Hey there" & f7 A8 ?' `9 t3 g
- p0 w2 w5 M0 e7 e+ I) _# ] _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。- x; l/ x& @* R
7 V' q- D1 K% H$ |! I5 v& Q2 G; r 五、BSTR、char*和CString转换' o; b9 t/ Y6 o& E1 z
' j! A' A; p2 G( n- P7 l
(1) char*转换成CString o. J# U" E9 _" k+ d% e
" Z1 |/ C0 W- G9 M6 Z+ L8 @! a/ R, u5 m 若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
# {. J7 M, J9 L# F
K9 [3 Z0 V2 ?$ Vchar chArray[] = "This is a test";) ?0 F4 J) z1 t5 M2 ?: U0 b% g
char * p = "This is a test"; 4 _) F* g6 {( B5 C r
* I V: `; t/ U* M# I8 J4 p
或
* D0 G4 h: [( P$ w3 u) n$ I; e( e/ n* J9 q) _
LPSTR p = "This is a test";
5 _) h" W" o/ U# ]8 M* W$ W$ h& t# @% {( H7 Y2 q, X
或在已定义Unicode应的用程序中' F2 y3 z7 O: Z5 q
, Y; [' K: e* `2 lTCHAR * p = _T("This is a test");
8 r/ b, N' T5 Y1 t$ O) U% C$ c+ j# v4 J- F. n0 I
或. {; a8 U; G' p6 K3 ]. o, z' ]2 j
0 p3 f2 Y" t4 X' Y+ qLPTSTR p = _T("This is a test");5 Q( c6 D9 B' J
CString theString = chArray;
7 W! d% d8 r3 \: m) X$ n7 htheString.Format(_T("%s"), chArray);
% c- Y, Y3 Y$ ^3 v* C. j; qtheString = p; 9 {: |- G3 O) e6 \. s2 d
8 x$ A: \, |! u/ X4 M& a2 F (2) CString转换成char*
5 Z. @! m8 B( d8 q3 ?
0 @4 X5 s2 A- l0 l* c1 E$ N( B 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:+ G) ]! S+ K+ [% O. p, H% U! b
4 o9 F6 z. g( W$ U4 t
方法一,使用强制转换。例如:
; L2 G+ e- _) s3 T, m% Q5 Y5 r. n; e( P5 x
CString theString( "This is a test" );: A4 ? f8 z2 t/ a* k
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString; / D2 ^. h5 |5 m- u! z
C( U& @7 L* i8 @$ V0 K
方法二,使用strcpy。例如:
+ I' U( X9 W2 l! o
, e1 i" W+ U' ^+ [' D( BCString theString( "This is a test" );
4 \- D4 s/ V, } G% s( CLPTSTR lpsz = new TCHAR[theString.GetLength()+1];
1 o) f+ [$ J- F7 _, B_tcscpy(lpsz, theString); # _% H! L1 w) u, U, T! {
! D3 X: _9 E( N8 | 需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
6 D$ n' }& a! Z' ^2 g: C5 t7 ~: N1 W3 t, M* z Q$ e
方法三,使用CString::GetBuffer。例如:6 |9 }9 f9 i3 G3 C
; T: ^- A% i) r- n% oCString s(_T("This is a test "));; N d: g2 u a% l' f
LPTSTR p = s.GetBuffer();
) q. @: S6 t1 H5 k( ?8 O0 g// 在这里添加使用p的代码
; r+ W' }0 T8 @. c% g' i9 _+ @if(p != NULL) *p = _T('\0');
5 h& l# a+ z$ c' @ x; g3 Ds.ReleaseBuffer(); / E- T" M- X* a% p2 I. t6 k
// 使用完后及时释放,以便能使用其它的CString成员函数 7 R1 g0 U! O5 B0 c
; c& X" v; _$ _3 Q (3) BSTR转换成char*2 L! S7 W& V0 @! k+ w( f
! M! \8 b! a' f2 c
方法一,使用ConvertBSTRToString。例如:
$ P, u- c5 @7 Z8 M: B# _" Y) ], F r q
#include . U- S- t( p( {% l: w7 r. r
#pragma comment(lib, "comsupp.lib")
8 U6 S) r* `. o- o/ }- aint _tmain(int argc, _TCHAR* argv[]){
; s1 s% k: w$ V$ Z$ WBSTR bstrText = ::SysAllocString(L"Test");
8 P3 N% o g D+ _( j7 _2 k9 |5 vchar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);4 _2 S/ P# ]$ E" H
SysFreeString(bstrText); // 用完释放
, ~6 e/ ^% ~3 i+ k0 Q9 K% n/ O% T" Xdelete[] lpszText2;
; t4 c, S1 Q6 f' L9 P8 w' Xreturn 0;
9 p/ ~* N, W. c8 M, ]} + k% g* h; `8 V4 _
7 ]- B; s" x8 z- @" _5 k6 X/ O
方法二,使用_bstr_t的赋值运算符重载。例如:4 c( X+ X5 Q1 @6 x7 ]& ?
* R- y. w2 ]1 F& d1 g& F9 v_bstr_t b = bstrText;
+ y i3 @4 M4 m9 pchar* lpszText2 = b; - X$ g8 d* X/ ?# G' G" ^ M
" P+ `3 u0 e/ A- p* z9 S+ L% a% t4 Q (4) char*转换成BSTR
" }' Z" \* z) \' m2 I, ]9 c
8 j5 r) W; c' m& w [( M 方法一,使用SysAllocString等API函数。例如:
: O3 }/ l0 M3 l9 R/ a A! S5 Y- X4 Z4 c4 y
BSTR bstrText = ::SysAllocString(L"Test");, ]/ |) I2 M1 F, _" w5 z
BSTR bstrText = ::SysAllocStringLen(L"Test",4);
- ]1 U4 F" R* GBSTR bstrText = ::SysAllocStringByteLen("Test",4); 0 u) ^& k7 n: q
5 m/ t! A7 l* o% K2 v
方法二,使用COleVariant或_variant_t。例如:
4 m: b7 a5 m9 l- O7 p8 s6 O
6 {; G0 u/ n0 t+ |4 y+ C) d//COleVariant strVar("This is a test");
# X* ^* \/ Q5 r/ P9 N1 L3 t& Z_variant_t strVar("This is a test");& @! j, A7 P4 D& ?+ V7 ~8 P" r
BSTR bstrText = strVar.bstrVal; " Z! y* S) I. W0 N. w# s& Q5 C
% B3 ^( m$ @' [/ g
方法三,使用_bstr_t,这是一种最简单的方法。例如:
& E6 m' |# \% p/ _7 E; ]: b" a8 c* j7 z) x- w; T1 r) r
BSTR bstrText = _bstr_t("This is a test");
# |% j; x9 O7 G' N+ X( x8 H O. U; G0 t+ {+ P: b$ P! l# N
方法四,使用CComBSTR。例如:
+ h7 @) L$ x/ w3 X; X7 r! y6 u2 X, u
BSTR bstrText = CComBSTR("This is a test");
# N( F9 ]) a$ m" w, H# E
$ K, V7 N" S z# I 或
+ r+ G* K' Y) O0 L N5 c3 K! a3 ?9 z0 W1 ~( H
CComBSTR bstr("This is a test");
+ k; C( a: ~; S, HBSTR bstrText = bstr.m_str; + _" M+ M8 p* H5 t
$ @! O4 j: t) D9 `: l" | 方法五,使用ConvertStringToBSTR。例如:
. r f1 o8 r5 k; ~' x* R+ T, G3 n& m/ p2 s+ c
char* lpszText = "Test";
, n6 U0 E7 l' q s1 F6 bBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); 1 P. x- r# u0 z6 ^ m
0 w* z0 G* y' p! F
(5) CString转换成BSTR
+ Z4 r2 X C0 c1 O& c$ x* ^/ \5 I+ W' v* j1 J) ^# Q
通常是通过使用CStringT::AllocSysString来实现。例如:2 Z Q X& s9 |9 f! e9 L3 }
# m, }3 Y- F% C6 J8 v
CString str("This is a test");
Q. `2 ]9 L$ ]* G8 i0 e+ `0 D. vBSTR bstrText = str.AllocSysString();. Q. u: C1 f0 V* R$ v
…7 e6 @+ I0 X8 Q' c3 s
SysFreeString(bstrText); // 用完释放
( |6 ?3 K6 y/ C" b0 T
1 u6 O+ P; ~( l' s2 U7 p (6) BSTR转换成CString
2 v1 K% F x7 h" U% e. H4 M5 r* U+ y# r
一般可按下列方法进行:- V0 M! y, d# }/ i& T" }
$ }1 G: H6 K1 yBSTR bstrText = ::SysAllocString(L"Test");
4 j7 ~/ U3 C! ^+ ]CStringA str;2 f3 F3 f) Q) u; ?1 [& c: e7 S
str.Empty();0 o9 }7 X% l+ \! `/ y3 D0 F
str = bstrText;
& r/ O; x: ^' l5 h& }, Y2 F
( C3 m4 N0 |6 h8 @! P( ^$ I0 t 或
4 r+ }; `% z. v. L9 N+ H
4 k( E( F; l1 MCStringA str(bstrText);
& w( D/ b2 i) o4 G! [
8 O$ Z( Y& o/ R# N, N: E7 H( f (7) ANSI、Unicode和宽字符之间的转换
0 b- Z" K/ V3 {8 A+ \! O1 Q5 _. G; d& a% W5 p8 l6 U" O
方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
+ K0 R6 q! O- n1 _% P' ^8 ^7 x4 x& s% L, x6 ^9 F: F& B( p5 S; r
方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
% }( E* u2 J3 i, h7 G: q8 T. I* ]5 i3 o+ v* S
TCHAR tstr[] = _T("this is a test");) T1 p T m9 C/ J
wchar_t wszStr[] = L"This is a test";7 h1 K0 o# _) a( _. w+ ]1 Y# H
String* str = S”This is a test”;
+ P/ Z# X5 Q$ B9 x$ @. k
1 m+ v8 U+ F9 R 方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:7 J5 [1 X" F( q# p/ J
* Z$ _ Q" v/ [* d6 e1 _
其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:, H1 b: i2 q7 m% u: O4 N( b
" _% @7 O9 z: P% t4 v. O
LPTSTR tstr= CA2TEX<16>("this is a test");1 ^$ Y! Z1 G' Y+ a/ _4 I
LPCTSTR tcstr= CA2CT("this is a test");
% o2 i4 j' m, n: [. u6 |) Cwchar_t wszStr[] = L"This is a test";
! g3 f2 I/ d. d% Y Echar* chstr = CW2A(wszStr);
2 v9 W6 L; R1 ]2 E* Z1 E c
' }, \( ~: p9 C7 O. y0 Z 六、结语7 t* k* |0 b% {* `" j- ~2 n
) Z8 h0 b- F1 G2 t3 \3 H( j: f+ M
几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|