|
2002-12-05· ·丁有和··yesky
; L) D7 M, {, ]4 j! e
9 s8 S3 ]9 g/ S5 ]" h1 A% \$ x2 m0 k2 j9 O- s8 X4 T
Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。6 j+ ~3 I# W c: ~8 }! Q
9 C' ~ g8 ~8 C O 一、BSTR、LPSTR和LPWSTR# H: B/ ^& \0 y' U" g/ u
2 B8 G. I5 _8 z9 t
在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。3 a6 V" |! z" o$ g8 d4 C- Y
: I+ W y! ]" m' ?; M8 k) s* m 那么什么是BSTR、LPSTR以及LPWSTR呢?6 I. ^9 U& m' o+ m
0 G( N. ? i3 \! c$ q BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
/ s# ]& S$ e7 p8 w& k0 w
8 T1 m/ g+ z+ }/ ]% W9 U; B LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
# m8 ~* b4 s+ `. Y1 _. x g; Q
% U5 y: r/ p9 W# ? 例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
/ H w: ^, ^" V3 b7 ^, z( @3 Q# B, U. y r' I- t* E8 ^1 ?1 x
一般地,还有下列类型定义:
$ u0 `: g; N/ {6 x8 K. |
3 x8 S0 z! Q/ M$ q1 K% L#ifdef UNICODE * p/ r" L. k/ g* `" u) Z# I
typedef LPWSTR LPTSTR;
* G5 @4 @6 F4 w! a0 h) e typedef LPCWSTR LPCTSTR;
, v( _. x5 w+ u/ _5 c5 j6 o8 t#else
/ O1 R) d& Z5 w typedef LPSTR LPTSTR;
. C, r. Z# q- T1 K' y# `/ m9 h0 F typedef LPCSTR LPCTSTR; - j' l% W3 ]2 g( b$ [% ~
#endif
4 w9 v3 f1 G$ {7 ~: x9 ?8 i# e9 C/ d7 m( G
二、CString、CStringA 和 CStringW% _8 r0 Y1 N( E& v) }& i
, P! C$ V; s+ L5 r. V& m5 ?
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应用程序中经常用到,这里不再重复。0 [, y2 o6 K* P: a1 ~4 l
6 K _% q$ d2 X2 O/ O+ A- g; L: D
三、VARIANT、COleVariant 和_variant_t9 X% m2 C- g2 e
% [' K7 C" R: s! W& ~4 B% W j0 @ 在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
_. k; r: x( ]8 n7 O! c/ W) J' C2 r h$ s. j5 `1 v; n) a3 ?% Z; E
struct tagVARIANT {
2 J8 o7 [7 ?. Z VARTYPE vt;
, }- H2 r0 U8 Z/ J# D3 B/ l union {8 i3 k$ m# ~: v2 D5 ^% k
short iVal; // VT_I2.% ^* d2 `8 I& |* ~. Y2 \
long lVal; // VT_I4.. p y" H7 c! _* x
float fltVal; // VT_R4.
, ~, P k1 n- |7 B4 K8 u double dblVal; // VT_R8.8 A( k$ _0 @1 j( ] W; ]
DATE date; // VT_DATE.
$ ~/ o% ?+ ^* N BSTR bstrVal; // VT_BSTR.
, k) R# ~: P/ Y* q: X …) [2 _; K, Z2 t$ b, P/ y$ Y; U
short * piVal; // VT_BYREF|VT_I2.
7 o( {) T# X5 z% _: z/ y long * plVal; // VT_BYREF|VT_I4.
: U: m- c/ U& j' G float * pfltVal; // VT_BYREF|VT_R4.
+ j* l b6 @( d- H& `" ^ double * pdblVal; // VT_BYREF|VT_R8./ O. Z, C, E' }* Q& o& P4 R0 I2 @
DATE * pdate; // VT_BYREF|VT_DATE.5 f( A4 [ C2 r. Z! X
BSTR * pbstrVal; // VT_BYREF|VT_BSTR.( _* V! }4 d. y* l& V
};6 R3 E8 K1 A% |& Z! K6 q* h1 w
}; + p) U- n/ B; ? s7 t
2 Q. J" [$ m, r n& ~
显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
2 k S6 p, X% j
& L, d* Q8 |" F0 bVARIANT va;9 p. h8 e1 J6 b7 s* u5 J l7 U$ z
:: VariantInit(&va); // 初始化$ d( Q" L, u6 ~6 j- W
int a = 2002;
- E: p4 T" V7 d( Iva.vt = VT_I4; // 指明long数据类型
& s+ a S1 ~4 Y' z- C5 }* S& C: cva.lVal = a; // 赋值 0 n1 l B: Y d
1 w1 _6 o2 ], e' T2 G
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:1 r% Z* g7 y1 v
+ [9 F+ H) j `$ V0 b/ V: q* A VariantInit —— 将变量初始化为VT_EMPTY;
* S. i- R9 {% y1 j- c' [0 I$ ], \5 f% g0 J
VariantClear —— 消除并初始化VARIANT;
3 w6 k" ?$ M9 }# \$ [' l: m) ^7 q& z" G% @ _4 L1 O
VariantChangeType —— 改变VARIANT的类型;4 F7 Y' l! j' v
5 n( z* U. y- u$ w
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。/ ?" z D8 ^' p# J# b( b$ D
6 M0 b* m! p9 K- L& }1 N; r# E
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:# @$ `0 U! \, {- s: @- ~& y
J' j/ O; I' pCOleVariant v1("This is a test"); // 直接构造
1 B- S$ @3 P' _ BCOleVariant v2 = "This is a test"; ) I* l* U( k8 [+ v. S$ @
// 结果是VT_BSTR类型,值为"This is a test"# h7 I6 @/ t' Z
COleVariant v3((long)2002);
# s4 c( d6 V" I6 g9 d9 ^5 N: hCOleVariant v4 = (long)2002;: n1 v2 Q' Z0 [" L
// 结果是VT_I4类型,值为2002
( y1 d: f) q9 t+ g. z
0 ]( N9 c0 l) c- c5 |1 u/ ~ _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:. b' v' I& i+ c% g4 s
! [) M* g f: h1 ?$ J #include "comutil.h"7 H1 s- P5 k( A5 i) _- m. _) B
+ G% O$ R8 d+ M; U$ s8 ^6 _# U
#pragma comment( lib, "comsupp.lib" )
4 k9 }% |- i0 w% \, y$ L7 M3 H2 z. L9 V6 s; H
四、CComBSTR和_bstr_t
/ X3 Z# z: |) S: S- l+ h$ h" F3 m: r( ^$ P# `
CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:# K+ U' g: W/ d3 D& _- O
" ]) ~$ D- h/ \# |. c: e( YCComBSTR bstr1; ; \! i4 L& _! U& s7 Z
bstr1 = "Bye"; // 直接赋值
# d/ c1 H1 s. N3 r. ^OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符; \! d7 S/ ^7 ]* L% v6 n" J6 W: B
CComBSTR bstr2(wcslen(str)); // 定义长度为5
' l- _9 L' A2 ?8 Swcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
* g' i8 e5 S# K* n; q) B! ACComBSTR bstr3(5, OLESTR("Hello World"));
% ]1 M) B% m( q8 Q' LCComBSTR bstr4(5, "Hello World"); 4 l( P- ~/ z+ M' U$ {1 m
CComBSTR bstr5(OLESTR("Hey there"));
1 H, g$ ~0 a& Q Z" I' ZCComBSTR bstr6("Hey there");
4 H% a1 P3 y% eCComBSTR bstr7(bstr6); . a& l. x0 x" v1 W& K
// 构造时复制,内容为"Hey there"
* O/ D. Z! Q" } X2 e& s; s4 _* ^2 |" K/ S( C( c
_bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。) f/ K, J& \+ F7 D
* |# t6 J$ k2 i7 p4 G. w& p" L 五、BSTR、char*和CString转换7 Y1 F2 J7 ^) T# ]
' g9 y9 u; |0 T (1) char*转换成CString
% S6 v; ]% s: h8 i5 |
5 Q4 a& @% ~8 f$ v1 W 若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
* W# A1 x# q# g3 W; `1 \7 Q! G/ d. X( I
char chArray[] = "This is a test";
9 B8 ^9 l+ h3 ?$ N: Wchar * p = "This is a test";
; x" J$ }% d" q% K0 Y6 B2 s& }: B5 z4 R" M: I3 K
或0 J+ D" F1 S f4 ~8 b4 c
M1 D% l3 R$ y# C6 u7 R9 F" }
LPSTR p = "This is a test"; 4 V! {; n' _8 @ n G- h9 A$ _
# j7 y; P2 e3 \2 T5 i6 h8 k' F 或在已定义Unicode应的用程序中
0 K8 V1 M4 {1 J$ N' v8 i
& `" f& X4 z! o3 _0 Q- K5 ITCHAR * p = _T("This is a test"); 6 Q3 _3 T( P% ~. O$ p3 F0 u
. V v: }/ A: H5 w8 |% e
或* r- `$ j! X/ y5 J4 I
: T% N8 o" q" R7 A1 u
LPTSTR p = _T("This is a test");
$ }/ Y3 g8 m4 [CString theString = chArray;
1 I" _* q% z+ N* OtheString.Format(_T("%s"), chArray);9 k/ ]9 _5 j1 G* ^+ ?
theString = p;
" V1 N% B) `! @2 l
+ C, v/ X y) T! Y7 N$ ~) b (2) CString转换成char*, L& S9 G/ ^# D+ w2 O7 U/ Y
# B/ L( f, j, e0 t
若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:1 ^7 t/ r) u/ L7 Y
# u, g. ~3 Z+ F
方法一,使用强制转换。例如:% ]5 A+ g. |. O t! @
, f- ?) E+ F/ `* d- q
CString theString( "This is a test" );1 [! G. t4 @& w) J' @' V
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString; * K3 _! C3 Z5 \+ D0 R+ V
: s7 W% I( I* f; ~* L4 p 方法二,使用strcpy。例如:
2 z$ W1 q, o: h6 E% L1 D' q9 B
CString theString( "This is a test" );
. }0 t7 s. Y: c. ^# g6 dLPTSTR lpsz = new TCHAR[theString.GetLength()+1];2 E( x3 f2 `9 \3 ]( X8 _
_tcscpy(lpsz, theString);
: ]( R+ B0 i' Z" g& N, l( L, ~- U/ i+ Q
需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。8 M0 v5 A: U1 M! ]
6 G& E& _: }' V; A5 [6 q2 }5 D8 f
方法三,使用CString::GetBuffer。例如:+ v( L& p! W8 J7 S1 b2 f8 Y
$ V$ l( B C. e; k1 b4 \- K
CString s(_T("This is a test "));4 y7 p& s! V3 C' `5 B- ]4 F. e' E
LPTSTR p = s.GetBuffer();) H+ O) x& b. e4 l9 E( F9 H
// 在这里添加使用p的代码
* k/ k) i( c. C* O* B6 ?3 y/ `/ xif(p != NULL) *p = _T('\0'); Y& q; c6 Q4 R) ]" ^- o, ~
s.ReleaseBuffer(); + D: F( [/ L2 |9 @& h
// 使用完后及时释放,以便能使用其它的CString成员函数
& f k5 ^7 s3 K! }" r" i' l
8 ?5 y7 k9 A* f) U3 z1 w* { (3) BSTR转换成char*
( Q" \) ]: G5 x7 H
+ K4 |6 _+ y$ L4 k& p. @ 方法一,使用ConvertBSTRToString。例如:. u- t/ K" O' u+ d' q$ q
% V( `, D2 c+ u5 r
#include
2 ^. v1 i5 N8 C#pragma comment(lib, "comsupp.lib"), _7 M5 L- [4 Q: Q5 Q
int _tmain(int argc, _TCHAR* argv[]){
, K* ?& h' `3 ^5 k$ Z: EBSTR bstrText = ::SysAllocString(L"Test");3 x* ~" @0 {: [# K$ k# v9 `+ h
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
2 R- m0 S: H2 _% s3 ? T" gSysFreeString(bstrText); // 用完释放
' |9 [0 @. a* W; e/ q9 M, P: T) l, ]delete[] lpszText2;! f/ m+ ? |9 J+ f) b& g
return 0;! {8 y) y3 H1 s, }: r0 V+ ]/ t
}
9 g) `) C, g6 W" W7 h' k
$ A- s4 y% m4 _: y5 s2 `9 D 方法二,使用_bstr_t的赋值运算符重载。例如:
+ V+ T. w- q8 H. x. M1 H0 v+ s" {- g; y5 \; w/ ~
_bstr_t b = bstrText;
0 w+ q9 n1 U; L5 w$ P4 C7 Tchar* lpszText2 = b; 2 \& t, a3 E) F/ u- y; }% f5 a
0 P# l% P4 N U' e6 @0 X7 Q
(4) char*转换成BSTR
% W: I# L2 Y$ l8 d) o! j0 h, h; @' S5 h6 d: W% g
方法一,使用SysAllocString等API函数。例如:' t5 E( t `1 S1 ?! m
5 ^- v1 d6 @) [' w. O& v
BSTR bstrText = ::SysAllocString(L"Test");
4 i7 h* Z: I" p8 ~BSTR bstrText = ::SysAllocStringLen(L"Test",4);
; j7 j8 `) c7 s# v2 _$ _# mBSTR bstrText = ::SysAllocStringByteLen("Test",4); ; D% Q: U8 g8 [+ Z- N1 Q* F
. O% g3 ]( ]' V& g8 _5 V) W3 p 方法二,使用COleVariant或_variant_t。例如:' ^5 {2 F* e. i3 e
- j9 j7 b- {6 b2 i V* k
//COleVariant strVar("This is a test");
* u2 Y+ A. i& a' [_variant_t strVar("This is a test");
" b8 w+ I5 B' vBSTR bstrText = strVar.bstrVal; # p( ]+ B9 S' {2 i. n, g) s
, s6 D! W y. F$ u' d: a$ `
方法三,使用_bstr_t,这是一种最简单的方法。例如:2 E2 F: h c" P7 S- a% E
) R: t1 _5 d4 d8 s) k
BSTR bstrText = _bstr_t("This is a test"); " m+ d+ b0 W: [
5 e, G% c o4 d. K& {
方法四,使用CComBSTR。例如:
$ Z/ p- ^" k* v+ m5 g& V$ P, Q
4 a) k4 i( l. W" I, v1 u5 G/ mBSTR bstrText = CComBSTR("This is a test");
8 @5 m5 e" P) ^0 L" w* ~" v/ N
或' ~2 Z. W4 I9 R6 Q8 m5 A& s$ Q$ _
8 L# }/ w# ]. ?/ T5 H1 r
CComBSTR bstr("This is a test");7 ~: Z! E' L7 Q, T: {
BSTR bstrText = bstr.m_str; 7 B. j( p. E2 v4 z z& K6 v1 i
" ~$ o+ a- C4 |2 h6 Y; r; ^ 方法五,使用ConvertStringToBSTR。例如:. a0 m) o& N( `0 F% ^4 L* q* a
0 `4 a7 K, S" Y5 m% Wchar* lpszText = "Test";7 `2 N. y7 E& s* _+ d+ @
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); + E+ l' _3 G" P" F+ V0 L
# C! q8 ?1 Z' r' T. A' `0 u (5) CString转换成BSTR' A% I7 A; j& u" |; q5 P; A5 x0 P
( S( T. ]+ ]0 R/ k" M
通常是通过使用CStringT::AllocSysString来实现。例如:0 p8 M; R+ E, j+ i, L5 F, M( N
& `0 ~ ~: p* e$ M, [CString str("This is a test");
8 U- _% F: n) P- UBSTR bstrText = str.AllocSysString();4 A1 I0 _& @7 B# z# `
…" @+ Q! p) R3 d2 }5 x9 B- J5 m; e3 y
SysFreeString(bstrText); // 用完释放
0 M0 F& h2 O( b/ v9 p. q
( b" x: u N5 h7 S" V (6) BSTR转换成CString
9 w. s# O/ N& X# Z! L6 e* v9 P3 \
6 }, v( B3 O1 c" v. f+ x$ {6 j 一般可按下列方法进行:0 f4 y$ Z& N; e g4 ^7 V- B
5 j5 B' R! y' H! f; ABSTR bstrText = ::SysAllocString(L"Test");
3 C' {+ k/ K1 _CStringA str;
/ a: ^5 P" x' d# t! [str.Empty();
- S; u- k% F$ i% B( p1 nstr = bstrText;
0 v8 }: \ N# i2 d5 m3 w6 P6 P7 V. D6 E; p, v* m
或
# f* w4 j( R* D
0 z9 v! v* I9 q/ [* | ZCStringA str(bstrText);
* W1 h+ h1 A6 Q' \* \% q3 R
* n- ?& r; `, N7 a (7) ANSI、Unicode和宽字符之间的转换
9 [4 l+ U. G5 q1 ]
% d9 Z0 b0 F+ I- V, U G g 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。# l7 X. {& f6 ]6 k% X( u
7 q& `* E* m2 b( v) v2 C 方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
. u/ f: v) X- l' b, } \+ E& @5 ~$ Q; `+ J3 a& F8 `0 n
TCHAR tstr[] = _T("this is a test");( ~1 R; |, F9 m! e
wchar_t wszStr[] = L"This is a test";
! i( S, Y2 A, W P" ~String* str = S”This is a test”;
0 ]/ ? s8 t L7 F( r" h! M& w; t0 f- V1 M( `$ r- H) d
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
$ c/ k2 Z1 x( _3 G+ A+ ^7 i$ s
1 c1 b$ K% o- d 其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:3 z1 h4 n- E+ ?/ Q2 o+ ?! e
# A% |5 h t7 ]
LPTSTR tstr= CA2TEX<16>("this is a test");- i0 O$ X/ X5 t$ V
LPCTSTR tcstr= CA2CT("this is a test");
0 n& C: @+ |& l* ^# M: dwchar_t wszStr[] = L"This is a test";
) w1 b( }; j& v2 Q9 r) h, F fchar* chstr = CW2A(wszStr);
1 C; [& V% C8 i8 z1 }5 W/ |, l" U
: y- D* H6 ~! t; K 六、结语$ C" T& [9 b4 V- ~" [' [
0 v J Y+ |3 |% M
几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|