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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky
  T2 D1 \1 t; ~6 m , H# ]; |8 R1 d& V  |2 }
1 m7 \- c$ a8 C, ~( O5 ?- N
  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。- ?7 ~: l# o) s

$ k+ I/ L# v" F2 F$ \  一、BSTR、LPSTR和LPWSTR- V9 i2 C2 ^4 B& t, Q: O: L
. n) }1 i( M6 C# G+ `% T
  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。8 V6 q5 z0 y; v9 M
; N; c# F0 z( W, i1 K
  那么什么是BSTR、LPSTR以及LPWSTR呢?, h6 N! u  Y8 n2 d7 h

1 @) O8 |" L/ x) C* x: s- b, _" w  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
: Q5 [4 ]+ H( j1 N8 H. `) p0 |6 J( c& T) O( p  K2 E5 b
  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。1 T4 o& e$ _, g, _) _4 v

! @- u2 h) A0 U4 g4 M  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。" R4 U# @! q+ r* T2 C8 \3 O

5 p( t% H. v) I; z! A  一般地,还有下列类型定义:
5 s" n/ M6 _; b$ m5 i0 Y% q, a+ i9 [
#ifdef UNICODE 8 O! m) m0 N6 W( h3 f) h& u9 ?
 typedef LPWSTR LPTSTR;# G5 V2 U" P) k+ e/ @
 typedef LPCWSTR LPCTSTR;
7 x* w% k" u; l3 U3 I#else , ^0 M: x8 }: ~# r. p1 h
 typedef LPSTR LPTSTR; 1 U6 q( C  G. q5 v4 z
 typedef LPCSTR LPCTSTR;
4 v  F$ H% \; h' {#endif  / A0 e8 B( a# O3 @* ~! O. R
6 X+ S5 B! t8 Y2 J6 Y' }4 G1 Y- s
  二、CString、CStringA 和 CStringW% w% T$ i% q7 f" h' Z

3 u% M. p% n: o9 q1 u2 L2 ?' G  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应用程序中经常用到,这里不再重复。
7 n% _. r9 l4 ^: t6 h( m( H# e1 X- i1 b: J( v0 X1 k/ r
  三、VARIANT、COleVariant 和_variant_t% u6 w1 g: T1 P2 P
) e" }+ z& \. j
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:2 \% `% @/ j9 W! `$ p
# r! @* C' {2 T$ e
struct tagVARIANT {
! Y3 b5 [* `; C1 H VARTYPE vt;6 ^  |: `% c2 f8 P' Z- j; R2 u
 union {
9 P* }) C! i& m: \  short iVal; // VT_I2.
) D2 R* o4 g' w4 S4 v  A7 J% {  long lVal; // VT_I4.
8 ?1 \2 G* P* a4 Z  float fltVal; // VT_R4.7 F" E+ ^& u4 X  h0 _3 D2 L3 M
  double dblVal; // VT_R8.5 Z* c. l- W( c  W
  DATE date; // VT_DATE.4 }# }9 u  ^+ B; U  I, W( E; |4 F+ A
  BSTR bstrVal; // VT_BSTR.2 y; ~* I" O$ l; I5 X. B* Q
  …
% [9 O+ C4 h3 ^, }1 _( T  short * piVal; // VT_BYREF|VT_I2.
0 i* X/ N& m1 y  long * plVal; // VT_BYREF|VT_I4.
# x! u9 A* d% d  float * pfltVal; // VT_BYREF|VT_R4.
& [1 u: A8 J! N# e4 z) V, P8 N3 D  double * pdblVal; // VT_BYREF|VT_R8.
; g$ y& F& ]2 v3 }- n/ }" i  DATE * pdate; // VT_BYREF|VT_DATE.
" |' f% r5 b' j* W  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.- f! V, v0 H. h% c7 A7 g* a' L
 };
/ R, I. F. t7 ~; ^& \* p& ]}; & y1 `0 H9 X" O  d

6 o3 M1 d4 P0 V0 p% Q6 \  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
/ w9 B. q/ L7 n! g% f3 B9 }% r/ [) q1 |( H
VARIANT va;
; r9 e4 w5 [6 l7 e; D, F:: VariantInit(&va); // 初始化9 R. U8 t; c: P* @
int a = 2002;
5 A( x6 y! O! `( mva.vt = VT_I4; // 指明long数据类型. m) J  w" X. M5 @" q# A. `- j
va.lVal = a; // 赋值
) \& F6 l1 C7 \( z# B5 l+ m- E% f' f0 D
  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
/ s" p( T' k1 c$ a% k. E
! D  M. y6 H5 W1 B, K1 L  VariantInit —— 将变量初始化为VT_EMPTY;
9 `# A9 w  `' q+ I$ B+ f; X& y; J0 g3 Q# w5 h; ]1 J
  VariantClear —— 消除并初始化VARIANT;
+ Z' C6 }2 C, q; i& x7 m# {  D9 o! ^3 A! M5 M
  VariantChangeType —— 改变VARIANT的类型;* m! C1 N# l/ |4 j3 d; N
: l) {! I) L+ \  H
  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
  `+ x3 V. v% T5 B8 ~. b$ j- l7 _7 {0 @
  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
0 ?8 C- c0 ~0 \9 B. X& ]( q# }* q1 x' S8 f$ _" h# |, A0 u
COleVariant v1("This is a test"); // 直接构造$ |' Y! e: n! M3 P7 v
COleVariant v2 = "This is a test"; 4 v; ?) S% Y4 ~  _3 N
// 结果是VT_BSTR类型,值为"This is a test"
( ?1 {  q$ W. Q3 jCOleVariant v3((long)2002);' v( G+ B8 s0 T( t2 _4 Y
COleVariant v4 = (long)2002;$ W, D* E7 s; P
// 结果是VT_I4类型,值为2002
  G! M/ Q2 l9 v8 A, R
( o. d5 U( Z, {- O1 d: L, y  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:: I+ y* Y" S% K1 P
5 [' Z* h& w2 O( X+ w" _' j' O& a
  #include "comutil.h"
% N* ?: ]: a* A( d0 r0 X/ _, f
. A$ D3 }  k* X; a4 A% s& s" T  #pragma comment( lib, "comsupp.lib" )
9 M7 q2 S! W6 G9 u" X" \
  A6 K% O; j/ C1 a! g- {0 ~, e: g8 r  四、CComBSTR和_bstr_t
" P' V/ S/ X. f" y8 Y
% @; g+ M4 h+ K) C& z  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
4 L8 U$ o: j% q" a% q
1 {* T# [/ M( X6 a8 O/ T0 ~; _$ ^CComBSTR bstr1; 8 h7 A. n. H, Z- U3 `9 C3 o) I0 R
bstr1 = "Bye"; // 直接赋值" u" k8 j% E! O& W# E& p( n: c3 I
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符8 n' O7 c9 U- v, B3 k
CComBSTR bstr2(wcslen(str)); // 定义长度为5
5 P* W1 v/ ?2 L$ K- Fwcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中* T1 w: ?0 E8 K; G% \) F
CComBSTR bstr3(5, OLESTR("Hello World")); - p) N. a  o' I+ o$ b- P8 ^
CComBSTR bstr4(5, "Hello World"); " \$ b% Y% u8 {$ K( |
CComBSTR bstr5(OLESTR("Hey there")); 7 I3 d. {! V  |) `0 O
CComBSTR bstr6("Hey there"); 7 ]0 r- w1 F& e
CComBSTR bstr7(bstr6);
! ?/ Z' v7 \. s- n// 构造时复制,内容为"Hey there"
2 s, ^7 v; f* j5 `3 H
' \$ H5 M: J0 J0 I2 `  b' G- {  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。" D. I! h2 w% O
2 A" c8 X! }; a
  五、BSTR、char*和CString转换
/ W3 b* ?. w" a$ _$ k, L4 t$ b" T! ]& O' w, o% c
  (1) char*转换成CString
+ e# H9 c$ ~5 Z  ^
& x# Y% U; F8 a+ @7 B9 s" h" i3 o4 [  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:2 U+ u: C- y! v- x

/ m0 c1 n7 a1 Y* \( dchar chArray[] = "This is a test";
% ~3 L* A" J) }$ W1 X1 Echar * p = "This is a test";
7 ^. b) i% {" g) x( w. n0 {0 \) C) `: j) ^$ l" x$ A
  或# c& E. `: z/ b! @5 V: f

- N7 q5 }) `* u0 gLPSTR p = "This is a test"; " q  I/ W8 Y* n: ]

' ~: e7 }7 \' g; m+ k, Y  或在已定义Unicode应的用程序中. C; I+ j" [% }* [" W( b
/ ~2 ]+ n$ M  E: c) Z$ o6 t
TCHAR * p = _T("This is a test");
, C: r+ M- |( ]* I; O7 d& |* d- l6 k. n3 Y0 J1 I
  或
, V  W6 s4 |4 R4 u( K* j  s, p6 a( Q6 F' L% ^& W) _
LPTSTR p = _T("This is a test");% U+ ?& e) u; e* q+ r& ^* s
CString theString = chArray;
2 J+ u) @. L) L! G  WtheString.Format(_T("%s"), chArray);9 E) l! P2 e1 F
theString = p; & u# t5 w* \+ L
) I, g  z5 M, }) ~" e! o( n0 V2 m; y
  (2) CString转换成char*# {2 s  _  _! N; }+ q& K8 a; c

7 K  Q- m' \; b8 E! T2 H1 J9 L  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
3 [$ L% E# v8 K. T' h+ ]
0 `5 b. D# e0 W# R0 P5 R0 y2 ?  方法一,使用强制转换。例如:& E; M7 Z% d5 e, ^; q
3 C! }8 ?, i5 k% Q
CString theString( "This is a test" );0 W' Q0 ^3 Q" N' T
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  $ W* b# _6 o1 g5 ~

3 ~+ U! k. n1 b+ V7 P- @/ I8 j3 n8 o  ^  方法二,使用strcpy。例如:
" X+ ^7 ?* D) K1 c
! P5 O8 n8 `! ~! e/ m) a, ACString theString( "This is a test" );
1 C; g: D( ?+ v. {& _' YLPTSTR lpsz = new TCHAR[theString.GetLength()+1];
# b9 k) q- U2 s0 T+ L. v_tcscpy(lpsz, theString); 9 M! m* C1 N" i1 d0 h

  y. ]' E2 r3 m5 u, U, _  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。9 |) y7 n& h  {- P( H

" R3 l2 m. M- o; c1 ], D6 Z2 f  方法三,使用CString::GetBuffer。例如:
: }& J/ d8 {+ w  n7 |! X6 A# }
' B* S% s2 C2 K1 D8 K6 f, uCString s(_T("This is a test "));
: ~' N: ~2 e5 zLPTSTR p = s.GetBuffer();- ~9 B! {* H3 A- q' l2 y
// 在这里添加使用p的代码
- t9 @% y# a* \0 v: wif(p != NULL) *p = _T('\0');
$ ^$ }" W7 h3 D+ J$ J, j! hs.ReleaseBuffer();
2 A  ?% n/ L1 x9 l# t// 使用完后及时释放,以便能使用其它的CString成员函数 , H4 K( x+ L! ]. A
4 _4 l. A! Q  @8 I1 ^2 Y
  (3) BSTR转换成char*
7 \# d# W& c2 T4 Z( r) v& ?/ [# B3 V
  方法一,使用ConvertBSTRToString。例如:
" {: q' D' X+ b+ d: k; ?4 Z0 j: N
#include 9 e' g$ n7 b) M3 Q
#pragma comment(lib, "comsupp.lib")5 I$ O  W5 H- W
int _tmain(int argc, _TCHAR* argv[]){. I  H2 }+ F/ ^6 F) G0 a3 \2 H' C
BSTR bstrText = ::SysAllocString(L"Test");
/ u9 u0 A* O6 y* \  v1 [+ achar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);: ?% ~# e2 [0 I3 X9 U
SysFreeString(bstrText); // 用完释放* W) m7 h) \5 Y6 X6 s# ?& S1 R
delete[] lpszText2;
7 g5 p1 I$ @# e/ \9 L% Ireturn 0;1 `$ I& E8 W+ W. H+ Z
}  
, E- y" b" C8 _& v( F; D: S1 e* U6 a( b3 O( O) Z
  方法二,使用_bstr_t的赋值运算符重载。例如:
) u7 {" B9 P8 Q2 Y+ o. A
( l" b6 f2 w* z- j& a# Q, }* U_bstr_t b = bstrText;
* _3 B5 V6 p$ A' G* x( z# F0 F. Echar* lpszText2 = b; : R' m8 p8 ]* Y- s. }: o( ^7 u  g

, x8 H9 e1 M0 c7 E9 a  (4) char*转换成BSTR9 x5 J* h; [4 U- ^4 T

. ~; Q! K8 z# d* j) n( x/ Q  方法一,使用SysAllocString等API函数。例如:6 f" ]$ R5 o/ f5 F; F% _( j

& {; [1 K/ o: r4 ~BSTR bstrText = ::SysAllocString(L"Test");
' H4 p- s' v: ~  ~) Z* ~BSTR bstrText = ::SysAllocStringLen(L"Test",4);
& E8 p3 W8 Z& O  F( |  q& VBSTR bstrText = ::SysAllocStringByteLen("Test",4);
+ u' u: @% Z# d: A/ _; h" G& M% p6 l  b# V8 h$ L' ?6 M
  方法二,使用COleVariant或_variant_t。例如:3 v3 p' j$ ]7 Q3 A* I7 @2 \, U7 l
$ q9 W- y# H: u% S
//COleVariant strVar("This is a test");5 [, ~, a) ]+ y* ^, ?0 ?/ d5 R  w
_variant_t strVar("This is a test");1 Q: k$ N, Q! Q- p! T; Q, m
BSTR bstrText = strVar.bstrVal;
/ E* k1 t+ {  k6 A
% s4 S7 r' r8 i' N8 ~% N  方法三,使用_bstr_t,这是一种最简单的方法。例如:
& @: F  \& U  r7 A+ k3 q0 b7 \+ a4 b! a5 p2 B5 F& q
BSTR bstrText = _bstr_t("This is a test");
$ i2 M" F+ R' H+ \7 r5 I% ^- ]
; t  t8 f4 k! C* v& O! [  ~  方法四,使用CComBSTR。例如:9 P; P0 [  E' R

$ v/ _3 [9 c9 ]( b. }' |% y7 yBSTR bstrText = CComBSTR("This is a test"); ) D' L+ O$ w. M$ A' h  g2 ]8 H
4 x. C! o; S# C: L8 v! h* [0 c
  或
# M7 O4 f8 x8 h' Q8 L) x2 i! A# \+ ^  ]& B/ D8 \# N6 P" Q3 g: n) O
CComBSTR bstr("This is a test");8 I8 E8 U+ c$ {  M9 N$ d3 x8 F
BSTR bstrText = bstr.m_str; * r5 g1 u, m& m" {( k
$ C& H' j1 B+ X, C6 ^1 {+ r1 L9 ~. ^
  方法五,使用ConvertStringToBSTR。例如:
1 D7 b, z5 d! q$ y+ d2 \2 _+ m
& H; I+ K, G# e1 Q5 cchar* lpszText = "Test";
, {! E. r% y) D' M! B! B* m% y! eBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
% p% C0 K9 u  n! _
/ \" ~2 @: R/ t) l6 @  (5) CString转换成BSTR  N2 P. H1 ~& @, {: g
' {4 H8 i" c7 O. F4 B
  通常是通过使用CStringT::AllocSysString来实现。例如:
" I6 j  e. u2 J" r6 Y* Q
% n5 C/ M  [' b9 g0 P* g5 HCString str("This is a test");& t6 f4 ], g8 M, D' r# S
BSTR bstrText = str.AllocSysString();
: p' I- r7 p  a' o) w# z1 _8 T! w3 T5 }
SysFreeString(bstrText); // 用完释放  9 t# r4 p- I, T# U+ P4 F
8 r. v* m9 {% C; e' s/ A# O
  (6) BSTR转换成CString$ u' i" s# {3 `3 p/ S' z9 ?3 @
7 ~/ ^5 D  A* ^5 \! y$ [; M/ D
  一般可按下列方法进行:
9 Q4 v6 o9 [9 m% F2 G5 {9 n# l! k( |; {; `( a
BSTR bstrText = ::SysAllocString(L"Test");
1 t" ?& _7 R  K- z( q% ICStringA str;: T0 P" X( ~$ g  {
str.Empty();3 S5 g7 c  {) s" @% ]
str = bstrText;  " n9 S% D) z" n8 T& [8 ^0 t9 P  [

' e( m- M* i) d5 v0 T+ B  或3 |: R; v, u2 ^. t
& }( Q# f! ^: \) x2 T
CStringA str(bstrText); 9 W# g7 ?  V. u- c1 L. v

% f( Q7 I- C+ g  |& X  (7) ANSI、Unicode和宽字符之间的转换" L5 v8 H" u4 @/ l/ Z

5 n  U( y" F: D2 J6 C4 Y9 b  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
* H8 @5 c, r$ m% \0 E
8 Z3 k4 p. D" [, Q9 r3 p- b( l  B  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
; G% ^, y. E9 L' Z# P' |# r4 }
  w/ }- D$ ~0 ^8 V% Y9 b: @4 L' kTCHAR tstr[] = _T("this is a test");8 ]  N1 r" N! r/ w" E* k
wchar_t wszStr[] = L"This is a test";
" B! W" ~2 S: J9 \% \String* str = S”This is a test”;
7 F5 {3 R8 P6 V' w, {1 @( k9 p4 M0 [% b7 J
  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:1 N1 j' f% q) y' z# \, d
0 u" g' n/ O4 m
  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:+ Z3 c: [% `4 ^5 g' g* |3 H1 x
3 ~9 W0 N& v; `6 W  p! U+ ?8 H
LPTSTR tstr= CA2TEX<16>("this is a test");; w- d, _) O( F- f! L7 u  g
LPCTSTR tcstr= CA2CT("this is a test");. @, D3 ^) j3 t7 Q
wchar_t wszStr[] = L"This is a test";
% [. S+ y9 ~* l* Y+ u) echar* chstr = CW2A(wszStr);  & j  o2 N4 L2 Y& f
  L$ _( n5 v# l1 Y: g
  六、结语
5 R( ~3 ]9 G' ^6 z/ z: [! f2 J# @" f! V4 U, D2 @! Y$ Y
  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-8-9 01:49 , Processed in 0.034307 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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