|
|
2002-12-05· ·丁有和··yesky
5 a; G+ B1 Q }7 [1 B+ C* J # d. c; y+ N5 | f
4 Q! ~$ c1 W6 W; c: b
Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
# w7 d/ q5 Y m% G0 A
3 w0 ^8 \! M6 r0 j# e+ ~; a# D 一、BSTR、LPSTR和LPWSTR
' r Z8 t+ Z7 z/ V. h
, Q) B7 O- U6 K8 X/ v 在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。: z+ o* d( Z- V' m) v
$ p, l& W& d/ |( `0 v. P 那么什么是BSTR、LPSTR以及LPWSTR呢?
; L4 S$ o% a5 p
! j4 r0 {& o. J# e BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
4 ?5 A: U/ @! d" N* x, t2 |2 v$ w3 |0 h# E) C* t
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。8 Q8 `- F @7 L0 p% D9 r7 ]. G" \( t/ @
* k1 f' ]. Y9 D$ {3 a
例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。2 K3 g5 |* L8 X" v9 o! k
$ {, H& E$ w* i- l
一般地,还有下列类型定义:
0 P$ `( @) z! ~# R/ T ~$ L0 W( b! E' n7 \( m
#ifdef UNICODE & A4 r5 f! r b6 u8 V; x1 H' E
typedef LPWSTR LPTSTR;
# \% [5 ?0 Y- S$ n$ ]2 a# ] typedef LPCWSTR LPCTSTR;
* D7 N: x' v8 E#else
7 f8 P- |% d: S) M typedef LPSTR LPTSTR; % o; Y5 s' D* L: C4 Z. c) {
typedef LPCSTR LPCTSTR;
: v. ?/ x, N# b/ R" x$ s#endif X0 w0 v. J+ E' y6 D) Z
% k8 ^; L' x- z5 r" }2 g! f
二、CString、CStringA 和 CStringW
! ]; [/ d' v t' t8 p0 H- I9 m+ z- \" v1 _* }/ {2 l2 P
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应用程序中经常用到,这里不再重复。
* G- k3 u5 s9 `, ~8 O6 L' b- T4 ]/ w" K X6 g+ }$ R
三、VARIANT、COleVariant 和_variant_t
& z1 _$ l2 h6 O
/ w7 \& I3 @# [6 P1 [7 r' G ^. U 在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:" E) f# v( N A# H
0 p) R f- F: i, n
struct tagVARIANT {
3 e8 Y. r- O& d9 [% m6 a VARTYPE vt;
, q1 i# R2 N( ~( o% u- |1 i8 x- K union {
9 z9 ]- f+ R) H short iVal; // VT_I2.+ Q! k1 s g- L2 X. `
long lVal; // VT_I4.. \6 z7 p. {7 d5 V) l
float fltVal; // VT_R4.
( K) a5 b8 S8 D, J( @9 \5 I double dblVal; // VT_R8.! h- ?" ~; U. S
DATE date; // VT_DATE.9 D( @/ y6 n3 @; Q6 B4 l. ]0 s
BSTR bstrVal; // VT_BSTR., N$ D& q5 V$ |% L' V
…2 J+ ?: _" F; V3 `( X9 d0 g
short * piVal; // VT_BYREF|VT_I2.
; d8 G$ \& f9 L) d/ G/ `4 ]4 ]! F$ C0 n long * plVal; // VT_BYREF|VT_I4.
! g0 a( l4 y2 O% t( a2 `. ~5 h float * pfltVal; // VT_BYREF|VT_R4.0 u! q* k; n0 r9 @& v" D7 ?
double * pdblVal; // VT_BYREF|VT_R8.
& ^5 k8 a, k4 M4 b. v# W DATE * pdate; // VT_BYREF|VT_DATE.
0 M$ \: n" v8 O& |4 |# z BSTR * pbstrVal; // VT_BYREF|VT_BSTR.$ i) m# a9 U/ S u; z! U O1 \
};
- i7 }. M8 ?7 ~- Q) H' `}; ! e% d8 N) Z+ Q% A
1 F$ e1 H5 \ f
显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:# i* n% a: a9 \4 d! j7 [) h
3 O( n" b8 R1 x' ^VARIANT va;. d" ^: M8 m, v! b
:: VariantInit(&va); // 初始化
* `/ B. W& w! q7 L3 ?1 mint a = 2002;* ?# L, Z" n* v
va.vt = VT_I4; // 指明long数据类型! l# ~2 L) E$ b) K% a
va.lVal = a; // 赋值 4 F5 u& s/ s$ G( P
; D7 C' |0 e8 e/ s! u4 m
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
/ R, m0 M0 B, z- n: T* a/ @, @; ~6 ?5 a) ~ h
VariantInit —— 将变量初始化为VT_EMPTY;
- }9 A4 ^5 A: a9 M4 K- u8 X3 {6 {+ B# C6 A8 t5 Y4 v
VariantClear —— 消除并初始化VARIANT;; ~3 R! l# ]' f& K
. R4 y9 P* {( K; H& N: O- h' ]/ R
VariantChangeType —— 改变VARIANT的类型;
* j' v/ ] s V6 M% ~7 `$ ], ]- T! ^% B5 [, V
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
+ a- ?$ f: A* x7 C( P! Y& y( ?" E% k5 b# O' U
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
9 _% _: k4 I2 v- f( w+ b9 W
7 t" s+ ?& m9 v0 u7 vCOleVariant v1("This is a test"); // 直接构造
9 X: u( A' s/ m# Y! }COleVariant v2 = "This is a test"; 7 j4 H" T5 p/ \& S3 }
// 结果是VT_BSTR类型,值为"This is a test"
# P* w& L4 E- }% D# S1 uCOleVariant v3((long)2002);
, n; K& t$ w3 K) FCOleVariant v4 = (long)2002;
- a* O' O* O4 k9 m// 结果是VT_I4类型,值为2002
& h+ s9 R' o$ v: ^
% y5 [8 P0 P% G2 N: Y _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:
# p/ B* y! f/ d2 d# H4 H% N; K" \2 H$ Z& O# b0 |( m3 N
#include "comutil.h". u3 y5 o$ N/ z9 s1 L. ^% |3 X0 `
! L4 c. u$ g' _6 B
#pragma comment( lib, "comsupp.lib" )
' d: Z5 M# u: ?/ t
0 y8 A8 G$ o" a s1 \ 四、CComBSTR和_bstr_t3 Z( r2 }+ k# c+ y
6 Q( C! w3 k8 ?
CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:% G# d3 `2 T/ ~& c5 t4 \
+ k( q7 a5 o$ ICComBSTR bstr1;
, s T6 V( `/ Y+ ]) Z* X1 y2 l& obstr1 = "Bye"; // 直接赋值
6 a/ ^! A/ s9 i4 D5 h3 E( xOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符
2 L( L. z5 h. r7 H. NCComBSTR bstr2(wcslen(str)); // 定义长度为5
( k7 q! p+ _* kwcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中+ ]+ o, t: ], B
CComBSTR bstr3(5, OLESTR("Hello World"));
0 `4 X- Z7 S8 ^) s4 O$ N) ]CComBSTR bstr4(5, "Hello World"); : |& H$ d, \8 w$ P
CComBSTR bstr5(OLESTR("Hey there"));
. q- B% k/ S: bCComBSTR bstr6("Hey there");
$ ?+ D( b, Q, s& e+ NCComBSTR bstr7(bstr6);
! `# k4 Q8 @) l3 b# `' ?2 {2 R// 构造时复制,内容为"Hey there"
, l f# @" G5 d, F( ?7 Z! r2 q1 m9 _1 G
_bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。- \) D% j0 L/ X1 d+ U
, u& x! D. Y1 B( Q. y
五、BSTR、char*和CString转换5 |2 y8 E: L1 d
& j3 x' h) H" A' d$ o" F# f (1) char*转换成CString
2 W V0 b/ D9 I0 A( L* B' K( t& e
' E! m) R7 i2 q. W2 M; r1 V0 w 若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:5 r6 K. y8 w% p4 c+ w( H3 h
9 G4 X7 R# _& q5 h% ]% [; f, Y; Y
char chArray[] = "This is a test";
) O" Z; O; Y( `3 P J v) |char * p = "This is a test"; & N% h- @4 K6 s0 L
* Z' n/ u5 O* S( D0 a8 s
或
, e: Z1 p/ r& n4 U6 t1 e- j1 A4 A, y) _6 S# V
LPSTR p = "This is a test";
( P- z5 X% [2 m; _
+ {- ^! g0 |1 o7 r7 U% L 或在已定义Unicode应的用程序中# W" v! R/ r8 }; i- r
8 [1 I" f ]8 @/ D8 \4 `# h. H, L
TCHAR * p = _T("This is a test"); , V: F m0 y, {) N/ W
, D; c) ^1 z; W4 M; k& F- F
或) I# e7 u+ \/ I
- }, j% x2 ~' Q9 t
LPTSTR p = _T("This is a test");/ n7 w3 i& `# C
CString theString = chArray;
' T, \3 ^+ Z7 R. k ~+ l; w3 ^theString.Format(_T("%s"), chArray);# f: b6 `! d+ r! H7 s% O) G' b
theString = p; # w& P8 o0 |1 R% R/ B
& a+ \3 y% G9 F+ i; T0 o# N2 m (2) CString转换成char*
8 B/ P2 m2 @9 ?( Z o5 v" C- }
! H/ {; m P7 e: z. L8 y# P. m3 T 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
c( t: |& k N- r7 _: D8 O% h# s b2 c) Y% y" l! N! r; n
方法一,使用强制转换。例如:2 m* l8 J! N$ C4 M3 u0 b
& ^; r4 G' B1 b& L! b
CString theString( "This is a test" );
4 q: j: i4 J$ m5 Z1 hLPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
. o; w! n p" t7 F, k3 P5 P2 n
5 e1 I) t; q, @6 K 方法二,使用strcpy。例如:
3 t- h) I5 `+ q: z/ a0 I. G7 D e$ a0 v5 c& U5 D0 z
CString theString( "This is a test" );. k7 z; `' F4 x" B
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];) {4 n& y7 X7 L! G- X
_tcscpy(lpsz, theString); 3 w# [# A+ t" Q
9 Z! ~+ G* ]) D2 n 需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。6 u5 q0 w0 A* ^& q, L: m
; I5 x& {$ v. o. D3 @7 \ 方法三,使用CString::GetBuffer。例如:/ y' a f6 _0 O& Y, L# Q J! g9 O
1 m- ^ i; ^" X2 N# F6 x- UCString s(_T("This is a test "));
, l6 q9 p( b2 }$ I& ~LPTSTR p = s.GetBuffer();! v1 B0 T' d( |+ g
// 在这里添加使用p的代码, N8 V$ `5 S2 D) i" E
if(p != NULL) *p = _T('\0');* X& V9 C6 ]9 [
s.ReleaseBuffer(); 4 I% o: m, }; r4 Z
// 使用完后及时释放,以便能使用其它的CString成员函数 # [/ L( J* }5 s% f" K
1 z' [- _: K- v, j; k, T! Z (3) BSTR转换成char*
3 D/ w8 G8 S- B! ~
3 q( l( C/ |3 N \5 J, I! ? 方法一,使用ConvertBSTRToString。例如:
( ^* m2 \/ Z5 G; J+ I7 [+ x! r0 u; v5 B9 r
#include # K) t/ F( u; m- ~. X$ K% N
#pragma comment(lib, "comsupp.lib")" j6 |6 w' q$ d( r) `
int _tmain(int argc, _TCHAR* argv[]){
, h/ A- R" }: I, Z, tBSTR bstrText = ::SysAllocString(L"Test");- T5 l2 Y8 m: T, y9 F$ J: K
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
/ Y, x& O) F# N8 cSysFreeString(bstrText); // 用完释放9 k: ` P$ E+ O7 C/ s( n4 U
delete[] lpszText2;2 e: M4 s* h! n
return 0;0 c6 }: s8 a' y3 B
}
, F9 S% h5 ]) ?! s: @
) {/ N9 l/ V7 ` 方法二,使用_bstr_t的赋值运算符重载。例如:
* p4 N q" W5 M& C
; ~9 |! N, G+ t, Y# l! r. Y- a" W_bstr_t b = bstrText;7 g: }8 `% t# r3 V
char* lpszText2 = b;
|' @: L! C. g8 W- P2 @' k1 t, U9 W1 D( W
(4) char*转换成BSTR) Y- |8 q6 m3 T( l0 p8 R
: |+ i# o, }) v: { 方法一,使用SysAllocString等API函数。例如:3 @9 S' [: y3 S% U; c9 \# J
! e; x3 h% h! |- X+ G: V
BSTR bstrText = ::SysAllocString(L"Test");
$ O& @9 O' x* g5 h) v# U7 |$ C9 c" }/ r! GBSTR bstrText = ::SysAllocStringLen(L"Test",4);# K6 Z1 _) u+ z: R. f, a
BSTR bstrText = ::SysAllocStringByteLen("Test",4); ( S) R8 H. F' G* F
0 ]# H1 r, Y% n 方法二,使用COleVariant或_variant_t。例如:
8 P0 p" V' Q0 b
7 B7 H$ K! ~) z$ v* |7 y' d/ E//COleVariant strVar("This is a test");5 B! N7 j7 f2 n7 f [$ J& W# M
_variant_t strVar("This is a test");
9 y2 v* z1 u% v. X5 |( N: LBSTR bstrText = strVar.bstrVal;
4 e! u! B; v" r/ L2 {# Y+ Z$ i) v0 ?* ~/ R+ v% q3 J4 Q% U& _
方法三,使用_bstr_t,这是一种最简单的方法。例如:
( R4 n$ k6 l3 }0 Q, p- \( a0 [1 c# m1 k0 m' S* T# d
BSTR bstrText = _bstr_t("This is a test");
- I. {9 Q. D5 {0 c" O$ X. N% B
3 [% ?. F; {; a9 F 方法四,使用CComBSTR。例如:
) R' X2 w" P- n0 j- D" x+ \8 i: f3 f/ H2 g% J7 r9 G: _
BSTR bstrText = CComBSTR("This is a test");
! }7 p8 ~& Z5 z/ D# w0 ~2 {* D M
或
, ?$ k1 n! m( G) w9 _/ U Y
% m4 F4 U' g; n/ {6 wCComBSTR bstr("This is a test");, ~- U$ j$ I' |' N
BSTR bstrText = bstr.m_str;
" j% r N; h6 y: m k# ]0 C& |
$ `2 [2 B; B$ { 方法五,使用ConvertStringToBSTR。例如:
/ u8 f/ }! R7 N
0 ]6 y8 e+ W$ z0 [- o2 H- gchar* lpszText = "Test";6 N1 @& ~4 A8 R% n+ Q9 I
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
% T! Q- p' _; f. ?" u
- r9 U5 A! o1 \. R: H& b- T (5) CString转换成BSTR+ D4 X* J# B7 z" H) Q6 S3 O
6 Y/ f7 p- J8 |9 \; r
通常是通过使用CStringT::AllocSysString来实现。例如:* ?( y% Q, L- r4 I
! j, |* A, A% x7 E
CString str("This is a test");: {$ s' {8 L1 d; d4 B
BSTR bstrText = str.AllocSysString();
( x. C t5 E! k- h$ p# x…
5 x1 I0 J" k9 G$ L8 xSysFreeString(bstrText); // 用完释放 S0 C7 t8 C9 l4 Z) f9 A ~. ^
0 k0 S4 L5 l+ a% L" x; W- @" J (6) BSTR转换成CString- p' o# ?8 s( A) g8 Q
' \9 H l0 w( o! v$ e
一般可按下列方法进行:
2 p! S5 b0 M. ]6 }8 F$ f1 U- K6 [
BSTR bstrText = ::SysAllocString(L"Test");. }1 h3 _5 y3 O, R8 a/ [
CStringA str;, A! e9 i+ ~+ [. [" ?
str.Empty();
: G1 G' z7 ]% [; M6 Sstr = bstrText;
* D# r& ~+ G" P
$ l4 Y0 C: ~: x' T! H; z, U 或0 L) S6 e% i9 ^: V: [, c
7 r( r5 ]+ Q, @7 W& A
CStringA str(bstrText);
0 X9 v8 S2 U/ G$ H6 C5 c; O2 R/ j* i
(7) ANSI、Unicode和宽字符之间的转换
- I- g1 X6 v, q/ ^% r2 i5 W
+ V, Y2 m6 N, ]+ f 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
! {/ M Q/ d a7 p4 c2 ?$ l, [- }9 L- b- A
方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
1 V, J; D9 b( _/ h' f* q5 f
# J5 Y" u( ^( ]' I& I TTCHAR tstr[] = _T("this is a test");3 t2 j: f8 {! k8 M) r
wchar_t wszStr[] = L"This is a test";" V& n8 k8 V" b0 B
String* str = S”This is a test”; ( b, r: X5 k* D% C' ^! p/ U6 ^9 c1 V
2 A& _0 m3 W# `1 U- q. l8 [
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
4 R" c0 E6 l8 w7 D: \( V6 c1 R9 p0 S( B/ T; `* j
其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:2 B3 O, z8 g8 ^' R+ G0 u7 I
# V6 B, e2 M l9 ~
LPTSTR tstr= CA2TEX<16>("this is a test");" X' ]+ e, V" w2 E8 u; Q
LPCTSTR tcstr= CA2CT("this is a test");
6 M$ _2 N/ [" ]$ Z' j% }% zwchar_t wszStr[] = L"This is a test";
, \+ I; X" ], p/ y5 @$ Z" j$ Mchar* chstr = CW2A(wszStr);
6 \9 U: r! \1 ?8 p# `) J
; Y' [ Z0 U' l9 v: q, x W; ~ 六、结语
7 H0 V5 ?3 G5 Y2 [& L! n2 v2 Z6 O* B- {6 F. b- N
几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|