找回密码
 注册
查看: 4729|回复: 0

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky
& S9 g% N5 t6 |3 F, s1 W % v' m, q5 Y% e3 s# U) e
  p. {& M8 }9 T" z" B  b' ~. v
  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。3 m$ @# R% v1 J  @! V0 c
9 D, Z# \, q; O# v6 R0 Y% \6 v
  一、BSTR、LPSTR和LPWSTR) m1 N! x3 I" O2 [
" G$ {. c+ |9 X: h1 R, J
  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
' M+ N% M7 L# x3 l0 g0 _- U4 @) [4 z; L9 f% g/ b
  那么什么是BSTR、LPSTR以及LPWSTR呢?
9 C1 u4 z' |* d$ _5 {" x& l( i, r0 y' n5 I
  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。( s1 o6 N  @6 y6 S$ k3 q

* T# L: u: ]/ }  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
  f! h) W+ S0 A2 P
7 [6 V6 Z+ B/ z1 K  H  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
( g& Z8 X/ F& W6 q) w/ o1 j4 f7 G3 [  ]5 b$ Z
  一般地,还有下列类型定义:
  O  N( d' ]  r5 H+ P0 ]% y6 Z! e$ ^& U4 U6 G) }
#ifdef UNICODE ; K. H# ^& _, E
 typedef LPWSTR LPTSTR;
  a. A9 b/ r1 X( Y typedef LPCWSTR LPCTSTR; , s4 J) F; |( j) O3 ~# Y1 h1 e
#else
& B2 |3 W1 `) \1 g typedef LPSTR LPTSTR;
2 ^7 f1 Y* w9 `) K, \! i. W0 ~ typedef LPCSTR LPCTSTR;
2 U& a, v# }; }9 `" h5 X  b$ ]#endif  
2 F1 K5 `4 _5 s2 _0 H0 ]) x  o7 J) K# c
  二、CString、CStringA 和 CStringW! s/ D; i+ t/ \% ?8 N
" c; u( V/ u# 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应用程序中经常用到,这里不再重复。
0 {% B3 F7 E1 l) P4 Z6 d
3 B7 a+ f% Z2 L  三、VARIANT、COleVariant 和_variant_t
* {5 t' Q4 L+ Q$ o. o/ S: i2 y0 O0 i0 `# ]' D3 V
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
( q2 U! v0 s+ `  e) h/ k2 U; t& p& p9 V
struct tagVARIANT {3 S, S3 P  W' w' H
 VARTYPE vt;0 s! @, S$ C) q$ t
 union {& H3 V& o. l  K, {
  short iVal; // VT_I2.& R% G4 S/ j$ Y5 M2 D* D
  long lVal; // VT_I4.8 X2 W7 o! f2 o  G1 m- D
  float fltVal; // VT_R4.. I+ y5 `0 J5 q8 V( Y
  double dblVal; // VT_R8.2 h! H+ ]& N  y+ `0 L
  DATE date; // VT_DATE.
, `" k& }0 o( V$ P9 b  BSTR bstrVal; // VT_BSTR.
. y. i2 r  n1 v  …* j8 L# ~+ k5 Z: P2 n! H) x1 o
  short * piVal; // VT_BYREF|VT_I2.6 ~0 l% u( n# {, o- J* A7 h+ ^
  long * plVal; // VT_BYREF|VT_I4./ A( j# H/ r* d3 X
  float * pfltVal; // VT_BYREF|VT_R4.
* t& _: A- w/ }+ n( u& t2 v3 v% ~) N  double * pdblVal; // VT_BYREF|VT_R8.3 q* H" Q* S( ]  W* c
  DATE * pdate; // VT_BYREF|VT_DATE.1 Z1 w3 L, S) \% e4 f! ]
  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
4 |3 n# T8 g; F6 ^* N };1 Y8 l8 o% W3 m4 t6 ?
}; 3 L$ d$ N' j8 l
2 V$ f1 J; ?! L3 D: I+ t
  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
4 ^8 I: m7 G' j  ^* p3 m# R. [; Y
7 Y0 Z0 `) h+ P* nVARIANT va;
: L- K7 W# Y; Q+ ?* i6 i0 Y. E:: VariantInit(&va); // 初始化
+ q; c( i' s( H9 g" ~/ Q  Zint a = 2002;
# g5 r7 r' ]: ^2 o: @) [va.vt = VT_I4; // 指明long数据类型- }! Q" G  Z$ [
va.lVal = a; // 赋值
( A4 p# V: s6 \4 u
6 L& P" p" P4 Y2 w2 Z  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
% A2 b& t$ ]' l9 u/ N4 f4 P, i9 D. [/ f. G* z) t/ p8 O
  VariantInit —— 将变量初始化为VT_EMPTY;
0 [; C2 N+ Z4 h* g% E
+ `) a! \; r  r. A+ ^  VariantClear —— 消除并初始化VARIANT;, }2 s4 v* x5 d8 M  l
$ G# t3 n. W2 y5 v$ k3 \2 r3 B6 r
  VariantChangeType —— 改变VARIANT的类型;
+ p2 ]8 b, N* b  M
# \6 K) C1 A- q  s! s  a+ X- S  o0 F  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。5 p- @6 m" X+ ?0 c) L8 F$ j

- Q8 r5 [' T! H) T9 M. f  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:2 F9 }6 {2 s* K2 m9 B7 ~. q$ k

% H5 E. X, ]8 z: E) l* mCOleVariant v1("This is a test"); // 直接构造
/ f4 c0 H$ C! h& ^$ N/ t7 mCOleVariant v2 = "This is a test"; 0 s1 T$ Q+ e" o' w
// 结果是VT_BSTR类型,值为"This is a test"
* n6 V. f: [& G2 ^$ J+ OCOleVariant v3((long)2002);- g1 o+ ]. \/ u8 `- ^7 l
COleVariant v4 = (long)2002;/ i$ r$ n* \' K8 f) J, \
// 结果是VT_I4类型,值为2002
4 P7 O0 {) \0 H9 [0 S# i5 }% t2 G/ p$ M) F! R& r
  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:! J" o9 p9 p1 j" ]

" K4 V' v' x9 n9 S$ q  #include "comutil.h"
* g0 n+ l. q1 H& E" L4 r
6 ]- y+ h  V0 a$ F  #pragma comment( lib, "comsupp.lib" )
# M/ w7 k6 ^( |& _
  O8 S1 _+ U/ S( y  四、CComBSTR和_bstr_t$ K( Q, ^& v' h! ]

" i) W5 Y! V$ q3 Q; `2 S7 W$ E  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
6 C5 o' v5 ~8 h8 x/ t. h
( `+ g/ w8 k# c2 \1 I% `/ P5 @CComBSTR bstr1;
6 Q" ~7 E1 U9 U' n# c- wbstr1 = "Bye"; // 直接赋值( R* e" v4 {/ b1 }7 K
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符8 B8 G: J/ F) t; ]+ ^
CComBSTR bstr2(wcslen(str)); // 定义长度为5
5 i  U% Z9 T1 p0 O& ~8 P9 }wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
8 [3 e1 S. u) S3 TCComBSTR bstr3(5, OLESTR("Hello World")); 1 b, X/ i, v) q8 Z7 E9 s% S
CComBSTR bstr4(5, "Hello World");
1 g' K+ J" `: I/ D: x+ N! {$ b8 iCComBSTR bstr5(OLESTR("Hey there"));
6 A4 Q3 Y2 j8 `: c& iCComBSTR bstr6("Hey there");
9 \& z3 Z/ `. Y5 j. s8 f! s! GCComBSTR bstr7(bstr6); / b9 k$ e6 v+ ^; N/ v
// 构造时复制,内容为"Hey there" % u$ W4 w+ J) Q
6 U# l6 i) g% n. r
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。2 n/ x4 L5 Y  I, I+ d: z. O

: K0 @4 a2 s8 J4 z. y: x7 L7 u4 c; ?  五、BSTR、char*和CString转换' q; f7 n7 R7 @" q9 M6 F9 S$ A# g  C
% [" e2 L1 b; I% p! z
  (1) char*转换成CString% w, v, Q; W  U8 |

& G+ t& ^! ^& h  Y  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
' ?( N! j; q5 U1 l
$ \9 h+ }& Z0 Fchar chArray[] = "This is a test";( }' y% }9 `7 e% z8 ]1 s1 j
char * p = "This is a test"; 0 q# u6 j2 r4 O# h' I8 A# e

2 {: C) Q+ b( f" W& E; j& H8 t5 D  或5 i% q! ^" Z) N

, r* G' v* Z) K2 W7 O9 _" |LPSTR p = "This is a test";
# U  [4 E3 L* @  r% v% o8 v. w2 o
  或在已定义Unicode应的用程序中
7 o% f. Z  A8 f9 Z3 N& E
( g+ L& m; Q: {4 ^5 G; n9 q+ U, {TCHAR * p = _T("This is a test");
) V( |  `4 I* N3 ]& ?% u7 `
4 u! B% M: Y' h4 J  或- k* z5 V: E; D- w+ ]" t2 ^

: ^6 w% D0 _; |7 p' r7 d3 C- B1 q+ cLPTSTR p = _T("This is a test");
" G' ?$ h9 z- kCString theString = chArray;; [8 |2 f! D* G4 g5 O- X
theString.Format(_T("%s"), chArray);
# r. l, x* F; EtheString = p;
$ N: L5 j: J$ P, \* a" B- `: w/ ^% p. f! J6 J1 H; P
  (2) CString转换成char*2 E0 |; z- J  B$ `7 m# s' L
8 x7 f9 t9 ?6 ?& I
  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
& q/ J' R/ G3 _. ?) F5 Z; p' O3 i/ m+ O+ a6 T8 _: D3 ~
  方法一,使用强制转换。例如:' L  \' d+ G  t! u) L8 w) Y
5 P3 X$ P- ]. h9 F
CString theString( "This is a test" );
2 C4 i" Q& b, E7 DLPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  4 l) u  F& B$ l6 a- F

: ]3 f3 U4 Z& J* j9 q  C: T  方法二,使用strcpy。例如:
* Q, S3 v+ |( M6 T+ i: o& }% W! K) u$ k6 I/ N$ g
CString theString( "This is a test" );
+ z. r5 u% l, c5 I$ ~7 Z% X7 i7 hLPTSTR lpsz = new TCHAR[theString.GetLength()+1];
& ~/ w- l. M% u8 J# U1 U2 R0 j2 t_tcscpy(lpsz, theString); ; Q7 g% N" ^4 K/ P: l" u

$ K3 X: h, D% w$ O' L# h' e  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。4 [6 g: L* K/ k
( U" d1 i$ G$ I( w+ O6 o) n
  方法三,使用CString::GetBuffer。例如:$ }  D2 D- R! R$ p) |

  v" L4 \; a' s8 \+ a: `! M/ h6 wCString s(_T("This is a test "));
8 g  M" S& B$ i  e: {, ~LPTSTR p = s.GetBuffer();1 J6 a6 a  e# d7 {
// 在这里添加使用p的代码0 E6 X; Q  Y6 B* V2 Y
if(p != NULL) *p = _T('\0');
" J; Q, u2 n( Q" D$ ?1 s% l! \; Is.ReleaseBuffer(); 4 Q; c/ c2 a1 @. q0 n: G
// 使用完后及时释放,以便能使用其它的CString成员函数 6 ^7 V3 G+ U4 k* l
2 u7 J, B; R8 q4 Z. U% C% T5 x
  (3) BSTR转换成char*9 K# ^4 m7 ?: Y, i: w& j
7 E; o9 X7 ^8 T& @7 r- s
  方法一,使用ConvertBSTRToString。例如:; X& |$ m2 t7 {2 B, V

& d  }8 q0 [5 y; G, ~; m8 A#include # U. {& k+ }6 D8 m
#pragma comment(lib, "comsupp.lib")6 V& s& t0 V' @2 W. ~) H* j
int _tmain(int argc, _TCHAR* argv[]){
3 ?2 `' h: z) c7 s8 HBSTR bstrText = ::SysAllocString(L"Test");1 E' \7 P4 `: R- X
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);0 W$ n. }; U2 f. ~
SysFreeString(bstrText); // 用完释放
- k9 d) P: s0 v' `/ U. w% mdelete[] lpszText2;  R3 C; @* ?& s, m. W# ~
return 0;
6 Z+ z9 \0 z3 n9 E/ W% q: Z}  5 N" O7 p, h6 q. k
% m, L9 e6 b, e$ L8 ^# m, f4 F
  方法二,使用_bstr_t的赋值运算符重载。例如:
7 `4 P1 P4 }/ C7 l
0 e* F/ i0 t- i8 W/ @+ t  A4 B_bstr_t b = bstrText;
/ _  _9 M1 e/ H8 Rchar* lpszText2 = b;
' N6 J; S' Q/ G
. j1 Y- `2 G  S' E8 s& ]  (4) char*转换成BSTR8 Z( f0 v& a# h  B1 k

# [: e. N( L1 g8 [9 a  方法一,使用SysAllocString等API函数。例如:5 ^5 L7 b. ]' t/ o* ]
6 g# m# [' v& v4 k
BSTR bstrText = ::SysAllocString(L"Test");4 }7 h5 y  L; ^/ }2 s3 i
BSTR bstrText = ::SysAllocStringLen(L"Test",4);
+ R- Y5 s( l. SBSTR bstrText = ::SysAllocStringByteLen("Test",4); * g( y* D; @/ \2 C# y

: K4 `9 i8 g2 T* f8 q  方法二,使用COleVariant或_variant_t。例如:
2 m) P" `% F% p8 ~/ m' Y% b
9 O- B5 ^( l- g//COleVariant strVar("This is a test");
: J9 Q5 T! O8 J! Y9 p" l_variant_t strVar("This is a test");
' `% z# D" E, t% J- ]7 O* P! n7 [3 lBSTR bstrText = strVar.bstrVal; % n2 S1 A; T& X/ o& d( `
5 x* Y6 M4 ~; a  p( X2 ]
  方法三,使用_bstr_t,这是一种最简单的方法。例如:
0 P' A+ x5 A* N
* F5 P5 k1 t. Y, j+ xBSTR bstrText = _bstr_t("This is a test");
% x* ^' Z5 m: Y2 F" J' A9 I: q- f$ U& U
  方法四,使用CComBSTR。例如:, v' g" S7 `, ], ]: U# U

! C( c, K6 ?9 P0 xBSTR bstrText = CComBSTR("This is a test"); + k0 @2 C) p! Y! L  h/ Y- d
% `& c9 p6 P1 j
  或
( h6 V1 U* z0 |! z6 K  r( b6 d/ }  i! w  g7 A
CComBSTR bstr("This is a test");
) b- w) Y6 `3 }. W5 o: a( o# ZBSTR bstrText = bstr.m_str; ! h' K( m( |" V
" e! F0 K0 \! g* l" J7 I; I* d
  方法五,使用ConvertStringToBSTR。例如:) w0 T1 ^7 S* L8 G: J, Y
! s! @, p  T9 Z* r$ d# a
char* lpszText = "Test";4 ^* ]: A! f/ m( n: x
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
" t2 d6 C3 N# Z- L' I% A; u2 a4 X) d- C) S) j0 s1 D
  (5) CString转换成BSTR
' P7 s1 `0 M4 I
0 m/ I! n2 b. t  通常是通过使用CStringT::AllocSysString来实现。例如:
7 A  z( k6 z2 J% J7 ~) ]$ D% \
CString str("This is a test");, b) [( D/ R0 x0 c& K1 f) J$ I0 K
BSTR bstrText = str.AllocSysString();
# H" K% c; z) B/ X0 D…! i# M; M! s, d6 e
SysFreeString(bstrText); // 用完释放  
+ \- {, ~% o* J- \2 t! f# N+ z* Q( n4 Z& t( y
  (6) BSTR转换成CString
; \4 e4 ~, G5 Y: i( n( X! U
1 s" I' u1 ^' ]. }, j9 {& d  一般可按下列方法进行:
4 u5 T' Q% r% S% ?7 X. [8 k9 H2 e, M  B# _( H3 b6 ?
BSTR bstrText = ::SysAllocString(L"Test");+ {+ y6 S5 K9 \* k3 r
CStringA str;1 v6 k0 ^. l" R  _- W! J4 R
str.Empty();
+ L, M: o% V5 N) D/ estr = bstrText;  
: L0 ~  K) w( ?3 f3 N" k3 w/ t6 B8 e5 v8 W1 _
  或
2 o) {2 I/ \4 b/ H8 J0 ?: _- G. S: I5 n: {. ~
CStringA str(bstrText);
' w! a% Z$ [" U5 _" }+ u+ }0 Q( c
  (7) ANSI、Unicode和宽字符之间的转换. z7 z) G, M. _' G3 K* o% D2 Q

( ?) ]5 ^7 D, ~, S2 ?0 X  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。' c$ _+ v1 U& T* m- ~# f' u

5 i' y2 w" E( J& C  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:' O" X# {) b/ H# x

& N0 H( U& o3 s( z/ f; Q3 W& @' x/ rTCHAR tstr[] = _T("this is a test");" \5 G+ t& [; V! }0 `
wchar_t wszStr[] = L"This is a test";! N% t* l" L  q  v3 {* i
String* str = S”This is a test”;
% r. ?  A; U* ]* o' e( m3 b
9 g9 M3 p- T6 a0 i  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
  r4 p$ e' J9 d0 n6 |- S
& h! S# _) N" D% z% g  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:+ b) b1 o& Y) w0 D: F

  w) {; ?  Z% o* t4 G/ d. MLPTSTR tstr= CA2TEX<16>("this is a test");
5 |2 t$ O& g0 b( m% @LPCTSTR tcstr= CA2CT("this is a test");5 y+ X# |6 o, d  t' l; q: t
wchar_t wszStr[] = L"This is a test";
  H9 J  _! }- G6 M2 l! Fchar* chstr = CW2A(wszStr);  
6 H& M; O+ k3 k
7 m7 n" w+ U( F4 e9 y5 R/ K  六、结语
$ o0 b; d% N/ o* d! ~
0 _" S4 v5 B0 B" G. D6 C6 T% ]  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-10-1 22:01 , Processed in 0.017759 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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