|
|
2002-12-05· ·丁有和··yesky
/ ~; P" k( N5 q$ W/ c2 T 3 I; \5 v0 I& e0 h( ^5 a/ u* V$ y, o* E
% z& k) d6 k4 l/ g5 y Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
0 p/ P8 C5 S: n" Z. T9 Y$ c
6 u! D f& u/ q2 S" K, L* {' ` 一、BSTR、LPSTR和LPWSTR2 b) Y; R# ]2 @9 K3 L
& B4 d; U* x2 _- J
在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
; r2 p, x9 }9 o6 J V4 J/ j" u! d. T* f5 w, t
那么什么是BSTR、LPSTR以及LPWSTR呢?1 V* r# b& v/ S2 J
6 x9 I8 \* J/ T% h& g4 q' l
BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
5 y1 o8 {- i: m
/ D& I" ]; G$ d o% E LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。, ~: s8 l" {7 M7 l* e l0 I7 Z
2 ~; Z3 J4 V! c. J; Q0 l
例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
5 i, g9 P% U" U* q, `4 Q. x$ ]+ ~% t- q5 \) q0 p
一般地,还有下列类型定义:
4 f. e* P" D: b1 z$ L$ j. G9 H8 E2 X4 x" h: n' ]
#ifdef UNICODE + |9 i" X' U+ h- Q0 r
typedef LPWSTR LPTSTR;! R7 U' z8 Q, X- X
typedef LPCWSTR LPCTSTR;
3 l* g, Q& I: V; R3 {#else
) e' `6 g- L; P# j typedef LPSTR LPTSTR;
" a+ o# r% O. ?& j* l typedef LPCSTR LPCTSTR; 5 v9 ^# L- s8 {6 A9 n5 H
#endif ! r& o1 B) z9 o
, Y5 [0 _' T, L$ q9 g: r
二、CString、CStringA 和 CStringW7 I6 F# Q0 E _% y1 N5 Y, p
0 ~: i" _1 ?/ a) s" ~ v
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 K O4 }) s2 z. ?* t4 h
! N7 b" \: P4 S# d3 Z 三、VARIANT、COleVariant 和_variant_t
, s: j* n7 B6 G3 e& c( E7 S6 h; a% ~. Q& {- m/ `# C
在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:! o2 f$ V' r! F* C& x! D
' b; r5 z' o8 A, g
struct tagVARIANT {
" g) F8 D4 y0 |/ R( g# @8 S a& |/ O VARTYPE vt;8 K3 a! g f( P+ T9 ?4 F
union {* V6 I$ t: @4 h# T$ m) D
short iVal; // VT_I2.! ^0 W0 {7 e: H% j, m+ [. G
long lVal; // VT_I4.
5 ^$ [& D+ P# l5 ~7 h. W float fltVal; // VT_R4.
0 \2 F( J9 @2 C$ l" Y2 Q double dblVal; // VT_R8.
7 {# G" W- ^; _- U7 y$ p DATE date; // VT_DATE.' y+ Z) C$ `# u/ ~- {6 z
BSTR bstrVal; // VT_BSTR.; V% Q+ w# K6 k2 f3 o
…
! E: G) P0 H$ a, `1 }" ] short * piVal; // VT_BYREF|VT_I2.) g% u. ~( x ^. l. I. i6 n m4 a
long * plVal; // VT_BYREF|VT_I4.5 S7 i; p! H$ |+ W5 k
float * pfltVal; // VT_BYREF|VT_R4.
8 I* G7 j( `2 ]( J2 u double * pdblVal; // VT_BYREF|VT_R8.) N) a0 m% U( M) W/ |
DATE * pdate; // VT_BYREF|VT_DATE.
2 W% L. D$ m- [- \$ N: b' t/ b BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
% b9 j4 e' x* V9 P' b };; Z! q; j; N1 G7 [% }
};
9 Q3 a e! v3 d7 c/ q- M
% C! ]$ T- n9 j1 `: j 显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:# @ `8 k/ u9 y% e
4 m4 D* c; B9 ~
VARIANT va;- P, ]' ?; ?$ }* n, o6 @
:: VariantInit(&va); // 初始化
4 i1 h0 }/ A) `1 z( R: ^int a = 2002;
+ ~) C) e T }# gva.vt = VT_I4; // 指明long数据类型( F4 `( d" y" \, q
va.lVal = a; // 赋值
6 s# U, x) X" E6 G* N8 |# X2 G$ m6 A( `3 H$ z+ E
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:" @& n( Z) r' b9 {+ w4 b z
$ V/ D" I% k. d" r VariantInit —— 将变量初始化为VT_EMPTY;6 x7 H& b. {1 _! W( G' |
4 @0 Y j9 Z, s$ @
VariantClear —— 消除并初始化VARIANT;) D, C! m7 k2 c8 F& A" H- P7 R) @
: a7 H# S' u- u! H4 y# G VariantChangeType —— 改变VARIANT的类型;: C( d! j7 F& Y7 a) c! x% f. M; F2 i! D" A& H
7 e6 W) p' d2 w; j8 u+ E- X: ]
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。/ Q8 ^6 R2 b! X
2 k* i6 p1 d) C# v( O COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:: h# y8 q& D7 D+ E; k) ~( |
* t$ @% H4 W+ @) SCOleVariant v1("This is a test"); // 直接构造
1 h6 ~5 g- Q. u9 }/ q) n, h! ZCOleVariant v2 = "This is a test"; , N3 A) H( B7 Z6 u l) \. r: S
// 结果是VT_BSTR类型,值为"This is a test"
: g+ c0 f: }: l% z4 S2 j3 XCOleVariant v3((long)2002);# o) X3 K. t$ I
COleVariant v4 = (long)2002;6 l5 y) p0 l6 R, S( m
// 结果是VT_I4类型,值为2002
) T4 J9 E' B, f+ P& \
5 `7 A" o- v9 \ y) F- W2 p _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:- B, R" r- R6 x! p' y- e# W) D
1 b' A+ b9 l6 l3 P$ |$ V
#include "comutil.h"
( l7 s3 `) q* q! o ?8 t. N2 c% P5 ?+ O, z) r9 u/ d; C- d
#pragma comment( lib, "comsupp.lib" )5 q6 E5 ?/ D3 | d1 `' |
! V7 A" T; z. t% X% g$ n 四、CComBSTR和_bstr_t8 b: m8 V& p) |1 O' c
# d8 b( e0 b7 n- j2 ]6 F+ F
CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:$ ], {, b& ~6 _# W I: D3 U W
5 ]8 R. c1 x* u/ k M4 |
CComBSTR bstr1;
& d2 _ ^/ k7 Zbstr1 = "Bye"; // 直接赋值
0 u* Q0 X8 R' E% h, s" K0 O. ^' | r% WOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符: ?. M5 W U7 D& r! I& s; t
CComBSTR bstr2(wcslen(str)); // 定义长度为5: N) |: T$ J* ]' Z2 R
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
6 }# A3 K- @0 S8 U1 s1 {4 V Y9 yCComBSTR bstr3(5, OLESTR("Hello World")); 1 \6 b: K, f% \* a, |4 K3 t
CComBSTR bstr4(5, "Hello World"); , l6 }: K* }( X' c2 m
CComBSTR bstr5(OLESTR("Hey there")); / [& P3 T. A k9 q& L6 \
CComBSTR bstr6("Hey there"); - q: r$ h9 {) q, \
CComBSTR bstr7(bstr6); " ]2 S3 g& Z8 |- U g. q; q: V$ H
// 构造时复制,内容为"Hey there" $ ^( e- h2 ~* n8 V8 H: n
$ _8 |7 m1 c2 r2 a- b( G R6 A7 b
_bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。2 o/ U" F* b' V; Z1 V1 g
) l3 R( ^ G# h1 S
五、BSTR、char*和CString转换) T6 N! W9 P3 b) r% w- n; s( N* }
+ P* ^" }6 B. a9 D
(1) char*转换成CString
- l( U R) ]/ T2 @8 s4 k# y) @3 g! P" c9 v# x1 X
若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
. @) c2 X/ O- N& Y! o9 G
& x/ \, } j+ g: L" k* R" T- echar chArray[] = "This is a test";3 T4 X3 w- g( W/ ]! A5 d
char * p = "This is a test"; : l1 @ w/ y# b/ R) D1 I& N8 w
8 R7 @( E5 A2 h% Z
或
# ~, `4 f T1 G! H& N! u: W' B2 X" |$ y: m# p& t# A/ h& e1 z% q f5 Z
LPSTR p = "This is a test"; ; X v9 u% G" [
& \) O p8 L# ^, f3 T 或在已定义Unicode应的用程序中
8 I; g. G7 G1 O- ^
" @8 @. R, k5 U" e0 J6 V BTCHAR * p = _T("This is a test"); 9 ~, s- @, K) Q) j" h) `, D6 ^, \! g& z4 ]
4 a. @ B4 ?& E1 j4 }0 ?1 L 或% H. m& F6 q5 J* n9 l
. \8 r4 `" n9 t$ ?. W4 n
LPTSTR p = _T("This is a test");
5 V' ` J: r$ fCString theString = chArray;4 X3 g. N$ Y0 }
theString.Format(_T("%s"), chArray);
, d# a- S5 e7 I! |! ^8 otheString = p; 1 N) B4 j, f t7 M. J
2 u- D8 p0 |5 b# P% j
(2) CString转换成char*
% l8 ~3 d1 ^, S. d2 z9 C% @* P4 c Q3 T2 Y% p5 H! S, R
若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
2 u0 u) [. m5 }+ N0 c! r* D8 E5 \- \* [% y0 f9 e3 A* s. j
方法一,使用强制转换。例如:/ M( g' ~# D! S3 f! b: H" o
7 S' }+ x# f! F! r
CString theString( "This is a test" );8 z# @9 G( z! k9 s) x+ q: q$ w
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString; 1 g& p4 E$ M. F0 \! `
, c* e" r4 m2 h2 }( N 方法二,使用strcpy。例如:' }5 Y, i5 V6 W
! i2 o7 ]) L/ ^( E5 H" A
CString theString( "This is a test" );0 o' ^" ]) ~! Y$ J7 S8 B& [
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
+ h E% @0 W+ I! q9 f_tcscpy(lpsz, theString);
% ]( e% \. ~' j' H: }$ f" }7 B( P3 R6 j
需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
! i2 ?7 d9 ]4 S; |- u; M8 M- o4 s6 Y2 s$ D" g
方法三,使用CString::GetBuffer。例如:
3 x8 J" m ?9 e( ^
/ [' ?* M' W' m" v$ P5 E- LCString s(_T("This is a test "));
7 r9 u' }1 W( @- N- D2 i: ~0 a" ], e! GLPTSTR p = s.GetBuffer();
/ f+ u5 U6 m! f& T( p7 } |' a// 在这里添加使用p的代码
! G0 N0 U. x; C$ Y. Qif(p != NULL) *p = _T('\0');& c0 I. E8 r! Z- m" b) B
s.ReleaseBuffer();
- W5 S& H7 A' U2 r. Y# Q6 {// 使用完后及时释放,以便能使用其它的CString成员函数 9 s$ t. f& [8 r! L
- ]' `2 h9 { ~# r3 A" O$ W, F
(3) BSTR转换成char*1 P$ j- y' ?& I7 _ M+ o. j
- C- A1 j; L9 _) `- F4 I 方法一,使用ConvertBSTRToString。例如:8 [4 @, U* ~7 b1 X5 k1 k r
0 o1 k6 {% D2 D3 L#include / P4 T( \/ g+ g7 B
#pragma comment(lib, "comsupp.lib")
! C) z: c4 u6 M0 ]+ `int _tmain(int argc, _TCHAR* argv[]){0 y" [% E4 u8 a H% g j
BSTR bstrText = ::SysAllocString(L"Test");
! ?# X' V% v0 g2 i: U Achar* lpszText2 = _com_util::ConvertBSTRToString(bstrText); X7 m6 {) [0 h- ?7 n% a
SysFreeString(bstrText); // 用完释放- c$ Q: e1 K0 R+ h3 Q
delete[] lpszText2;
* A' w$ D, [# [! ^ Freturn 0;
" @: `1 H/ `" S& @}
8 _$ F! a0 L/ p; k* ?. Y: Q9 K4 s1 ]9 G
方法二,使用_bstr_t的赋值运算符重载。例如:
2 W/ b/ r( n. E" z$ q
" h( o& p, n! u- E0 l7 P! C_bstr_t b = bstrText;
- ~" ]0 E" s. Wchar* lpszText2 = b; & F# y+ L+ z, w4 n( C9 j
, a' \1 I' B6 H- { (4) char*转换成BSTR
) _ L; H3 v* y' Y3 K$ N. t% P8 H7 e- r1 N5 A& d* c; _* h( n- f. D! Y
方法一,使用SysAllocString等API函数。例如:
, @* j4 A3 \ P5 M" c' C2 [' d. A/ T
BSTR bstrText = ::SysAllocString(L"Test");
: N* @% F8 ~1 K$ N2 _* F2 |5 b3 QBSTR bstrText = ::SysAllocStringLen(L"Test",4);/ `5 G" Q' n5 V' y
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
: v" [$ p5 k; i: u6 q( n) E' J/ V! V: k* h1 t
方法二,使用COleVariant或_variant_t。例如:
6 V: L* ^- r$ S6 |' Z! Z: c: g4 z( J6 E$ W! Z7 n
//COleVariant strVar("This is a test");! W, c+ t* K" h
_variant_t strVar("This is a test");
2 g: }7 b4 O" S+ X0 o% a3 {BSTR bstrText = strVar.bstrVal;
& b4 ]+ c* K/ o3 u& Z* O3 H: V x5 ?* q, u1 T1 b( Q& M
方法三,使用_bstr_t,这是一种最简单的方法。例如:5 w! l$ Q$ r0 S/ B v
4 i ~* \: \! q- x( s/ sBSTR bstrText = _bstr_t("This is a test"); 7 w& g8 n2 {9 {* Y2 y+ X
3 a4 u5 n% M8 Y9 P$ n( p
方法四,使用CComBSTR。例如:+ _# {+ n' E( @
@) k3 t& k5 y O7 E# x; VBSTR bstrText = CComBSTR("This is a test");
, I% ?3 M' F& X4 S4 v& C( m+ |
; l5 ?* D) A# L8 y* M- s# v" _ 或
* q J* D4 `0 P7 q6 T1 u: O. J4 K5 `* v* I$ V1 Y# I( F
CComBSTR bstr("This is a test");$ K! G& f, X: }5 O& W3 v: c5 {1 ?
BSTR bstrText = bstr.m_str; , |, x; R2 H* o' \ W* s
4 D4 w* K% T" s7 R 方法五,使用ConvertStringToBSTR。例如:
7 O. T* Q+ l7 g; E3 M9 f; z! V. O3 ~( L8 h* P# A
char* lpszText = "Test";
' N- M% ~6 z; o8 S" R wBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
9 x6 b6 t1 ~1 \. |; J- r
+ W1 u5 @" \3 }: b1 s (5) CString转换成BSTR
" h6 J' o' f" p; \
4 d1 k; p, @( ~+ s/ Y6 @( e) | 通常是通过使用CStringT::AllocSysString来实现。例如:" N2 d# W- }) O# _ k
% ]5 ?% U' B# \" y
CString str("This is a test");
' T2 q) a& D: l$ ~: {/ S: M3 V; [BSTR bstrText = str.AllocSysString();' Z1 S1 }' ]5 f$ S
…. {; M! F- i" v p1 T3 K
SysFreeString(bstrText); // 用完释放
# @1 w* j1 G5 ]2 |. T
( e" Q0 `) |6 s( z# W3 L2 V (6) BSTR转换成CString* |5 k0 L6 G/ m% {
& q8 O! J( ?5 G! t 一般可按下列方法进行:
5 ~+ M7 c+ @+ Q" L% e! S) V+ u2 S. J5 Q
BSTR bstrText = ::SysAllocString(L"Test");
% w% B7 L6 e8 P" a6 VCStringA str;6 o0 f# D. j2 W1 j
str.Empty();
0 O2 h9 I5 R, l* j4 I5 p9 {str = bstrText; ; s! h% z/ w p6 h
' \# F D! \! T3 p$ ~$ f2 P 或
9 G7 M3 f- R `, c9 I0 c; Y. V4 O# A
CStringA str(bstrText);
2 B, X. I0 t9 m
/ z3 ]; w0 G' E; ^5 } (7) ANSI、Unicode和宽字符之间的转换* K9 k9 ?, }3 r' C6 w1 g/ b) p
& a: g" ?- ]2 D' f/ v 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。' E' q+ T5 A8 w4 y; c3 ?# o+ `# K
& Y+ K$ O, Q2 ^. {6 C L9 [% w
方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:& ]9 C: G3 J; O5 s6 U* Q
) z- V% ]+ I1 R5 q8 `
TCHAR tstr[] = _T("this is a test");
( t1 G& n$ e! cwchar_t wszStr[] = L"This is a test";& H3 z) i0 u" E$ {, w7 ?
String* str = S”This is a test”;
L6 ~; ]6 A0 X9 c# B4 Q8 a+ K- o" @- S
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
; e: V" O. T8 E+ V
) ^! S$ d; k" `6 ~. }0 O 其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
9 X0 f8 ^& Z% q% y: B# D" F! z, X2 u% `
LPTSTR tstr= CA2TEX<16>("this is a test");
* h( w+ _- ]- _& \& g' QLPCTSTR tcstr= CA2CT("this is a test");
; O+ O b: w' ?: y9 @# ]wchar_t wszStr[] = L"This is a test";5 B5 s9 H! J5 {3 ~/ C7 X1 {
char* chstr = CW2A(wszStr); 2 {. o/ B- L0 H/ e
( x$ I! ^& d8 [' _/ y/ _% z" n 六、结语
; P, e" U1 H/ T: P" ^% h! i5 L
几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|