|
|
2002-12-05· ·丁有和··yesky
4 Z1 Z" C. V$ I; ?7 V
. G. T% l2 H8 ^: o! t5 m
8 S/ b6 l9 z6 @" G Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
9 T8 ]5 ~4 V) x+ v& ?) O+ r' ~
7 _ p7 D+ {& Y* E( O4 y( ]2 I 一、BSTR、LPSTR和LPWSTR( [! v" U+ K" O# O0 c; e
2 H4 q) t4 E, D" K' W( U' d% S 在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
5 [, W: w% W% S/ O7 D2 I: O; |
- }. P3 a! s, X% v 那么什么是BSTR、LPSTR以及LPWSTR呢? w2 R& }' X) r: l' A6 h! O4 T! v7 P' e
# a- M1 Q5 p$ s" S, `3 \ BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。' S) n0 Y) C& w2 m5 k3 v1 V
/ f0 A( F1 n7 {- t4 P! S LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
' C( C- c4 t4 U
; p* ]$ o8 K! A9 k! U# } 例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
& {) @1 T( Y+ i# I% p6 k; p1 W3 K0 p/ A3 D
一般地,还有下列类型定义:3 p2 g4 |2 I& T% e1 ]9 ~
S6 p0 L3 k6 ?
#ifdef UNICODE
% U, |( D5 t0 S4 y$ \. {4 J typedef LPWSTR LPTSTR;1 Z' F+ _, P& n+ n8 k
typedef LPCWSTR LPCTSTR;
- b. a! C( |& ^1 L2 R D#else 5 Q* W$ @$ x) |
typedef LPSTR LPTSTR;
1 ]4 @9 b; `; ^ typedef LPCSTR LPCTSTR;
+ w7 W4 [/ f% z3 D2 K0 i5 B3 Q$ o#endif
$ \$ R( o7 Q3 h$ A0 Q! [- C% o! _. ?/ t* Q3 O6 z/ _
二、CString、CStringA 和 CStringW% z# Y% n" |( d
# c& `, S" c8 m& o9 f 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* p' x3 K7 n8 K
3 j( N3 W) w k+ d 三、VARIANT、COleVariant 和_variant_t
3 F3 J( t5 s; }! s6 H$ \/ c- s0 w0 e- @" h1 d7 k! J j
在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:' U& s& I0 r3 L: `9 ~
1 I, o+ m4 p2 v! B
struct tagVARIANT {8 ?$ C/ T8 x; t
VARTYPE vt;1 h7 w: e* V0 O( {2 Z5 l& W
union {% f' v* Y$ C9 q4 ]4 s8 X
short iVal; // VT_I2.0 e9 \6 G& n! @, C" ~
long lVal; // VT_I4.' W( P% D3 |! x1 ~2 i; w
float fltVal; // VT_R4.
( @3 W' O* Y5 z% A" b; X double dblVal; // VT_R8.
( I; c5 S) ~0 u5 R$ [& X DATE date; // VT_DATE.. b& u6 e6 Q# U' N+ X
BSTR bstrVal; // VT_BSTR.' a* X% s# i2 u
…
z) ~+ m8 E5 t* D [( _9 k. | short * piVal; // VT_BYREF|VT_I2.5 F7 S5 ]/ b2 F, P8 |
long * plVal; // VT_BYREF|VT_I4.
2 ]. T- o2 r# _( N' D* T9 o5 R float * pfltVal; // VT_BYREF|VT_R4.
2 w# L. P1 }+ t( `( e double * pdblVal; // VT_BYREF|VT_R8.6 C0 v0 G t! W, I& q
DATE * pdate; // VT_BYREF|VT_DATE.
5 @$ ^3 \1 M0 V. Q# B BSTR * pbstrVal; // VT_BYREF|VT_BSTR.% e- A# B' `8 I% A, w$ S
};+ A2 G, s' d) {+ F' `- `
};
' `6 }/ ~9 W% R& [0 H3 b. Y3 x- ]
; c# f2 a) X9 G7 b! H0 f 显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:: N& r* S! [% O9 L. U
3 @) O. x# S8 [$ FVARIANT va;
8 ]" p( F1 @0 {$ M:: VariantInit(&va); // 初始化
- R( ?/ N2 g! kint a = 2002;8 [& \, _% t- L6 Q
va.vt = VT_I4; // 指明long数据类型8 P2 ]" }) Q4 ^4 o3 w" q
va.lVal = a; // 赋值 . s8 R5 N, n i, M
- O0 D6 N$ O w0 j4 j1 u
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:6 T0 U& Q) h$ J. K8 i$ o
5 z; x( @6 j1 n
VariantInit —— 将变量初始化为VT_EMPTY;
# z8 W2 i: r. l4 y- Z/ {* g, E+ w. `% v% |
VariantClear —— 消除并初始化VARIANT;& n. a/ A! V0 c s/ X* d
+ y! N. |& y! Q! v VariantChangeType —— 改变VARIANT的类型;$ Q, f' r: f# a( M8 u
+ i% S, C+ g; b6 ? ^1 ] VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。! C1 m# O' l. q. e
$ d" s$ @+ Z7 M4 d" u' y
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
- h( Q* n0 Q- d$ s+ A* v) ]! Z) f( a) h* O" x; m0 ~/ x
COleVariant v1("This is a test"); // 直接构造
7 ]' F" t E" o8 xCOleVariant v2 = "This is a test"; ; Y# F2 ~# T+ Y
// 结果是VT_BSTR类型,值为"This is a test"
- m0 O; C s* s+ |7 D; hCOleVariant v3((long)2002);5 {+ Z2 D$ Z4 i% A( `: \/ O
COleVariant v4 = (long)2002;: F% Y ]+ {# B- ^# M* g
// 结果是VT_I4类型,值为2002
1 V1 U) O1 b" \6 N# {
' q8 J0 u+ M# f0 e& p& K @* _& C _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:
' E) {8 O" e. F- O/ J5 A8 l0 w( d# s
#include "comutil.h"/ T R$ e% q- x9 i8 N
: U, h9 W- ~7 n) K #pragma comment( lib, "comsupp.lib" )1 ?, j7 w' p5 G) K. c+ K: r, m( S3 e
: W! g; S' T+ ^9 q# d' y4 U 四、CComBSTR和_bstr_t
; ?- k6 q4 ]" D/ K0 t* x6 t# `# J8 Y) t% j1 O3 @5 U1 r
CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:1 O [1 [/ ?7 O
3 F, O( l( M5 V& A5 o5 M- h$ D1 oCComBSTR bstr1;
9 m5 F6 s) ~2 m4 d! y2 \5 qbstr1 = "Bye"; // 直接赋值
* a& j+ X6 B6 L- h: p5 E" N6 bOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符2 t4 [# k5 X* p& R9 _* ^6 `" Y
CComBSTR bstr2(wcslen(str)); // 定义长度为5
! _% Q5 N" Y( M) t; }wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
0 }7 I& w1 e/ E8 A! d2 M# UCComBSTR bstr3(5, OLESTR("Hello World"));
; w* w; H5 I+ v5 B. f$ qCComBSTR bstr4(5, "Hello World");
2 U! M% X; W( I$ \0 G1 lCComBSTR bstr5(OLESTR("Hey there"));
5 ]) X+ A C* [9 R3 d# q. p9 HCComBSTR bstr6("Hey there"); * W" f1 h$ n: | E- K7 g
CComBSTR bstr7(bstr6); " L3 `6 A+ h% a3 u0 r% {/ @
// 构造时复制,内容为"Hey there" 0 m1 J' D, g$ {8 i7 |* D
" B# S+ ]2 H2 C; z9 h, ?
_bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。3 P; ^* \7 N) u
2 t# a# P9 e/ A6 H6 ]0 W( j5 c1 Y 五、BSTR、char*和CString转换$ s# J$ B6 Y: K! I- c6 u( B8 z
% |' y3 ~, F8 C" p+ B. L (1) char*转换成CString
/ a/ x( L% y( O4 [) H; J) Y0 B! w5 g$ C* n6 h6 R3 b& ]$ h
若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
1 h8 Q5 r- N- n7 F- S! e- D& l s/ y1 o/ R# Y- E4 C
char chArray[] = "This is a test";. ^: z$ M5 m) D! v! [8 W! r
char * p = "This is a test"; ! I. ?$ O/ g: K+ G; i; d
8 n: x& T% q& n( n
或
9 P$ B6 [8 V3 Q3 [' w" i a* |# }5 f' @
LPSTR p = "This is a test";
9 d( P! { t- y( z: _( i
# U/ W5 U, b( ~6 V" `" p1 {- M 或在已定义Unicode应的用程序中1 ^, T( }" X7 v6 j* [
2 W6 S4 D$ R/ I; M' Q# h+ oTCHAR * p = _T("This is a test"); ; \) r7 E3 S0 o- \) d( Q: L/ Q: |
3 }: a! p, _$ z" Q 或4 O, P9 |' q# z# u2 i) N8 J
: D' n3 ~3 c& T2 G% c8 _" f" f8 }LPTSTR p = _T("This is a test");
+ A/ ~- N5 u: a2 m; {CString theString = chArray;6 @; Y3 _9 L) L
theString.Format(_T("%s"), chArray);
% j* r+ F* A( a8 n0 K+ ctheString = p;
% @7 {, ]) T9 l/ [' J }- Q3 i1 n8 n
(2) CString转换成char*
0 @ U$ j. c4 j8 v L$ h4 _# ^& `
# \' B6 N- i) ^% [ 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
! M% i; P1 V" `- @3 z3 W/ b7 i& B" Q, @0 q1 Q
方法一,使用强制转换。例如:/ N, A# O$ _3 a# a" O1 Q8 ^6 h4 i
: C; Z+ G8 F) t2 @1 T% t
CString theString( "This is a test" );
q; F N6 Y; f( [7 O- W4 i' zLPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
. V* ~/ E) k' W- _
* F O4 c0 {: r( y9 ?/ H 方法二,使用strcpy。例如:
! a( a+ k: t% E9 }6 p0 Y0 k
) N0 {2 R& |/ E; WCString theString( "This is a test" );
1 d+ x+ K# [* O' m: R: qLPTSTR lpsz = new TCHAR[theString.GetLength()+1];+ M9 Y' S* z- j' `
_tcscpy(lpsz, theString); 7 z$ `1 |: \0 |* B
% v% \9 R o& t) B3 b
需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。 j& r: `+ Q0 i- O @/ s
7 s& S2 p. o* a4 N. d. P
方法三,使用CString::GetBuffer。例如:
: m/ K( m$ C( g( ?7 I! X8 y/ o W$ H9 C; e/ @8 a
CString s(_T("This is a test "));+ ?8 M/ L' Q3 E, Y3 j
LPTSTR p = s.GetBuffer();" M2 c. j( G; M1 N
// 在这里添加使用p的代码6 R# d, j ^' s
if(p != NULL) *p = _T('\0');
7 J' Q g6 n1 _7 ^. ~3 y" B+ @s.ReleaseBuffer();
7 K7 @/ v% P5 y$ _$ |// 使用完后及时释放,以便能使用其它的CString成员函数
, X6 y I, p. g' l& Y, X0 C+ C; P$ s) n2 i! S3 ?
(3) BSTR转换成char*
& p- F( I$ C7 a$ E" P
7 _9 p$ B, h5 a+ w5 q 方法一,使用ConvertBSTRToString。例如:& Z6 M4 p* R" T
7 x8 Q* Q& M* r" A w
#include
9 p) }+ f- b- j#pragma comment(lib, "comsupp.lib")
* P1 u% ^ m3 P) pint _tmain(int argc, _TCHAR* argv[]){
- |' S( ?8 [3 H' J2 l* }4 iBSTR bstrText = ::SysAllocString(L"Test");1 W/ u( n1 v1 }
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
& I# Y' t+ G! m- P: B* q# s7 @SysFreeString(bstrText); // 用完释放2 j8 p) }* p' a
delete[] lpszText2;& i9 | c( n8 c7 b
return 0;
% i7 W" w2 c, L+ N, T} + V1 v' o9 t, ]: r
* W9 n( B' \, G: C 方法二,使用_bstr_t的赋值运算符重载。例如:$ ^% ]2 r, o- g6 h1 Y; g+ H
) m" {( t' Z# S9 N. ~
_bstr_t b = bstrText;8 c( l1 Q2 W% ?% R" z( F
char* lpszText2 = b; 5 t$ G6 \) S% T/ Z8 c
7 ^8 B/ q. c+ A0 |( Y+ ]* \5 Y (4) char*转换成BSTR7 n {- m/ V6 _( [! o: e- ?
/ f4 g- L% m( A$ ]7 ?( i+ n% S( T6 v# Q. k
方法一,使用SysAllocString等API函数。例如:+ e, g2 D; e W" i6 d
1 Q, T7 n( B; U% _( dBSTR bstrText = ::SysAllocString(L"Test");
2 y4 z+ O9 B: d; `9 EBSTR bstrText = ::SysAllocStringLen(L"Test",4);/ y; T" D* a3 A( K2 [' ?
BSTR bstrText = ::SysAllocStringByteLen("Test",4); + R. \% v! A( c9 }
6 A, T# D5 Q& ?0 v- e( [
方法二,使用COleVariant或_variant_t。例如:
! f8 Z! m7 \) t/ Z! D6 T$ E0 c+ O3 m0 E g9 x7 z
//COleVariant strVar("This is a test");7 j" x8 t4 y' G3 }6 K: ]2 E) S
_variant_t strVar("This is a test");
0 w1 W0 N% P( G+ y7 Y- ]BSTR bstrText = strVar.bstrVal;
& H$ z- m/ C( ~- }; J
4 A% _( w/ C" j 方法三,使用_bstr_t,这是一种最简单的方法。例如: U+ ?. |: Z, \$ R( ^/ I
2 N: M0 ?: P! C' r& w0 r r7 w, QBSTR bstrText = _bstr_t("This is a test");
$ W" _) g' R4 c' {0 u0 H" c7 q3 e: M1 |6 E: y' \4 G# U
方法四,使用CComBSTR。例如:- C, r% ]2 u: u3 V# K0 g
! K2 D3 L( v: o' ^3 j$ Z; [BSTR bstrText = CComBSTR("This is a test");
+ j( q( X! k9 Z
* |( G$ X4 o3 t" R' h/ v1 c0 E 或2 M5 r) K0 t; d1 O. X2 q2 X
8 i% ]7 F/ Q# D( x
CComBSTR bstr("This is a test");4 I9 |8 J! ~ A' O$ ~. S
BSTR bstrText = bstr.m_str;
8 P% P8 T5 @# s, J/ d
0 V R, G( E8 o# ? J, G 方法五,使用ConvertStringToBSTR。例如:
8 Y# `, O- w u i& u) k4 _! S' H$ E& o% m2 G' I# H. q+ B0 a; Q6 c
char* lpszText = "Test";
) J) ]9 C' o" e" d# _4 n; KBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
, C) D0 ]1 D- f8 C0 K. s
/ L& C/ r! B) v6 i4 x/ T (5) CString转换成BSTR+ N9 A# J0 ~; q6 n2 ~8 H9 Y, ]
, j: v, b- i. U5 N' N2 L
通常是通过使用CStringT::AllocSysString来实现。例如:
1 H1 J7 I H6 m( c3 a0 D2 @2 K5 D6 _5 g9 h
CString str("This is a test");
) H+ x/ A6 R2 jBSTR bstrText = str.AllocSysString();5 r) z t }1 m
…7 h) Z2 Y1 f% A- r2 ]8 l4 [
SysFreeString(bstrText); // 用完释放
! Q0 ~$ y3 Q3 r4 m4 c
: |9 `( J2 n7 v- { (6) BSTR转换成CString, o9 q8 ]4 X- m- ~
0 t. t$ O- C5 n, N: V/ x) P, V
一般可按下列方法进行:& O1 U) z8 m1 `. V4 U5 l' u7 ~4 j5 j+ \
' n) \* {, t1 U- w; E9 c7 hBSTR bstrText = ::SysAllocString(L"Test");- t. q8 u$ m' v% @: W& G9 Y
CStringA str;
2 [. f, P1 V/ a' cstr.Empty();
' S5 s: B! P/ E5 F8 D- H5 ~4 V0 Gstr = bstrText; 7 }' n O' o: I# h8 E
1 I# Z, D" C. x( a3 U9 b
或) j' N1 C6 Y/ w, m$ ~
$ _- I/ P1 r2 k% c+ q# dCStringA str(bstrText); 8 b' p {7 c6 ^: S l7 k" n& @# E: M
5 H2 k7 D' e u! v t% r. w
(7) ANSI、Unicode和宽字符之间的转换* a: y( v/ I" l- D
g2 s! _) f) d5 j 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
( x- A5 ^0 B- B* {' K0 Q2 b3 p: |
方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
0 H1 F7 b ?+ p. D8 s$ F
: O. K: Y( t ZTCHAR tstr[] = _T("this is a test");. {5 E- l% o' x
wchar_t wszStr[] = L"This is a test";
4 L* v, @, b9 p% K+ |String* str = S”This is a test”;
" ~+ ~, M" o6 ?- Q3 E' c0 H+ B* g3 b6 b# H% G8 S
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:& p/ s) ~& p4 P9 F& t
+ ~# x. Z( W9 x2 [: P! C& ~ 其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
; k2 p0 i3 a& l
' M6 E% e# W# [7 g! CLPTSTR tstr= CA2TEX<16>("this is a test");+ |4 v9 H# j* G! i$ U! u/ \4 w
LPCTSTR tcstr= CA2CT("this is a test");* T7 x6 v$ ]$ W2 v; T# M
wchar_t wszStr[] = L"This is a test";
* n% B; V# v0 w: schar* chstr = CW2A(wszStr); 3 s# W/ d& M* Y
) Y* c" @- D9 i2 H 六、结语
( V) H5 q7 M, w0 {8 ^, k! ~0 v0 L( {/ ?6 a% C8 X, y/ V& Q; a
几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|