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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky
2 {; T+ I  I  v' J8 z 9 h, ?+ f- d# V5 ?

$ `* c' X! \8 x! j* ~: K8 N; M  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。6 }5 G) D/ x9 [
4 z& C7 U8 W5 \/ f
  一、BSTR、LPSTR和LPWSTR4 t; D: m: J* s* H. g& p
( S* Y2 `& F: h6 d/ C
  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
& o( ?- b& w' h6 J- q. Y
0 \) y3 o+ y" L5 c2 [+ ^  那么什么是BSTR、LPSTR以及LPWSTR呢?- v2 @& \2 |) G% b
4 ~# i$ x* u. w! {" g
  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。( K' r0 `& f6 V( J+ L0 q! z& J
2 s# l7 C  h$ O9 p9 H
  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。2 d( f  ^8 s8 G8 X% e2 J+ `; `5 J

3 u3 B) j: D6 u; |2 n" c1 l  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。5 R5 ]) b6 O! X- g
! L9 d% u8 }( U
  一般地,还有下列类型定义:" v4 Q& E: R1 l- `: `; F
- o6 Z( ]7 J6 n# z0 g
#ifdef UNICODE 9 E& X1 ?6 H5 C4 r6 Y3 h8 V0 f: q$ x
 typedef LPWSTR LPTSTR;7 t* ?8 \4 W3 D; v7 ^
 typedef LPCWSTR LPCTSTR;
2 {! d) \" q# K; u4 g#else
& i5 l2 O5 U5 I0 U0 \* a* _/ A2 Q% x typedef LPSTR LPTSTR;
. j6 l! W# ?- I3 t5 \ typedef LPCSTR LPCTSTR;
9 L& y6 j2 A1 v# K3 G3 ^#endif  ) m- ?. o% p& f( }

  w0 `, d: Q: G) q  n% t, {0 Q  二、CString、CStringA 和 CStringW* w* o! q5 L9 h7 L' o* S0 T
* w# |7 G1 {+ B
  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应用程序中经常用到,这里不再重复。
' Z& x2 z' N' E
8 p4 {# ^$ R2 J$ B+ v5 T7 E. Q  三、VARIANT、COleVariant 和_variant_t, \* d, z1 p3 \- B/ o% D7 Y5 a. y* O
! Z: F# q$ Z, D& C$ U8 R6 W
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
- d/ Z( b) C: l! \, J0 w4 w# b3 f- {) H4 ]8 {# G
struct tagVARIANT {5 R  C7 e" B! t
 VARTYPE vt;) g* d4 h7 y( V  W
 union {
: z* R( s: ~2 S  short iVal; // VT_I2.
' q2 N3 A6 B2 l. w- ]  long lVal; // VT_I4.
+ W6 i4 o+ S, J" f  float fltVal; // VT_R4.
4 n& X5 ?+ Q0 q* ?; m  double dblVal; // VT_R8.5 \; @9 e4 s6 G' p& @* Q4 N: m6 @: _
  DATE date; // VT_DATE.
  I5 a' B! |9 t/ [  BSTR bstrVal; // VT_BSTR.
8 h) w$ [/ e8 L* W6 {0 [( h  …
2 ], v. v! u/ Z0 O1 Z  short * piVal; // VT_BYREF|VT_I2.) m2 x- z/ {1 L7 z3 L
  long * plVal; // VT_BYREF|VT_I4.
4 z6 D  R% }3 z: ~  float * pfltVal; // VT_BYREF|VT_R4.- I+ Y& C3 ^1 R/ e' b" `
  double * pdblVal; // VT_BYREF|VT_R8.
. \2 M% L  M# [: v7 p  DATE * pdate; // VT_BYREF|VT_DATE.$ }/ N; S  V) O: v& F
  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.; i( b6 e: u* {( m% h$ _
 };/ e( A5 ^  N6 Y0 N- Y7 p
};
: a2 @; ^  V5 Z. k) [
3 g, Q/ S  L/ _) l# }3 a0 C  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:) E0 y6 }$ z- z! s0 ^4 g

9 z; ?7 D: }# D$ N  VVARIANT va;  y  G+ v$ [# p( Q7 ^( r
:: VariantInit(&va); // 初始化
5 L3 C  t5 z; v4 T* O4 Z" Lint a = 2002;
7 a4 _% ?+ C" Kva.vt = VT_I4; // 指明long数据类型
" Y: ?! A0 O- \. }va.lVal = a; // 赋值
" v6 \: F8 ^, D2 g2 ]- K' r, `( X% ?1 }( ~# w2 t. t6 g0 A, T
  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
9 e3 B& Q: }4 w9 Z
  t2 z6 k8 b* q1 n  VariantInit —— 将变量初始化为VT_EMPTY;
4 h2 B' K. C9 }5 U- V, G$ k) g; i
  VariantClear —— 消除并初始化VARIANT;: }+ [6 Q0 s. a. `: p3 I7 l
# n7 G: X' o0 T4 [0 A
  VariantChangeType —— 改变VARIANT的类型;
4 K$ p% F4 @- R$ a# ^; \. A
  P( R0 H7 `( ?5 s  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
0 l' |% V: V% R. w1 z* i1 w
0 h" J/ \% J& c* ]9 L  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:: {4 \& v0 x' V& N7 }
6 G" E- m: e7 `9 M% }' d
COleVariant v1("This is a test"); // 直接构造% H1 V. t" \4 U& G# e0 N
COleVariant v2 = "This is a test";
9 F: U* H1 H9 p; G) a5 C// 结果是VT_BSTR类型,值为"This is a test"
; p; u- d8 m* p6 r# @COleVariant v3((long)2002);, F) w( y- \5 a9 ]3 I* t0 l
COleVariant v4 = (long)2002;" @# c2 }- m3 o: x# p' [
// 结果是VT_I4类型,值为2002
, v8 \# ?9 M4 L7 Q! ^
  o3 j# X$ H0 \$ W+ t5 e8 B5 S! |) B5 R  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:: t" b$ B! q) i0 q; b8 r4 j& e
, v/ C6 w- m  V- v, `
  #include "comutil.h"8 j$ N$ m. T  a
$ ^1 \$ m2 Z% L1 f4 V
  #pragma comment( lib, "comsupp.lib" )
- q# C8 i1 I/ j; P( A: i  E, ~1 M; S7 J( n9 d) W9 B: O
  四、CComBSTR和_bstr_t  q4 s2 E5 N/ p+ P
, g6 z7 i  o( y$ O) {- F( j( e: {
  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:/ l# [) C8 A5 i

3 p& M0 z3 S3 W1 C5 aCComBSTR bstr1; ) n/ ]# ]( M+ h
bstr1 = "Bye"; // 直接赋值. r# _  k8 s: v% n
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符
0 p' u$ }$ g* R' z( {  ~" sCComBSTR bstr2(wcslen(str)); // 定义长度为5
  @5 h: O! e/ p& K$ R( m( twcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中. V3 ]& [7 X7 w3 s: S+ Y
CComBSTR bstr3(5, OLESTR("Hello World")); 2 F! y6 ]3 d. X+ h5 P; C) u6 ?' X
CComBSTR bstr4(5, "Hello World"); + f' J! M' {" T4 X) T
CComBSTR bstr5(OLESTR("Hey there"));
7 a1 n5 f6 P+ rCComBSTR bstr6("Hey there");
; r# c" Y4 G) ]7 dCComBSTR bstr7(bstr6);
3 d; I6 V; v$ k: x, l6 N. P// 构造时复制,内容为"Hey there" 7 M# P9 j/ D) v1 t6 Y
, Z! d1 ]5 A( u3 Q6 E+ c
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。$ b$ O2 {% w, ~
9 E# R$ U3 E/ h
  五、BSTR、char*和CString转换
5 y& w6 A* p: ~( r+ f0 j0 g/ d2 I# x- d% I$ s% C* X
  (1) char*转换成CString
& \4 r, ?5 ~  u  R6 L. }4 \3 z) ~. b5 q
  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
+ E; C9 c4 T: ?; {" ~7 u6 R. L! @- G
char chArray[] = "This is a test";
& g+ g3 j( c1 \! ~char * p = "This is a test";
/ {* A! o7 x+ `- _& d/ n# D/ _# I9 ^0 _" W
  或5 l. _3 G$ c8 v
; J, k2 M4 {* Z# b
LPSTR p = "This is a test";
! M3 ?3 q7 S% v. D$ X7 A9 S8 U+ p& a7 }  d
  或在已定义Unicode应的用程序中6 f) s: s+ [: A: y
4 d. Y3 {9 T) R7 H, |
TCHAR * p = _T("This is a test");
  z- o( t! M( V* |9 t# g8 u
: D' Q  q; I) `5 A) {& c% S  或. M5 x9 t0 g+ {" E3 {3 h2 S. s% |
8 g$ s  L3 S* f) M- X
LPTSTR p = _T("This is a test");+ v% J, t1 P% `
CString theString = chArray;
1 q2 D- n' B0 UtheString.Format(_T("%s"), chArray);( p3 }6 t" s- z
theString = p; % h: W6 V4 W  ]8 C/ u. c' c
, B  f7 X# _/ H: K! V+ _
  (2) CString转换成char*
2 L# E6 O! h" l5 Q/ @; w6 x+ h2 B, v+ u5 Q/ `
  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
  M9 N/ K2 o! U, s3 h3 [0 w8 ~, S0 ?; y! r3 r# j. y7 |* o5 h( w) J
  方法一,使用强制转换。例如:
9 O- p6 j  m9 p( `- \  V" S/ B! `" X: p5 X
CString theString( "This is a test" );( g6 n3 w, H) j" K
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  ( L# t% _+ l5 P2 I
  c7 `  Q' R1 }/ N
  方法二,使用strcpy。例如:
% b3 A; ]% B4 ]# z# n
( J1 _7 ^$ D2 t& x5 |CString theString( "This is a test" );4 N7 V7 t9 }: c8 f0 w/ N+ o
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];, w5 z4 E/ k" C/ H: ^; c$ w
_tcscpy(lpsz, theString);
/ a# }2 ~9 B6 }2 ^  x3 g* _
3 B7 L. i9 S, |3 B% C, U  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。. _3 V' Q: ?. j* u! N

9 e9 Q* t2 ]5 n- B; S. z# k  方法三,使用CString::GetBuffer。例如:
7 P5 Y+ {; C4 L8 C3 H+ [3 T1 t9 W+ r+ O2 e3 w, @, x
CString s(_T("This is a test "));
! ]5 s9 P; V6 L1 `  rLPTSTR p = s.GetBuffer();
3 V8 N% G) i8 \" W% X// 在这里添加使用p的代码
6 k2 ?- M$ ~$ Mif(p != NULL) *p = _T('\0');, N$ U, [: N9 d' b  k6 W# Z7 b
s.ReleaseBuffer(); ( A4 y  a" p, E  P5 q7 [( `
// 使用完后及时释放,以便能使用其它的CString成员函数 % a' _( r8 N0 a( E

, u5 k0 p, `, c, |- K  (3) BSTR转换成char*1 N2 b* N7 F5 G4 a. a9 R+ Y4 d

( N1 {4 a& j  L) l# P8 j  方法一,使用ConvertBSTRToString。例如:. N3 y9 v# p4 Z/ r1 B' r/ ^$ t

+ }/ o4 {+ A; h- |6 Q- S#include
! A! m8 [' A& m+ G#pragma comment(lib, "comsupp.lib")4 e( x& l1 u! O6 \* i" C8 V
int _tmain(int argc, _TCHAR* argv[]){' R. P' O5 b3 n/ k, M/ v/ T$ ^
BSTR bstrText = ::SysAllocString(L"Test");
4 X3 }# f: j1 Uchar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
4 y/ P0 I' K' ~8 BSysFreeString(bstrText); // 用完释放
1 A7 {1 y$ }1 F1 W; e+ F( H6 Cdelete[] lpszText2;4 m3 A) v6 ]/ ^" ~
return 0;! M- q  G9 V& ?* Z8 p/ a
}  : U8 Q" X; g9 d

7 R+ b0 b: E$ @  }7 z  方法二,使用_bstr_t的赋值运算符重载。例如:
  P, T7 \6 G8 D/ h5 u+ m! ]7 R* a' \! W8 R( o  n( Q
_bstr_t b = bstrText;
) J# |# O# ?+ ~char* lpszText2 = b;
+ ~7 e( [' f/ w& E9 G5 Z
3 V  I" ?0 n+ Y, `8 o6 `8 [  (4) char*转换成BSTR  z; M5 y$ a9 H% W
8 N9 }6 |9 l  z! A1 d' ]; M; U# O6 j: e: ~
  方法一,使用SysAllocString等API函数。例如:: y) {0 P/ Z2 |6 `6 Q$ l
4 \9 R6 j4 V& h2 t
BSTR bstrText = ::SysAllocString(L"Test");
! i$ S7 p0 R. ]7 v- x: @+ k. uBSTR bstrText = ::SysAllocStringLen(L"Test",4);2 n4 S' Q( y! g4 b8 l
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
1 J4 i8 i, E) X* F7 |6 Z. Q4 k/ u, k7 S& p; D5 h1 i1 k3 S# f
  方法二,使用COleVariant或_variant_t。例如:; w, c1 ^- s# z) u' I) E
2 n* Y& f1 e, ?+ u2 t
//COleVariant strVar("This is a test");& r2 C3 y/ R9 u/ A0 s" M
_variant_t strVar("This is a test");* W# E2 y& Z, L, N5 t# q# u# J' J' \
BSTR bstrText = strVar.bstrVal;
9 L6 x2 {: n+ }# s  \, t7 s7 M0 O4 E2 N& R. Y, n6 z  \$ v
  方法三,使用_bstr_t,这是一种最简单的方法。例如:
, Z3 u+ y& X; n% r
" D) @2 s  K+ f& uBSTR bstrText = _bstr_t("This is a test");
% m! K3 ~4 l" F" S. r) g9 }* y* U+ K
) }& \* e: A% `7 [2 K2 S7 r; [  方法四,使用CComBSTR。例如:6 r. u" G% r$ w# b
" L# E5 J7 Q6 n% Q$ M
BSTR bstrText = CComBSTR("This is a test"); + y5 K9 l3 X) J3 l* @: w
5 I# C6 ~6 D+ [: ?
  或, M- M- e  c1 Z2 n
; K6 B: X3 y6 F9 D2 d  c+ v
CComBSTR bstr("This is a test");+ w9 h2 m8 o8 \3 q
BSTR bstrText = bstr.m_str; 1 S* v1 m! \0 n  h- O

4 t( X9 k$ N6 H  方法五,使用ConvertStringToBSTR。例如:
$ X' v8 q8 ~  z$ J  i  [! B$ H
. h+ e( U8 m8 v- z, \4 v+ z. ^6 C  A0 F% pchar* lpszText = "Test";% K- {/ x. L+ N& G! b- A
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); : p2 N& w# u4 V# g. c
+ \( Q! q8 N! f+ z, C/ {) p1 D
  (5) CString转换成BSTR+ }/ ~  |, M0 J) ^6 F) m
$ \" a7 ?- H9 x4 }
  通常是通过使用CStringT::AllocSysString来实现。例如:
7 n& m6 ^$ m3 E, W9 J/ q2 _  H+ Z" ~! ?4 g: I6 H
CString str("This is a test");+ l4 ]! e. u; o" r
BSTR bstrText = str.AllocSysString();
- R5 u3 y" k  I) M+ j* c4 h3 T
# M0 z! H# b! c. xSysFreeString(bstrText); // 用完释放  
0 Y0 {& j2 Y: p& P
0 @# g  K# p: D7 E& ?! B$ g  (6) BSTR转换成CString( N$ x# E8 V6 m9 _  }& M8 S
. [6 _# S3 `$ y) z5 h
  一般可按下列方法进行:2 q0 N4 v8 t) ^# l& O9 v/ S

1 I9 k7 a5 Z# W. }8 XBSTR bstrText = ::SysAllocString(L"Test");& {( A* c5 F6 P& S2 ?+ l' y
CStringA str;8 T8 v1 {7 a/ N& z3 y. I
str.Empty();& {( l4 t  D" i; H6 `7 {$ e
str = bstrText;  . j) o& z3 e7 x3 a8 ]. j
. ^0 S% A4 [0 M+ _
  或6 M( f% ^+ V3 r8 D% s8 ~. _

+ Q3 B6 y- ~- ]! i0 s  Y1 a& c1 S( YCStringA str(bstrText); ! @6 B# \* h+ x
0 f5 Y  |1 r2 f# e2 j; V: Y
  (7) ANSI、Unicode和宽字符之间的转换
/ o8 M) ~& z* h/ }  }
4 X: U" p2 g  c2 P  h/ [9 B  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。5 Y  M" ]. V# [  ^0 Y3 [

) W! G0 ^5 S( V, B% F* G0 b  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:0 I& t9 x( J1 {& F+ R, I1 \

; [# l4 z7 y, f# u7 D1 k! D2 zTCHAR tstr[] = _T("this is a test");
, i9 H" L% ?0 ^. k) g$ |; fwchar_t wszStr[] = L"This is a test";
: g" H- F- D2 @9 BString* str = S”This is a test”; / J( }% K6 F3 e' R$ @5 d3 ~$ h6 R

+ O1 s8 b/ l! f* W) ?( z& B  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
; a; h6 M( d! j1 P* `$ N/ }+ w- L
: B1 L3 d- E# c  Q! z: N3 K: r  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
- w0 Y3 C  c) k7 g! i: O, E/ ?0 N9 d" J
LPTSTR tstr= CA2TEX<16>("this is a test");
* p) f+ }9 V0 Q( m  N; P2 uLPCTSTR tcstr= CA2CT("this is a test");: B- Y8 u( i: Q7 B6 E- K7 M
wchar_t wszStr[] = L"This is a test";
' k# a0 ]7 l5 ?: h4 Q" u$ ?char* chstr = CW2A(wszStr);  
' }% y1 N9 w' j; Q, N9 d; V9 D4 \
4 F. U- Z- R& d9 L8 `0 w  六、结语5 v7 z" A1 v4 O% \1 C# D& V* e9 \# U% k* n

7 h& \0 M( z2 t  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-8-9 04:53 , Processed in 0.034464 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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