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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky4 g, v5 A+ p" g/ D% W
) u  y5 _2 {) A' P8 C) b. H7 ~, P

5 H3 {1 A6 ]2 r- d  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。" Q: T3 ~4 q& b" T- j

7 x3 z+ w) W- C" @" f( c  O8 F6 r) ?  一、BSTR、LPSTR和LPWSTR
5 K* w$ ~/ R+ f! U+ q
5 u0 d! I! W; n  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
7 e: O* x3 u: b- w
( @  y' Y* z  A7 W6 f3 o  那么什么是BSTR、LPSTR以及LPWSTR呢?6 }3 Q) x+ T) K  H5 N0 T9 O

: ^3 i6 ]) W( F+ w  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。. e  r: I: N$ ?1 j! y! r+ {
; ~8 C# T! z2 Y
  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。3 H7 o' X( Q8 m9 h4 W
, ~7 g" R- r3 ]8 E5 F1 s+ [
  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
1 O4 p: X- c0 R1 B7 s3 r1 K& o+ ^1 s% M+ ?
  一般地,还有下列类型定义:/ ?3 I+ k% P* ^
! P' _* S+ @) K; u' \
#ifdef UNICODE 7 d+ V* y9 m, n; }+ _' B
 typedef LPWSTR LPTSTR;
9 d! B* d% n( h- E5 j3 u% Z1 F typedef LPCWSTR LPCTSTR;
& J1 I$ }/ W  _2 ^# A& E#else 0 ]& Y' T+ H" {6 ~
 typedef LPSTR LPTSTR; ' M/ P# N6 t5 L: @* _$ o+ [
 typedef LPCSTR LPCTSTR; 6 A8 M. d  L$ q# ~
#endif  
! P: x, k, a% ]+ d! j5 g- @  L5 B# L5 r* P$ s
  二、CString、CStringA 和 CStringW5 \% n& j# |9 V6 k8 \/ I
  p7 n) [7 z5 h$ A
  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应用程序中经常用到,这里不再重复。
: l/ {2 l" M4 G1 h
/ w! O, e/ H/ Q  三、VARIANT、COleVariant 和_variant_t
' V" |3 ~0 n. `' F0 Q5 F1 x% C* }/ l3 v. I
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:) [* n6 g- `$ e: X

) a6 t. X! i) Zstruct tagVARIANT {
0 B" g9 g$ ?' D; A$ j8 q VARTYPE vt;- K4 X- B2 P3 a
 union {/ ^; ?1 N5 z% |! z4 L! O' W4 c' T
  short iVal; // VT_I2.7 ~& t0 z9 H1 E
  long lVal; // VT_I4.
8 g: B- y8 K, }) O: S  float fltVal; // VT_R4.& H# a9 `5 `! L" w3 C
  double dblVal; // VT_R8.  `5 G5 w& G- v3 [
  DATE date; // VT_DATE.1 R$ r( V+ `: o( G4 _
  BSTR bstrVal; // VT_BSTR.) S& P. t! G) r( D) Q0 t" o/ j- E
  …
5 L/ x* I9 G+ w# `  short * piVal; // VT_BYREF|VT_I2.. {; E- t& Z. p+ M9 h% `+ `
  long * plVal; // VT_BYREF|VT_I4.+ \6 r- x6 v" c1 s7 E7 R; o) R3 E6 ?
  float * pfltVal; // VT_BYREF|VT_R4.1 {+ F* M; Z! ]* t) f: ~4 A
  double * pdblVal; // VT_BYREF|VT_R8.
+ V1 w2 P5 y% T" R& R5 k( [  I  DATE * pdate; // VT_BYREF|VT_DATE.$ k4 Z1 L& r# v* i  `. y
  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
1 O# s' i& Y1 d+ t };
2 B; P5 P( @( @  r: C& z& ^};
# X. Y. L& D9 X& r6 b0 A( D$ D2 j! M* S* M! {3 @7 k
  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:7 m" c7 ]3 L# `0 T* w5 h2 O( P

- h  w  {& e8 R  H/ j, W6 a3 H, q' _* fVARIANT va;
; N  Y7 C2 d& X! y:: VariantInit(&va); // 初始化
8 i3 f& o  {; wint a = 2002;
2 b! b/ \+ q$ e( i/ l6 C! k# ~va.vt = VT_I4; // 指明long数据类型
4 g6 M+ Z; Z0 e. W1 \7 G$ C1 Xva.lVal = a; // 赋值
5 ]' ]+ x3 t% W8 v) a( K- F9 S, y1 O- ?$ V
  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
8 I* d, }; E1 Z( w4 \5 h
5 m8 [! f' }2 Z7 H5 l  VariantInit —— 将变量初始化为VT_EMPTY;, m  ^, t$ U( F. B7 K

4 _8 \' e8 ~% `+ D' d  VariantClear —— 消除并初始化VARIANT;6 I1 E5 W3 Z( U6 T- U

% o$ g; z. V5 ~4 w  VariantChangeType —— 改变VARIANT的类型;. s+ n$ m2 _1 b4 h$ t; v% M
4 b5 F  g: V; b$ J
  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
' M/ m  H! e6 V( X+ b: g( _0 y; B5 o0 K+ z+ n* E, g
  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
( T0 ^7 P5 f3 t& x/ j8 t9 z3 I! r# J, X. T/ w4 u* V
COleVariant v1("This is a test"); // 直接构造' a9 G6 j6 n6 \8 R  v
COleVariant v2 = "This is a test"; 8 o- i: L! a9 m+ S, T  P
// 结果是VT_BSTR类型,值为"This is a test"" U3 j6 o: ]# [
COleVariant v3((long)2002);# E5 l. ~* i' g- [: G& z3 O
COleVariant v4 = (long)2002;1 ^# q! B) V$ v) e
// 结果是VT_I4类型,值为2002
! P" B6 l- B2 a4 w) i1 E! R% D- r& X4 O
  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:
/ r8 W& ?1 s( U) E, M
  `5 P; {1 F1 H; y) r  #include "comutil.h"
8 C# `" h  U# i7 I# I) H
3 i8 a% F5 f/ R0 h  #pragma comment( lib, "comsupp.lib" )
9 r" H# l' k; Q/ m" Z, g5 ?" L5 V: V( P8 I# q  ~9 f4 m
  四、CComBSTR和_bstr_t
  D' Z. }  u1 H, V2 F  \6 a% a7 Y! l
  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
) B- p6 a# O  q6 G3 E; o( B
( g/ y$ V7 f4 P8 t9 UCComBSTR bstr1;
, l& \& V$ L, h8 r7 jbstr1 = "Bye"; // 直接赋值. [/ ~) Y4 G  e( c- A) _
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符2 C, u/ g, {! [+ G) J
CComBSTR bstr2(wcslen(str)); // 定义长度为5! ]( @' I/ x% J. ^# W1 `: J6 u
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
1 D1 m! n0 t3 ?* _* t; lCComBSTR bstr3(5, OLESTR("Hello World"));
0 R0 _. X, X  v1 O: l3 eCComBSTR bstr4(5, "Hello World");
+ t- R6 ?7 e4 o9 W8 P6 `CComBSTR bstr5(OLESTR("Hey there")); + s, ^; r% a- u8 O* I0 k6 {
CComBSTR bstr6("Hey there"); 8 K7 D, R! V0 g
CComBSTR bstr7(bstr6); $ W$ ~! w8 q2 m% W; s; N0 P0 F
// 构造时复制,内容为"Hey there" 1 m' v& s7 t$ f8 c; R3 ~

$ ~; T4 A( T+ b3 R  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
* n- f& p8 e+ z3 @1 s& I8 D! a! ]8 Z, p9 M/ K2 @! L2 \+ B
  五、BSTR、char*和CString转换& b3 e; x/ E! ]0 w- t
8 O8 ~: }  O- N. s. F
  (1) char*转换成CString
2 i9 o2 @3 c3 ]" }0 h. n! `2 d* J3 h& z" L$ X" p
  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
" ~7 Z+ W$ I" C! x8 \; E. p/ F% L/ J4 ]3 U
char chArray[] = "This is a test";( J! M4 {; }& Y4 j) B, |7 X
char * p = "This is a test";
7 I; n0 C- ~; }7 k  K3 b, |9 a( d
% n1 J9 c/ @  S! z7 J1 d  或! C! p, i% z3 r# Z% J& p' \) `) ?

$ K* h3 t+ N4 `* c* \LPSTR p = "This is a test";
: n) m) Q3 O& z, @) P' \  ?( c2 d; x) V+ K1 d
  或在已定义Unicode应的用程序中) Z3 K7 [7 F$ q3 _7 e- q$ d

3 T4 w2 l/ S# i" w( MTCHAR * p = _T("This is a test"); , |  n9 \  P& }' v2 V' b

* d( h6 Y, b+ _* }& c  或
; K0 q7 l7 z* C/ F4 g% a% M  J/ s; d# k3 u6 D* I9 t9 S* `
LPTSTR p = _T("This is a test");0 `: ~4 I+ o4 K, r3 ^! ?! C0 x
CString theString = chArray;
- Z" I) o+ Q4 e. b" XtheString.Format(_T("%s"), chArray);" y( [7 o: E, P9 d4 f( q3 J, {
theString = p; 7 a5 w/ t$ J' Y4 ]" k

" e& o9 Y. ?- [/ M, |  (2) CString转换成char*0 |( |0 ?! t9 l$ B; C9 P( [% F4 T

8 i7 z5 q* r) [+ s. p  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:+ Z1 _) q1 @( F

7 Q8 {' W: c, Q  方法一,使用强制转换。例如:- T- p1 @& c7 e

1 K, t( Y2 w, {) B7 UCString theString( "This is a test" );8 i1 A" E  }, F9 K, R
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  
& {0 t0 X( \. Q/ L% z$ m4 [# c& o: f9 x; K3 r* G: |; z0 F
  方法二,使用strcpy。例如:
+ ]. x$ l( |7 r: r0 F9 P% M  r- u) Z  _: l7 i+ w
CString theString( "This is a test" );! ]4 ?9 ?4 ^% b0 S1 Y
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];' k3 \/ m! O( `' `& {* r. n7 \
_tcscpy(lpsz, theString); ) }8 P5 T7 x( f" [" j8 W* A

* _, s5 h# G5 P2 V! q4 K: M( [' |( W  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。4 e1 L: B9 `% d9 Y: q: ]+ C, `
& V* u) X8 b2 G5 _
  方法三,使用CString::GetBuffer。例如:: S. h8 r. F5 l4 {. [9 S

' E4 R  G4 @4 W7 }0 zCString s(_T("This is a test "));
. e% l  P! [/ {LPTSTR p = s.GetBuffer();
# Y* f; H% p- `8 f. ~// 在这里添加使用p的代码
% Y. p, t; f) ~7 `) D! X# Wif(p != NULL) *p = _T('\0');0 \1 a' c. `; W& m- b7 F$ ~, {' O
s.ReleaseBuffer(); 5 O1 g6 j4 P8 X5 o% w, k0 o0 W
// 使用完后及时释放,以便能使用其它的CString成员函数
: x6 r" c& T. ]4 {" V1 C4 C7 ~% J' w, q
  (3) BSTR转换成char*
/ H0 o9 D4 U; v6 {  {  J$ _' _: ?( W" {) u
  方法一,使用ConvertBSTRToString。例如:
6 g  U' n6 D. u! g# Z+ q6 @
8 _% @7 U- n$ |7 Y* F' \#include
9 j* g4 ^. f6 s+ d$ f#pragma comment(lib, "comsupp.lib")) H' i- k' {: Z* v- V, F
int _tmain(int argc, _TCHAR* argv[]){
3 T! B0 {! e( Q0 t6 XBSTR bstrText = ::SysAllocString(L"Test");
$ i. j3 H, i% d! o) q. schar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
8 T1 b, E6 C! S$ F: ^) P* QSysFreeString(bstrText); // 用完释放) U& |( p1 S+ ?- l& g9 E4 Y- o: S2 I
delete[] lpszText2;
6 ~$ K- m$ P0 ~/ Xreturn 0;
" e, g  u& q% A1 _4 J}  
* K, {4 \% w0 r+ ^4 O
8 I8 |* `0 h+ @! j& F  方法二,使用_bstr_t的赋值运算符重载。例如:2 n& f1 n2 a0 c3 b3 y% y

. t: a# b; R- P6 n7 J_bstr_t b = bstrText;$ z# N: q; a$ M
char* lpszText2 = b;
1 u0 T$ b+ P% V/ a+ I% o
6 @6 j. W8 h, B2 t8 j3 M  (4) char*转换成BSTR+ b# Y2 ?& m. X' Y5 u+ I* H, f
& ~' O# Q& J) |8 T" G& y
  方法一,使用SysAllocString等API函数。例如:
$ O0 V' [9 S* N& I% J$ C8 [6 l% u! R& X4 h! |- A  Y
BSTR bstrText = ::SysAllocString(L"Test");
! C5 d# z& {0 W; P' n7 t* Z1 jBSTR bstrText = ::SysAllocStringLen(L"Test",4);
3 n  O; J4 G- [) ?BSTR bstrText = ::SysAllocStringByteLen("Test",4);
- v4 f8 h. F* P% i( e5 z- @/ t! H/ _9 N$ V; m$ L1 e$ E9 l0 r0 V7 n
  方法二,使用COleVariant或_variant_t。例如:
# D" e' d, ?% w5 o1 V, F
3 c7 j" K/ l2 V+ R( F//COleVariant strVar("This is a test");
2 H8 H1 q3 h7 C; k2 X' p_variant_t strVar("This is a test");# ^0 T! ~$ U& i* v* v9 l% v! P
BSTR bstrText = strVar.bstrVal;
0 Z8 x8 v! l: Y0 d- V+ F7 I- h% d- c
3 x  r' j4 ~* u" I, g' e, i( n5 y. S  方法三,使用_bstr_t,这是一种最简单的方法。例如:
3 L( Q0 a9 C% x8 t# x: \! A) _9 T; }3 V
BSTR bstrText = _bstr_t("This is a test"); 7 l2 L1 u& a8 L8 [( R2 J
+ n, p5 K  }/ z3 z" b  m
  方法四,使用CComBSTR。例如:
3 K. p- x. Y. K' C' l: l$ W  T2 k7 W* F+ J5 K- O, ~3 s. i
BSTR bstrText = CComBSTR("This is a test");
% w$ F4 X. m$ {3 ?) e
6 m4 Q& G5 p1 |$ Z  或
0 c  H: n$ J6 M4 e! ]/ b0 c: l5 k
CComBSTR bstr("This is a test");
3 ~) h! t- V; Q% QBSTR bstrText = bstr.m_str; . m" _' x, \9 Y# D6 T$ H2 U7 ]
9 ?+ x7 b9 Q( x/ e" _
  方法五,使用ConvertStringToBSTR。例如:  N3 X- a5 I4 N5 K0 U

6 A9 u6 b, U( Cchar* lpszText = "Test";. ^8 p& B0 }; }) ]. i9 W
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); 0 N: j3 r: c% }6 t) r
% [( i" z. O, f# S: m$ N0 R$ @! D
  (5) CString转换成BSTR# T  U% ^* h+ b  Z

1 r/ R) `& g- C7 ]) H2 l  通常是通过使用CStringT::AllocSysString来实现。例如:. v' n; s$ R  X" s% r8 `

$ t! L3 _( w$ `, f0 _CString str("This is a test");
! }% c8 t) y2 p* p* MBSTR bstrText = str.AllocSysString();* Q* Q" ^. s$ R& Y2 v- b

* C0 K0 z8 a7 T" {, O3 JSysFreeString(bstrText); // 用完释放  & v  E# J5 v: ~8 ~8 n1 u: a8 n4 }

8 m) C$ F3 J, c8 O, m  (6) BSTR转换成CString% L1 u1 k' J: I$ c9 a8 R

& C' f* a9 O& ?  一般可按下列方法进行:' e5 x( f# ^# a0 L
, B8 V" ]: }5 ]! v/ o1 d. r; E
BSTR bstrText = ::SysAllocString(L"Test");; {1 M1 z, o+ y( `6 Q4 h" A% m2 N
CStringA str;
  `5 f1 q' X- i5 j& Wstr.Empty();
2 h# ]0 Q  {: }- ?3 b+ |9 ]2 ?str = bstrText;  ; N) V7 s; D, h9 V) B% e) U
# T: m, |! A  f  R* J# E
  或
! O8 _" T' `* l6 l( G. F& ~, R7 Z& j4 _2 h- a$ a% \
CStringA str(bstrText); 0 c. w1 V1 R3 e5 u: T% @5 G
. }/ L2 r$ ]# h2 D4 H; g1 Y
  (7) ANSI、Unicode和宽字符之间的转换2 o0 u  w/ ]" Z, l* \& s0 D8 L

+ c' Q% F* S( q$ l% Y" P$ {8 ^# X  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。: J9 E7 \" Q2 z6 s- O2 l

( M/ N) ^6 L* f; s( g& Q0 Y8 n+ n" X/ n  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:, q3 E# u7 \: m8 E3 B( u
; ^# M! Y# H9 X# }* S5 W. _$ I
TCHAR tstr[] = _T("this is a test");- E3 I3 Q$ t* g" w! C
wchar_t wszStr[] = L"This is a test";) ^+ Z3 S2 f6 d& T& `" E) V
String* str = S”This is a test”;
: h2 s/ x# r# ]( |& [" ]1 u1 }$ ]" c- c1 X2 L  l
  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
; A9 I/ U' V" g2 c  G3 E( j4 S9 d7 v  Y/ t" g4 |4 h
  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:" Z+ r/ F' t4 x# S9 Z, q- y
$ [6 B# U% e5 W6 A6 `
LPTSTR tstr= CA2TEX<16>("this is a test");
) n  o$ s  Q. q. F! w/ ]LPCTSTR tcstr= CA2CT("this is a test");
4 [) S- I5 b0 g2 H/ awchar_t wszStr[] = L"This is a test";6 a. j% `% C; F9 [
char* chstr = CW2A(wszStr);  
8 Q7 L- d' ]/ P4 m- @9 ~& ?- B2 \' ?2 \1 y* I8 W
  六、结语
0 X3 N. t) l4 c  R* t+ t: }3 D, B+ k: L8 W7 m: b- r. L% P, \
  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-5-2 11:46 , Processed in 0.017822 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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