|
|
2002-12-05· ·丁有和··yesky
/ e- i: |, E: c; U ; x# x% }7 U7 a0 L% e
0 k0 l1 `/ e* @2 u0 }
Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
, P* J4 d6 i9 ?9 D O7 D4 l }! s, p f" } D' K3 F" h
一、BSTR、LPSTR和LPWSTR
9 ^' s1 v( x: h3 ^8 F, p. l+ `( b8 K2 J$ o6 N
在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
0 b ]5 y$ O* C0 Y4 X$ Q4 i1 @" e+ {5 `% [
那么什么是BSTR、LPSTR以及LPWSTR呢?
" U& o8 y0 v" [ S, @% ?! k' Z; V+ U! X0 K7 {+ n+ ?
BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
0 _9 q' B+ w9 Y: a/ S, P4 o* e9 o( b5 N$ B9 ^: V) @
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
; J5 K# G: o& S$ i1 w) h5 i
" N% }) {( r& `! n) _1 v' E- v1 I 例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。5 ?1 I. r8 ~& [! ]
! J1 Z, f1 j$ L7 z5 W$ K 一般地,还有下列类型定义:
' M6 S2 h' s; ?$ Q8 _0 O" Q" ~- q: d8 G9 H1 r( r0 W
#ifdef UNICODE - J, z, x6 L8 N$ N$ T
typedef LPWSTR LPTSTR;% K' d! \# J7 k) ~6 ^
typedef LPCWSTR LPCTSTR;
# v: N. s _$ H, \+ V& k#else
2 C3 B9 s* Y& M4 Y: A k" U- f typedef LPSTR LPTSTR;
5 m5 e. `3 K' p$ m8 {' g; ^( ? typedef LPCSTR LPCTSTR; H7 M7 ^% B; |1 C6 R$ f+ F
#endif
) W- o9 I2 ?$ W: M v1 Q. y& n3 c0 N
二、CString、CStringA 和 CStringW8 U. V7 a! J) ~) M# N& [+ i
. T5 C) m' T: L( ^( d
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应用程序中经常用到,这里不再重复。
9 v9 i8 l& b7 r4 w0 ^6 B
# O3 B" M* O' ` 三、VARIANT、COleVariant 和_variant_t- P8 p) ?" u: o, C9 {
9 H) d% {0 ?% p8 [+ ^% c 在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:# W; @" B @9 m' M! o3 N
& g6 W F6 Q/ \
struct tagVARIANT {
" M7 M& m0 L! i5 j& P* Y VARTYPE vt;
, Y/ V- Q" y+ n0 s6 o" \' K& n: ]: ] union {2 o8 j# p. X* R- n
short iVal; // VT_I2.
# M ?( U% e4 \6 N: q& @ L( L long lVal; // VT_I4.
( g3 }/ K0 s5 i2 p" i4 d float fltVal; // VT_R4., a; j. h/ ~# E0 l3 Y) ?& U
double dblVal; // VT_R8. z7 ?4 g9 [7 w
DATE date; // VT_DATE.1 H( q# a( K/ C/ [! N% ^& ]
BSTR bstrVal; // VT_BSTR.' I1 C9 u8 [; m1 f) U: B
…4 N6 y) I T# K4 w0 ^8 B
short * piVal; // VT_BYREF|VT_I2.7 B! n+ m) Q1 k) e
long * plVal; // VT_BYREF|VT_I4.
5 U- V2 P* W' V3 ~/ e float * pfltVal; // VT_BYREF|VT_R4.
+ `+ Y+ ?8 G. ? double * pdblVal; // VT_BYREF|VT_R8.. m0 }# }( L" K; d+ K7 i
DATE * pdate; // VT_BYREF|VT_DATE.5 ~2 S4 j% q4 {. ?5 W6 o* Y, n
BSTR * pbstrVal; // VT_BYREF|VT_BSTR.1 D* t: D e" ?% y7 v" E+ b9 m
};
6 Z4 H" y( I0 y4 f! l0 b7 O};
- j W8 ^( a- a6 f- k: J5 M2 u- q# j5 T6 j
显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:5 E' t ^3 d" C/ ?& ~
* u; Q$ h/ J7 ~9 Z" H% EVARIANT va;- \# H8 e4 a4 n. ~; p: t& b
:: VariantInit(&va); // 初始化
" j' f' D7 L* Q6 U; |( c/ u% Pint a = 2002;2 O7 Y" p" {4 |0 q5 B5 M
va.vt = VT_I4; // 指明long数据类型
# q2 G$ w6 B7 ]) ]$ n$ kva.lVal = a; // 赋值
! N+ F3 ~, g: K5 V9 i5 j" F; V9 r2 g: K$ a' r
为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:; d a R6 O3 `, ~' x' k
0 ^# c2 c$ a7 _+ Z- M: @6 q% ] VariantInit —— 将变量初始化为VT_EMPTY;
3 B* j6 v, s4 W; n E4 e' e" y# K
2 y C t. B/ E: W: z7 X& R) g* J VariantClear —— 消除并初始化VARIANT;( p1 {* R" k$ D
% v' b. S9 a T/ f+ [2 N VariantChangeType —— 改变VARIANT的类型;
1 I9 Q7 B& l% b/ ?9 {# t2 K( f5 r3 d2 y/ b1 f S( |; i8 U- y
VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
: P8 {. b: i8 m- T2 j7 T) M( V3 x2 E- B, Q! X8 v8 S6 \. t
COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:$ V* m3 ?1 [+ U( a! [* E
X$ R5 I: z" @1 ZCOleVariant v1("This is a test"); // 直接构造1 ~3 ~& c9 A# ]" i9 T" ?6 ]
COleVariant v2 = "This is a test"; ' d# x8 T1 P/ n, u7 x. a
// 结果是VT_BSTR类型,值为"This is a test"+ E; L3 Z! A$ {' Z* t4 M+ z0 u
COleVariant v3((long)2002);
, o7 B2 a. m6 k0 fCOleVariant v4 = (long)2002;# P! A3 p# E, @) Q2 k$ E3 _6 ~
// 结果是VT_I4类型,值为2002 : J" R8 {9 Z/ l+ x$ ?. n! }
7 r M- B& m4 P' }! _ B _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:" N; g& [8 @( ~: G
) y0 R% \6 [/ j2 V0 @
#include "comutil.h"
/ }8 G- B. S" b) {$ J* g/ d0 x. i, `* ^. K7 B9 m
#pragma comment( lib, "comsupp.lib" ): l) V- b: b+ U8 c
, Y2 x$ s3 ~+ z" L+ e6 l( h% {
四、CComBSTR和_bstr_t; B0 _4 W1 l+ n( O# P
4 V/ R5 |, } L1 G" X CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:/ P/ D* u2 k5 k; }8 ?1 J
0 I R8 h G* k) B" |
CComBSTR bstr1;
) V! A! H/ w3 @. c2 ~bstr1 = "Bye"; // 直接赋值
) F; u( Y- X* OOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符
1 c" ?2 w% L; _5 I1 _CComBSTR bstr2(wcslen(str)); // 定义长度为5' z+ r) z5 V1 G! o5 ~; Y# |- Z
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
2 W2 u8 ]* B- y" x- o( [CComBSTR bstr3(5, OLESTR("Hello World"));
7 P5 t& U" {- _/ K/ Y) MCComBSTR bstr4(5, "Hello World");
* B% s. B) D. G U& n/ t4 ^CComBSTR bstr5(OLESTR("Hey there"));
$ N, a( g& v9 U. W7 RCComBSTR bstr6("Hey there"); * o0 n" d. [2 `" ]4 r: s# ~
CComBSTR bstr7(bstr6);
$ F9 z, z1 q; F* I- Y' g// 构造时复制,内容为"Hey there"
# Q5 b* C; ?# m/ X' T
. O. C; j- a" U _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
$ g' T) I( k$ s6 s; y/ V6 O+ N% F- z2 {, k% I; Q
五、BSTR、char*和CString转换
/ r5 h l% k: u8 ~$ G; s" Q6 P6 {% b4 }; C- u' G# O
(1) char*转换成CString1 A' t, P( }: v+ A% V7 U& S
9 v8 X O, C5 v4 a! H7 c
若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:: g6 |2 B& j3 S! T6 O, t! q7 y9 l
$ T- R4 F# M2 c a1 h! dchar chArray[] = "This is a test";
- k8 Q4 i! e' A* [8 Jchar * p = "This is a test";
. \: }; b- p4 V
1 h* E2 w7 U' _$ b. O" f/ T% r) Z& d 或
4 l* v; R7 v; R% [ @' `7 p- T: L5 {. ]2 @" h; h. C2 c
LPSTR p = "This is a test"; 6 D% R, v: n n9 L8 |( J; b6 O
0 {( D9 Q' x/ t4 y: I6 {" w6 \" Y 或在已定义Unicode应的用程序中
5 O7 o1 B/ q1 n% ]
" ?# i% U( j( N9 V3 [TCHAR * p = _T("This is a test");
. E) \# y5 @: f' H- i/ l$ L4 _
- |* f+ W) w* w w1 \0 s w H 或& E n% e% `" P: W ?& Y7 M6 \$ O- w
+ X& ~/ m0 |- N4 f# f' m( n x: X$ Y* \
LPTSTR p = _T("This is a test");* T4 j/ e- |) |* ?7 Y" S4 j) h
CString theString = chArray;% U0 [6 [ U; A8 A2 ?
theString.Format(_T("%s"), chArray);
( W: X- ^8 E; u2 s& h1 RtheString = p; . w- @7 ]% X$ ]
% F5 I2 a# u+ N: [- `5 _
(2) CString转换成char*! O1 h8 c3 O% A# h1 {* q! c
" a+ `- t# c0 u* G: Y; C 若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:1 F' f! `& a2 b8 t5 c: N1 p
' t: T( c1 L& f! x4 { H+ F+ k
方法一,使用强制转换。例如: S2 {$ C, r7 P e2 Z
3 @: W/ P# h7 u7 ECString theString( "This is a test" );) ^6 a0 O$ K& u9 Y7 L
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
( I0 {" l W5 A6 Z8 C3 {+ w- Q; W
: O$ O( r' J- ~9 t4 _/ v5 Y$ U 方法二,使用strcpy。例如:
, ?8 V& u2 j( w" P6 o' y) b% v1 f5 |4 @/ a$ N! L
CString theString( "This is a test" );- b' K- l+ T5 Q! Z( W7 ]( `4 G
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];2 W5 k1 G6 E# ]" K
_tcscpy(lpsz, theString);
' H& K+ W$ u$ I7 L
9 g* U1 [8 ?* m. u. E- n: l& y. ~ 需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
9 \9 _2 s* p( r m: E8 S9 |+ |5 Y3 h# w1 v- L) H' R
方法三,使用CString::GetBuffer。例如:& ~9 v: n! a# u* {% x7 |
4 M$ V) X; P7 ]& a$ o
CString s(_T("This is a test ")); K! ^' @- X, y) {5 A' J8 B
LPTSTR p = s.GetBuffer();
3 X. O% H6 [( U& a3 J. D* F// 在这里添加使用p的代码
5 |6 D# e3 v6 Pif(p != NULL) *p = _T('\0');
1 w' r# c; |! g3 v/ g5 ds.ReleaseBuffer();
. [" z$ R2 F" j1 n) D9 _4 }; g// 使用完后及时释放,以便能使用其它的CString成员函数
0 X& y7 v& H5 Z$ K7 R: n" b% T+ c( m
/ a' h! P+ G7 x (3) BSTR转换成char*8 P( ]$ O3 h3 ?
" \; k i- Y: v- a: i, v: [ 方法一,使用ConvertBSTRToString。例如:
; N) b/ R9 e+ E$ M: h e; F/ M4 L' \$ M/ O6 m
#include 1 B2 E8 a2 O' \
#pragma comment(lib, "comsupp.lib")
) Q0 L; E: Z$ n6 m; } D+ J& kint _tmain(int argc, _TCHAR* argv[]){
( C9 }5 P! X! J$ |BSTR bstrText = ::SysAllocString(L"Test");- j" K/ F2 Z1 q/ q1 \
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);/ D6 X) |# i. A0 e' e: p. k
SysFreeString(bstrText); // 用完释放
7 H- M2 S* ?+ f. Z4 j9 X- cdelete[] lpszText2;
' @: ?4 u( X* g; A9 V2 ] xreturn 0;
* I. M% C1 s4 j) h' G}
6 z2 A f1 M7 H8 h# w2 g3 k3 j% t5 z9 ^0 U6 R6 U N
方法二,使用_bstr_t的赋值运算符重载。例如:
% o& \( q6 |8 Y- `$ y. Z$ E: z0 |. c7 D: F b) ?
_bstr_t b = bstrText;; p3 b3 a2 p" v* q9 S' [
char* lpszText2 = b; 5 G* `0 Q% D6 V! [; L4 T
! g3 m/ Z' w8 Q! s( u& A
(4) char*转换成BSTR3 |" t; Q4 j* `: c* d& a& e) ?. _
6 r4 Y: L( y) P5 T; B9 S 方法一,使用SysAllocString等API函数。例如:
: z& \6 O, U. k5 L; C) r, s8 q$ L$ a) c: X8 g6 X
BSTR bstrText = ::SysAllocString(L"Test");2 k o' o8 G; f, b6 _' V
BSTR bstrText = ::SysAllocStringLen(L"Test",4);7 d3 q, b7 R- ^- _
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
) C; Z4 ]2 l! j, k- q2 A
3 ~6 [. h* ~7 P/ b8 S# L 方法二,使用COleVariant或_variant_t。例如:
/ z; g: L* a7 A
3 Y* d8 a w2 v0 L4 `3 k//COleVariant strVar("This is a test");% V. H+ ]7 `7 R5 g
_variant_t strVar("This is a test");; G- L9 @- e# q# n8 `( x3 d* u, v$ A9 n3 E
BSTR bstrText = strVar.bstrVal;
8 ]9 ?/ B) I. ~4 h, {0 C! N n5 J R+ c/ B
方法三,使用_bstr_t,这是一种最简单的方法。例如:; b& N V, B7 `# O, G
$ e4 m! y( u6 w2 F. s
BSTR bstrText = _bstr_t("This is a test"); * B: `; s: |$ P) T
! W x) s5 G0 u6 m' @5 |/ X5 E1 x, P: I, m 方法四,使用CComBSTR。例如:7 y" a7 v8 r' o0 E. J- \6 f2 Q1 K
4 g/ ?5 R: [# x, u- lBSTR bstrText = CComBSTR("This is a test"); ; q) W1 a* `' s9 w
1 Z% _. {% w) Y" n D) ^( y 或
6 n$ g2 k$ E q% K( }- |
: P4 @1 t; \9 ICComBSTR bstr("This is a test");
* K+ S3 J; r5 L; j. iBSTR bstrText = bstr.m_str;
0 ]2 O. {. X- z7 n$ `
5 e" P% R+ P# P 方法五,使用ConvertStringToBSTR。例如:
) f% x( J' M9 E8 |* \0 c! R' x4 T5 j5 w3 |
char* lpszText = "Test";3 ]% g3 O$ g- X) K' {
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
8 C: H, y8 P* A% R8 E2 f0 {- J$ ^7 n( V# a' u
(5) CString转换成BSTR
) l7 V" d* O# `7 {- e5 `& M8 w: e. y; x
通常是通过使用CStringT::AllocSysString来实现。例如:
# ]' u y4 _- e1 Z4 `' \- i! o1 @. r
1 q- R5 T% x7 E8 H4 u3 K3 GCString str("This is a test");& o" a6 T/ D( d3 O) R
BSTR bstrText = str.AllocSysString();
+ C8 g+ c3 T# l; T( D0 E…9 _0 f$ j! F- T6 `- X0 X0 C
SysFreeString(bstrText); // 用完释放
& b+ N' s7 w, v1 Z# O( O- x, I
# v6 _7 {; [; c+ E2 ^3 ?! W (6) BSTR转换成CString" C) x+ n6 ^- W; }6 }0 o7 [
% A8 n2 G* c8 j2 N) L- ~1 y 一般可按下列方法进行:& t! k& V8 f1 Q+ @% Q, ?3 n
1 u/ B+ d9 F8 ~6 v
BSTR bstrText = ::SysAllocString(L"Test");5 ^0 Y: U( G# C% J, ]8 x( W& v
CStringA str;
. K; h( s* I% i0 y2 ^3 z: C; c; dstr.Empty();+ s; D* P' ?' L3 \- w$ U
str = bstrText; 0 y1 q* c9 ]) m% B, c f
* d- E" d3 v+ Q+ S. {9 a 或
' O: j3 o% Z) \7 ~# U5 f1 J0 I, B) k/ w% U
CStringA str(bstrText);
" p9 U3 w4 I+ ^0 P7 ?
' v j Z+ r- S& Z0 K F! M3 ] (7) ANSI、Unicode和宽字符之间的转换7 l+ F+ w) e0 b8 g# ?* M
* ^: w$ S0 W: }) y. P 方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。% f( _5 z. U7 Y8 q$ L3 Y9 u$ f- `
1 Y* b" t; h% {0 C 方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:4 V8 J0 t/ Z3 J7 E9 c& Z) G! F/ A
. s( y% L* K4 p# ^
TCHAR tstr[] = _T("this is a test");
& a0 Q9 \9 G, h% o& [9 H, Z, Ywchar_t wszStr[] = L"This is a test";
" o: n* w+ {4 PString* str = S”This is a test”;
1 y6 h. @: Q9 M+ U$ D4 C& E7 m* ?7 o
方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
9 j L+ A# V `$ Q) P) S; B# F! I* a9 B0 f& w; x
其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
- G4 @5 h. D( H( `
$ K8 Z3 B$ `& p: ?LPTSTR tstr= CA2TEX<16>("this is a test");9 s# V8 r) x- |' m% ~& E# n
LPCTSTR tcstr= CA2CT("this is a test");
, T. ^4 T4 o6 i/ ^0 Y# i2 ?wchar_t wszStr[] = L"This is a test";$ A* g( S4 Q: j1 O. }
char* chstr = CW2A(wszStr); & @' p+ x( y9 c2 L" P4 p' |3 l
7 C, Q$ w2 M! ]) j$ U) a
六、结语
2 S- T q X, w2 U" ^+ O) m. _6 U' d/ _8 x% T( }
几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。 |
|