找回密码
 注册
搜索
查看: 4622|回复: 0

Visual C++.NET中的字符串转换方法

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky+ Y! x# ]3 R' H% V& ?5 M" n0 {
5 o4 w! R+ |# U$ Z
: \: G4 u( I2 l2 x8 u; A
  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
' D% [. \( E; z( r) Z( Y
6 j# d+ o8 V; J1 r8 |6 a  一、BSTR、LPSTR和LPWSTR
- \( l& [  o. u% I% [+ W/ q; t4 a5 E- @, y! T+ P
  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
" U/ W' b- d/ W! Z4 Z3 [) \1 R2 z2 Z; J; l
  那么什么是BSTR、LPSTR以及LPWSTR呢?- f' ?/ A0 i% x, r/ x% s
+ a6 `# x3 O, H4 P4 e) t/ ]
  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
9 l4 U; b0 m& o8 \# L
! y. a3 l& ?  Q& s  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
! I8 H# c/ C# r7 d
7 k0 J* }  h) L. v+ B" H  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。! F7 `) i, y9 S& P
! y9 ~' d& c( Z* {( D5 n0 L
  一般地,还有下列类型定义:/ ]2 B$ x* ^! C* }/ W- l
) n  ]" K6 O& ~0 `* b
#ifdef UNICODE 1 `4 k; b2 M/ A5 x
 typedef LPWSTR LPTSTR;- I2 L- G6 b  U3 v" X) a* n, [
 typedef LPCWSTR LPCTSTR; : m" Z# A* f$ T( v, N& h2 H
#else 3 F. [: @4 a7 q7 Y9 U% ~1 N, ^% ~& Q3 i
 typedef LPSTR LPTSTR;
5 r3 d: H2 u3 Y typedef LPCSTR LPCTSTR;
" O- D- u. T1 Z" u#endif  
$ H; z( Q0 c, ^+ P( Z+ V* r7 f& g) \
! c8 u: m4 c0 u/ z' S  二、CString、CStringA 和 CStringW
! p2 a1 d; Q4 E
, ]1 ^4 I8 p$ z8 E+ i  K  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应用程序中经常用到,这里不再重复。1 F( `6 c$ k: K1 P5 E
& [3 ?. j' I! U6 H
  三、VARIANT、COleVariant 和_variant_t! D" g& u+ y. Q5 A$ i

0 |; q( @# H( ^( L+ D4 Q0 s$ T  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
4 i. X8 \1 k' ]4 k- d0 k5 G  W/ c3 q2 }; [
struct tagVARIANT {4 s$ D+ Z8 [% o' |8 i7 T% {/ m
 VARTYPE vt;
8 H2 k2 i. K& @% ^- X9 {8 W union {2 _' z( @$ j, A3 _$ q6 D, s
  short iVal; // VT_I2.
# b: X, u9 N* N+ e3 Q  long lVal; // VT_I4.! R9 T% g% g; j$ k
  float fltVal; // VT_R4.
- h' K4 m& c4 n6 _7 s  double dblVal; // VT_R8.. ?+ f2 h$ Q% k: I7 P7 I  m2 e
  DATE date; // VT_DATE.
* i5 t" Q! F, |0 C  g+ Y  BSTR bstrVal; // VT_BSTR.. R5 j& U1 H( I: }( ^: |
  …
  C0 B; V* }9 D2 h+ l  short * piVal; // VT_BYREF|VT_I2.: u7 G& I0 }5 C% c
  long * plVal; // VT_BYREF|VT_I4.0 E% J+ A6 b* R+ K& U0 u6 J/ }
  float * pfltVal; // VT_BYREF|VT_R4.1 w8 P5 t' t% B7 U  i0 _+ [
  double * pdblVal; // VT_BYREF|VT_R8.8 j" p! L! ]# ?7 _/ v: l% X$ O
  DATE * pdate; // VT_BYREF|VT_DATE.5 t! d5 b: `" \9 h+ V+ f4 {
  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.6 q( e0 D" ?0 L1 r1 W& m1 V
 };' `4 Q% R0 v4 k& P3 r
};
/ J& q% ?$ I. I% s1 E% U3 s+ A; m3 e& W( O* Y
  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
1 d2 J& d8 B, f8 t
+ L7 M9 \& f! k; E8 kVARIANT va;
! f; P. D& N6 Z0 G0 h) w:: VariantInit(&va); // 初始化" ^$ ?' J" J  z$ J& w6 O0 T
int a = 2002;
: h: K# c; x5 [va.vt = VT_I4; // 指明long数据类型
: v" t, M2 z) \- k* v6 N0 K$ S$ lva.lVal = a; // 赋值   g- C/ O# T+ ?# i; f/ W7 N: |

) X  I5 I# v4 n  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
# \7 H7 v2 j4 U5 \0 Z* r& }
9 T8 {* @+ j7 h( m  VariantInit —— 将变量初始化为VT_EMPTY;
, V% u, m7 S! t7 b! A) l8 U0 D& {5 g, a/ Z
  VariantClear —— 消除并初始化VARIANT;" [+ r( R( \8 q+ \, `, V! S0 f

# n0 I+ p0 h7 \  VariantChangeType —— 改变VARIANT的类型;
& X6 G9 I, W. h' w
2 \- k1 ]  m, R( t/ O# q. n7 B  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。1 R( Y; A' W" @9 E! @
; c' W5 I- h  c
  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:) F- B% W2 t4 M" ]+ F1 p; \% D; N2 x
5 ?0 `/ n" J! P0 D7 h
COleVariant v1("This is a test"); // 直接构造6 l4 ]$ m1 u+ Q% w5 e* O0 T0 q7 W9 m
COleVariant v2 = "This is a test"; ; X& }) G" s/ Y0 i
// 结果是VT_BSTR类型,值为"This is a test"
4 P- Y; X9 N3 ~' ?- PCOleVariant v3((long)2002);
3 ~! A; k5 F$ ]COleVariant v4 = (long)2002;7 x+ t/ p; H" t, m( T+ u/ K! s
// 结果是VT_I4类型,值为2002 2 S' e' k* J/ ]  c- H4 k/ `( f
  \0 L9 E$ c8 L! f
  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:
) v2 C: _1 f. R# S3 G0 \
* y% J9 J) V2 g5 E  #include "comutil.h"
. |8 F* B: f( W- |
, g4 ^7 G1 ]' t) T% u  #pragma comment( lib, "comsupp.lib" )' q0 S# [, |# J0 k& @
- g6 r1 h- A. D
  四、CComBSTR和_bstr_t
. K2 X# W3 }. {0 D% \& ~) t4 X
. \& q( q/ T9 k  h: m, x4 ^  t  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:! H7 k! R5 a2 {" g% J. [

: i# o' G2 U) i+ h! c+ mCComBSTR bstr1; ! \, t2 g' p  }* l
bstr1 = "Bye"; // 直接赋值
1 D0 P& }# X5 X+ c) f4 U1 GOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符% e! }. O) j# ?
CComBSTR bstr2(wcslen(str)); // 定义长度为5
6 D/ [( ?4 |# z, K: w7 kwcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
" H$ ]+ l7 }: Y$ z* nCComBSTR bstr3(5, OLESTR("Hello World"));
& b, J! e5 c. JCComBSTR bstr4(5, "Hello World"); " q& L8 w' T$ q: s+ G( [
CComBSTR bstr5(OLESTR("Hey there"));
+ H; i, p3 c6 ^4 |# L0 ?CComBSTR bstr6("Hey there");
2 Y+ E! p1 J3 [  s) fCComBSTR bstr7(bstr6);
& P, R- `6 v4 {9 F7 J2 |( n8 u( H// 构造时复制,内容为"Hey there" , A( ?0 E; q' u' X- \0 m

9 `2 w( U0 C7 a* K0 F  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
# m6 r# q+ Y; o2 }& O7 Y% a+ \
# g, K, B! d7 _" E" q  五、BSTR、char*和CString转换
0 u9 U$ Y9 l' ~; T$ [; [- j. a6 V' H4 M( ~3 N3 \
  (1) char*转换成CString
8 ~2 l5 j, \5 @1 d, x$ x" ~1 S. r( }, {1 r1 D- A
  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
- e/ ^7 ?1 S; h* K3 K4 i$ ]# d/ N
3 ]( l: z' }- p3 \! _5 Mchar chArray[] = "This is a test";
; P/ @% j7 \. _char * p = "This is a test"; 0 j5 Z; j, a3 C  ~+ q3 T

' N+ J% x. w' {$ c( y- ^8 `* M  或
% H" ^, k1 k8 j6 T
( C; q/ X5 i& k$ |! ^; ZLPSTR p = "This is a test";
& K/ r: _4 @) ]/ v& ^/ `9 N$ D; x  E8 j! d, _' G
  或在已定义Unicode应的用程序中8 S3 g6 A3 l$ V
' P' I" v; ?6 u: N% S. j4 [2 `
TCHAR * p = _T("This is a test"); 2 z- |. ?$ x# b3 ^3 v  K9 A: @
; C- K$ p+ l% |  f: s
  或
7 h/ U# Y7 G* i( D/ d1 m% M
9 I% c; R4 c! ULPTSTR p = _T("This is a test");
$ D  c7 }8 r+ ]CString theString = chArray;
6 u' j" b/ h0 g" p" k% ntheString.Format(_T("%s"), chArray);
$ X1 b8 e; H" m4 V' UtheString = p;
+ U- b) q2 u6 S( W* [, \' @
2 s" \) ^1 U% j  (2) CString转换成char*
0 u4 L! R: g) Z0 T4 r: |/ G! Z
7 e" T3 X  K4 j3 O; s5 p  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:) z; V) ]7 G: x- K; F) Y, D
, [) [5 q7 _: T  P# f2 R
  方法一,使用强制转换。例如:% U* N8 v, K7 Q
3 d& }* q* I6 G* P& f- ^) X0 e
CString theString( "This is a test" );
9 L( m$ M" h& I2 Z; [0 g& K9 ]LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  ; j' j, T2 ]  f
; t: O" e+ L5 G% _
  方法二,使用strcpy。例如:( T9 W- V) C, E& |( \7 ^
" M' a% M( l' F$ C
CString theString( "This is a test" );
( I* q% K! S1 h' f  sLPTSTR lpsz = new TCHAR[theString.GetLength()+1];
* a! \+ d3 ]4 p' U/ J2 P* s1 C_tcscpy(lpsz, theString);
! Q" t8 D% F9 Z: {; O$ X
( l( m; |9 y- B/ i/ X6 B  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。9 }% O+ k" F8 X9 B8 L
* |5 L1 ~) ]& y
  方法三,使用CString::GetBuffer。例如:. j! t' m+ w) ~* R
: c( X3 O% ^0 r3 [: u6 m
CString s(_T("This is a test "));) p- O( b0 r+ v
LPTSTR p = s.GetBuffer();
! X! X# {  M( l6 }% S8 l// 在这里添加使用p的代码
8 O& q& q$ {. k6 {0 k* @/ Gif(p != NULL) *p = _T('\0');
1 h7 x6 q; G% V3 w7 Y& j. zs.ReleaseBuffer();
& n' Z# Z1 M( m% P: D) D// 使用完后及时释放,以便能使用其它的CString成员函数
, P/ G- I) ^8 o7 w* p/ v  j, E. n; G( V
  (3) BSTR转换成char*! d) I0 a# G/ Q: M$ e9 ?4 x
& Y" H2 h; J$ P; l
  方法一,使用ConvertBSTRToString。例如:
. {# w7 O% P* X* ]$ V! n
  F- J- u. c0 V8 g6 j* k#include
" c$ ^- V3 T! P3 F#pragma comment(lib, "comsupp.lib")
: b8 e7 Y4 k2 t$ `, L$ Sint _tmain(int argc, _TCHAR* argv[]){7 C% s1 x: M0 E) Z, [. P! V+ g
BSTR bstrText = ::SysAllocString(L"Test");1 B9 c9 L% f  g; i0 O1 ]
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
& X$ D1 s' }- I# X% x4 ]SysFreeString(bstrText); // 用完释放
1 x3 o% j, E9 c# ], C# A! f8 i4 tdelete[] lpszText2;* i, ^. N' v. n3 Q$ C- A) Z6 o
return 0;% }- a7 _3 B0 e: B4 a
}  
' }+ A  Z. P1 J" N% {# m/ Y' a
) w; e! n+ Z' I8 h# u  方法二,使用_bstr_t的赋值运算符重载。例如:
. Z0 G) f! n0 h  F" b. q- r/ y) N0 W+ p( h% K
_bstr_t b = bstrText;
& E( h; Y( _$ m# x$ F; Fchar* lpszText2 = b; 6 O! H6 l! L$ y
% o* V( H7 X3 e4 S$ \0 M
  (4) char*转换成BSTR$ i* q3 _  g' q% }: L; C. X% Y- z
0 X* g/ v- U; Y8 u1 E2 A
  方法一,使用SysAllocString等API函数。例如:4 ^) p4 v" }3 h) r# T1 i3 O
# R& ^. C, l/ g& _/ f- x0 w
BSTR bstrText = ::SysAllocString(L"Test");
3 r2 d  i9 `2 e' x- `BSTR bstrText = ::SysAllocStringLen(L"Test",4);" E, Q& J1 T3 l3 {" E- z
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
/ a% ~; i* ]5 G" U4 w7 r3 k- d; H. [% ^/ @# ^4 }
  方法二,使用COleVariant或_variant_t。例如:4 |7 @( L( W# Q7 K  `
; B( J- A: @0 }; }
//COleVariant strVar("This is a test");9 x" y6 [# R3 t. i  P/ V; d
_variant_t strVar("This is a test");
/ M9 v2 p& }( [! J5 N" ]BSTR bstrText = strVar.bstrVal;
' `! s# l* U5 H9 i7 g; {
  A+ y: q0 J, g. g3 ]5 S# L& f6 X+ c  方法三,使用_bstr_t,这是一种最简单的方法。例如:
: o1 R7 R+ G3 ]5 L( X- F
" K2 y! r! h) I+ }1 @6 \9 hBSTR bstrText = _bstr_t("This is a test"); " g* k# ^; [" z

/ ]7 Y  Y) O6 g2 d, T9 y$ o( [  方法四,使用CComBSTR。例如:% i1 k, ^& v* V+ i6 o# O2 @5 X
8 t% y  Z6 p8 u7 B( e; b
BSTR bstrText = CComBSTR("This is a test");
* e& i- v7 a7 Y0 H! @6 E  h# E" P! m% N
9 H5 j7 Q' t2 `  D( w  X  或
& D( H0 ]& U3 W& J% T% k) B) X; U+ o7 ~: I- e
CComBSTR bstr("This is a test");. g- s5 ?( }+ k4 R# h- J5 S
BSTR bstrText = bstr.m_str; 2 J0 u1 N8 S3 l! x' D
+ M% Y2 }! _, q$ a" }; _% k
  方法五,使用ConvertStringToBSTR。例如:" _- E/ E  e( p1 C

. L1 }& J; V& Achar* lpszText = "Test";
1 z+ z  o( Z5 Z- D- V" VBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
. u6 W3 x' F( C" S* f2 ]$ z* \& @! U$ h7 T' ^# m/ `/ R; w7 I
  (5) CString转换成BSTR
$ Q& K6 M& @$ [' ~4 ]! F! v4 Y  H( a8 ~" d
  通常是通过使用CStringT::AllocSysString来实现。例如:
+ m' ~  F5 |  l! W" r3 k% U$ p+ z3 A. ~2 v: J' D) l! ]
CString str("This is a test");- q; _7 q- \% d. Q) z
BSTR bstrText = str.AllocSysString();
% p. v! p- F3 f  V$ k) i0 O
. s2 A" f' B4 ^6 v. @5 ~4 HSysFreeString(bstrText); // 用完释放  
" ]2 s% e; M9 o+ x. d/ S# z+ J$ g. ^7 d
  c, a6 J8 y, x6 C/ O  (6) BSTR转换成CString
/ ^; }- Y8 g/ U  a
! m0 x! s3 o, L! V5 F, H: O+ e& Y7 I  一般可按下列方法进行:
  `) ]5 N! O: |- s# N
0 Y$ d0 m# O% Y0 v/ TBSTR bstrText = ::SysAllocString(L"Test");
1 A" D2 r9 V) i1 j0 w  Y* eCStringA str;
; k2 c3 k$ L* Q* a1 bstr.Empty();
* i" z4 {3 ~. s( Nstr = bstrText;  * n3 ]- o( S+ [! M" a* @2 {

# K& I5 t) Z, r: \4 G  或: U$ N; H/ W$ q" H. J4 [

+ q2 S4 q3 [2 f* z% Q; r0 dCStringA str(bstrText);
) C* W9 M! z9 B$ O+ r1 F- R- g! m4 F; b+ z) m+ y
  (7) ANSI、Unicode和宽字符之间的转换
6 L9 m/ s+ n% o; d: `1 t; y8 F/ j0 N9 D+ K$ j' i* R9 M
  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
4 y1 D" i* d9 g$ M) Z" K1 G" i0 {: U4 L4 M9 d$ y- Z4 J
  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
+ h2 C1 W8 H8 e1 ^1 f7 A# |, }5 G1 t  e9 F9 s0 i/ u$ _
TCHAR tstr[] = _T("this is a test");
+ B- Y# \. B  W' C* N2 {wchar_t wszStr[] = L"This is a test";
/ b7 v4 U3 T4 o; CString* str = S”This is a test”; 0 d1 {. P$ y. H3 l9 b; M5 W! ?

  I& S- d, `- F5 [  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:  b4 ~" q; M  w4 [+ _! y) Q, Z
( ]! E3 ~9 D) y- x, |
  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:$ F) N6 \( D( G* }  S
; l  I" i3 X( |$ ]
LPTSTR tstr= CA2TEX<16>("this is a test");
2 ]5 H' I( O" {* F6 _' KLPCTSTR tcstr= CA2CT("this is a test");6 r+ e$ b# [7 f! i& c
wchar_t wszStr[] = L"This is a test";& B( k! v; y$ _7 g
char* chstr = CW2A(wszStr);  7 t. k* J" d4 @$ `# u
" @  y% x: g! z1 J% F* [
  六、结语9 K+ D% E& ^7 @. `3 K. A3 q
" z5 ~( R. L2 M
  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2026-6-18 08:08 , Processed in 0.019173 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表