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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky, _9 ?  |, U# i7 K  N' a4 L
3 b; i; {; q5 ^5 j0 q+ C, a

! t) c$ R0 X" ]6 K; @" Y0 r! T  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。3 A; H! `: N  u  o: Y& x. D

2 ]- O' B# m, @0 r4 G) z; C) R0 F% W  一、BSTR、LPSTR和LPWSTR6 c2 B+ Q0 ~5 A) B: F# ^) Q
% Y/ b/ W) j1 [6 B
  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。. C: m$ n# g5 _
8 f7 x% e! l! U
  那么什么是BSTR、LPSTR以及LPWSTR呢?
1 m6 q/ H, D4 M7 \- ~* q$ {; Q2 q+ M9 K
  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
# {5 G2 S- e- ?! \; M0 t" u- z1 [
  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。; Q  O, w2 j6 P3 L# W2 l8 D$ J
" W7 z2 I: x" C' J: K7 }8 }4 |7 W/ k9 m
  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
* T) P0 e8 @8 I( @7 t& K* V- b! R( V
6 b, w3 A- S$ Z  一般地,还有下列类型定义:2 |2 U3 _5 y% O, S, o& `

6 Y$ O% R' @  j#ifdef UNICODE . S  G8 N3 b, Z+ n
 typedef LPWSTR LPTSTR;" @& v- K+ ^( f* ?0 P4 S
 typedef LPCWSTR LPCTSTR;
* L4 C: C+ U  j4 @  G$ m#else : J4 [# r/ D0 Y* q' w
 typedef LPSTR LPTSTR; 2 c' o* ^7 t6 e. z, W4 F# ^
 typedef LPCSTR LPCTSTR; / e. }5 G# L. ?5 t6 M- i+ q7 n
#endif  / j: @' |$ p, f; u2 J
- x- P$ b7 S6 H" X
  二、CString、CStringA 和 CStringW% f6 N4 M! A; C% D9 N

3 g7 U% f; b  L+ M8 N, `' n  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应用程序中经常用到,这里不再重复。  ?! x/ U  E  F& O7 F$ [0 ]' P

7 I+ ]+ M) x4 c9 `$ ~" i8 s' h  三、VARIANT、COleVariant 和_variant_t/ g+ ^9 Y( [0 l8 V, \3 ~' b9 E( W4 C
* j! d2 s7 U- |" V* ~
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:1 m! i! n0 l/ ?* t, n- \: {: q
' `6 y* c5 e3 S9 j
struct tagVARIANT {
. b& {  |: i$ T VARTYPE vt;
9 ]. S2 ~1 B  |" y+ N0 { union {
5 e* l6 ~0 N& R3 v8 i: j  short iVal; // VT_I2.- o3 b+ Q- @' N' z
  long lVal; // VT_I4.& u% r% E4 v5 \$ `8 H% ]
  float fltVal; // VT_R4.
5 A4 J/ Q1 K) ~9 F- i1 c  double dblVal; // VT_R8.
- Y* Q! n  r! c* `) p! L  DATE date; // VT_DATE.$ o0 e7 z' t1 R: `" W
  BSTR bstrVal; // VT_BSTR.
4 h  G0 g3 T- Y7 p  …/ l; v5 R% j( f( @, _( N. G" r
  short * piVal; // VT_BYREF|VT_I2.8 j6 v2 \6 k: o7 \* O
  long * plVal; // VT_BYREF|VT_I4.
' M2 Z* R0 n0 I: r; ?$ U# S6 L  float * pfltVal; // VT_BYREF|VT_R4.
1 R  ]/ b0 w, a3 m: H# v! F  double * pdblVal; // VT_BYREF|VT_R8.3 Q1 `  ?9 H1 ]6 {  f% Z
  DATE * pdate; // VT_BYREF|VT_DATE.; ?; Z5 h# {6 r5 g- T
  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
" R7 ?9 n# C* }9 f4 C9 G4 |2 v };& o. N9 }8 W, @2 v( C; i+ c
};
. _( I3 U0 y' P% S$ e# @  D5 a1 q4 x
  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:8 j5 y+ O7 l& a3 ?- f& d. W9 M8 d
. p- `3 w6 K0 L$ S: q
VARIANT va;
5 x* l& H( z' ^) W2 z9 H: s:: VariantInit(&va); // 初始化
8 O( B# Z" T3 U. Yint a = 2002;
2 ^. E* ]1 O& t/ u# Nva.vt = VT_I4; // 指明long数据类型  m  n5 A) M9 d4 ]. m
va.lVal = a; // 赋值 8 ~1 r* ]: Q* Q: r8 r
6 `. |% Y. j0 b. N! ^- z; w
  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:: A, T6 a/ g, |2 X9 Y- Z# A( z$ m
7 u: |4 t7 z6 `( N# D+ }" r' w) h% |
  VariantInit —— 将变量初始化为VT_EMPTY;
) k+ M* I! s  b. x( Y8 @  ~6 }/ M- Z. C2 g
  VariantClear —— 消除并初始化VARIANT;+ x# {0 e; g8 b/ I
& q. G6 P! v; W: o8 a! @
  VariantChangeType —— 改变VARIANT的类型;
" M  m1 f6 C- C5 H$ R* [
; B( _5 A3 }( E( E  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。! l+ y" s# v: a1 z
* J; E/ j+ u- h9 M; @+ g
  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:1 E- n. S3 w0 z6 |7 s
& m& G; ]. ^3 e4 B' ?
COleVariant v1("This is a test"); // 直接构造$ s" a# r8 c) \
COleVariant v2 = "This is a test";
) t* S: ?1 Q2 l  S8 S// 结果是VT_BSTR类型,值为"This is a test"
" M  }$ o2 f. I% B, VCOleVariant v3((long)2002);
; P; H" A5 Q1 }) u1 T, U/ s, JCOleVariant v4 = (long)2002;
( U- T0 R' I4 a& d0 w! {; ?6 e// 结果是VT_I4类型,值为2002 2 b: V4 X( [( B
/ z# q$ V) x+ S. ~, [( u4 `& m, J8 Z
  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:
* h" _5 t  u1 u8 s& e: R& m4 _, ^0 B! f( i4 q3 `" i$ m/ J
  #include "comutil.h"
1 t0 t" F* o' V- G1 M/ u; Z+ A( ?! `3 v; m; Z! e
  #pragma comment( lib, "comsupp.lib" )
0 v/ ~% k- H7 u! |* |
$ ^$ K+ T0 i* {% E4 c/ k  四、CComBSTR和_bstr_t
, i( m4 U# f* h& n3 e1 }0 I. O7 }7 ^6 _5 Q8 o
  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:5 W6 l0 T3 |7 S, u' x
: D7 R# H1 g/ g& f& b* R. J2 X
CComBSTR bstr1; 2 Z5 p, \% e8 g1 p3 L8 v2 L
bstr1 = "Bye"; // 直接赋值1 |( h8 Z. p# _8 h2 }6 j  n
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符" E+ V/ A! M# g3 `. y; q% K3 L
CComBSTR bstr2(wcslen(str)); // 定义长度为50 E- l) g' v7 {
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
! |9 \7 K# q; S8 f* w( I/ tCComBSTR bstr3(5, OLESTR("Hello World")); + I( j# t- p9 q# X$ ?/ m
CComBSTR bstr4(5, "Hello World"); 6 n8 T% C( r% g& l1 V2 M+ ^. m
CComBSTR bstr5(OLESTR("Hey there")); 5 }# u- p9 e; W
CComBSTR bstr6("Hey there");
* V! W/ V- H% B: Y- r2 O; M: pCComBSTR bstr7(bstr6);
% i! B' n+ {- N% B. {0 M// 构造时复制,内容为"Hey there"
0 c; g2 w( a! O$ [7 `4 L1 V: B' A& o7 B' Z) @: B
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。/ [" m" N' t4 m- s. t3 I

. m6 R8 p6 c: W! l  五、BSTR、char*和CString转换; a. l& G5 D1 @  @! `9 {

- e4 ^* i: C2 V5 g! W  (1) char*转换成CString
* ]' D% `9 M9 r  W" ]4 K
$ ^1 C" U. X' e5 X3 ~' W3 y3 u. b  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
% i/ Y* J6 U$ o" ^& P# w1 s0 w6 _
( l$ f  @: z/ [% _+ lchar chArray[] = "This is a test";3 I5 t* P/ @1 J- @% L
char * p = "This is a test"; # N2 p( c0 B) q- w4 |
2 a! q* a3 U- }$ y( ?
  或. ^) `, }( z5 B" e" ], @
- W# R  ~1 W) N* \! C" f2 }
LPSTR p = "This is a test"; : K: C: @7 @7 @2 K, V; i

: W& g/ w9 r* s: X  y2 u  或在已定义Unicode应的用程序中
( C! ^& t: j! p# E4 B3 K) t7 I4 K7 S
TCHAR * p = _T("This is a test"); / Z4 K) R9 m7 Z; Y
: c9 }# ]( x8 a9 t3 K6 T
  或: [# ~3 }% b+ D2 y  m1 u

8 K& ^" N4 V( L5 A3 o. }: HLPTSTR p = _T("This is a test");
- X5 g6 p0 O# S: ~9 GCString theString = chArray;) `# e: ^, Y! q. t8 Q
theString.Format(_T("%s"), chArray);( Z5 D, i$ Z) y2 p- z
theString = p;   W1 W$ `- o+ ?* R- i8 R; t9 w

6 H0 C9 T" n( \7 m  (2) CString转换成char*
. R7 k# e- b% k
  t  E5 t' O; Y" P! {5 d0 L% J  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
8 O  P3 t$ U  C! b' R( v! s# c: j$ R4 q+ O$ }
  方法一,使用强制转换。例如:1 n  |, T! ~4 e3 L6 \8 S/ K( i& E9 R  k

; D) a5 S2 @5 d6 fCString theString( "This is a test" );7 \6 o6 u; N7 N- f+ D8 S) U
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  
  n/ |  j% Q5 n& z2 n  }: D; ~
+ n9 z; o4 ~0 [" [  方法二,使用strcpy。例如:
# d, @+ S* W% b" l+ t9 V7 W; _9 L
CString theString( "This is a test" );
/ o0 ]& b; r/ G0 {LPTSTR lpsz = new TCHAR[theString.GetLength()+1];" h1 _8 N9 C0 o0 H5 o
_tcscpy(lpsz, theString); 5 ], |  P, v# T- Z# D8 y% T1 B

8 Q( ~' Z- W! |6 t! x) X  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。/ D3 v& d+ l) \: a( q

1 f! r- J# [: E; a/ J& ]0 S  方法三,使用CString::GetBuffer。例如:3 v5 a" }/ M) o* F/ Z
7 ]! \0 \6 B) U, d! {' x
CString s(_T("This is a test "));
& j; B, [3 J+ w! Z& b+ M% N3 rLPTSTR p = s.GetBuffer();
8 v9 i1 U7 }/ S3 S0 @/ j0 d// 在这里添加使用p的代码" w5 S3 V3 S, Z; `
if(p != NULL) *p = _T('\0');
5 C6 J, J( h, M4 V1 cs.ReleaseBuffer(); 9 S/ d! c# }2 O' a0 k/ W
// 使用完后及时释放,以便能使用其它的CString成员函数 9 p: x( w9 A% p9 D9 C9 I
/ o0 t3 u% o+ |, k
  (3) BSTR转换成char*" F. o% G9 n5 R7 t
' M0 s' k# ?  `9 i- |
  方法一,使用ConvertBSTRToString。例如:
1 |) m; I. {5 {* s. Z1 A- v* |
: r% U% {6 W. `! n#include ) ^0 J' X0 d6 p/ U7 h
#pragma comment(lib, "comsupp.lib")3 t& o0 h4 C* c/ O
int _tmain(int argc, _TCHAR* argv[]){
/ H& }) g6 o0 N; kBSTR bstrText = ::SysAllocString(L"Test");) a9 m' V; Y; b6 F
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
2 q! F6 n* C8 Y% e; C4 BSysFreeString(bstrText); // 用完释放
8 W+ Z3 p/ `2 G. k1 S5 Edelete[] lpszText2;
* u* X* W# |$ r3 c7 H' ?- Xreturn 0;
- U" _/ V: t" M  }  t0 z1 U0 h}  
! L/ l) H; q! |
- H2 ^: X4 b" X; w  方法二,使用_bstr_t的赋值运算符重载。例如:8 v1 q- _/ M. y) ^- Q: ]9 k

- H& m3 M: g& K4 A; x_bstr_t b = bstrText;, g( m7 d, r6 @2 R; L* V
char* lpszText2 = b; " o" o& L9 S: f6 l& B1 u7 d3 V
# v- y% P" h4 d. Q
  (4) char*转换成BSTR
& F9 D8 I- {5 E. T+ R- H2 i& g
2 h# c% _8 @1 O. V0 t4 p% k  方法一,使用SysAllocString等API函数。例如:4 `# e# _1 I" |% J2 g# O* U2 c( h

# g6 h$ j; I4 p% x$ s9 cBSTR bstrText = ::SysAllocString(L"Test");
) `, t! m" g: |. @5 H9 H) P; I* aBSTR bstrText = ::SysAllocStringLen(L"Test",4);
- J0 J5 ?- s$ L/ }0 d* h! lBSTR bstrText = ::SysAllocStringByteLen("Test",4);
; I( x* a5 W1 h" p: m0 o* q
$ H/ u' Y- v8 m6 @) s  方法二,使用COleVariant或_variant_t。例如:1 }, e7 D$ H. Y! n/ |/ h) Y
) x# v: I/ G) x* m  P$ o2 O
//COleVariant strVar("This is a test");3 L0 ^8 Y8 Z  p/ p
_variant_t strVar("This is a test");
" z" i8 l) S& J, \* c4 ^: _' z- LBSTR bstrText = strVar.bstrVal;
8 H" X1 b2 z: N8 F+ a! V1 H* h$ \
) y4 v0 S) W9 L) Z  方法三,使用_bstr_t,这是一种最简单的方法。例如:. I; j6 _1 p% [0 R
8 ~0 h1 i. _, z6 T
BSTR bstrText = _bstr_t("This is a test"); - y( a6 h1 G0 ?, X2 }' S
) _* f5 J; ^' s* \9 [
  方法四,使用CComBSTR。例如:
3 E2 I3 `$ S) C# A) L7 G) s1 |0 t4 P1 o: p
BSTR bstrText = CComBSTR("This is a test");
/ G, t" J, E/ s* U8 w
1 R" r7 g1 S5 c) D  或
4 I( G1 r; v8 z
& D, y7 O: R- U) q; Q* K  ACComBSTR bstr("This is a test");9 E  n9 y! S2 l
BSTR bstrText = bstr.m_str; 2 H6 u5 @' g; R

3 T; @0 }. P. S) Q: m  方法五,使用ConvertStringToBSTR。例如:
5 G) q: q4 f( t; x7 q- V. g: z
, [/ ]. q5 r+ n* l% z$ j% s; T0 jchar* lpszText = "Test";
$ ^, U* b* f6 f  Y: BBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
% g% p' A, Y/ h/ {6 I! m) g! L) y7 a8 }. r3 |: ]
  (5) CString转换成BSTR6 c3 I2 }7 a/ a' O
9 E; ~. Z5 r: u) e
  通常是通过使用CStringT::AllocSysString来实现。例如:0 A+ _3 ]5 `, G1 t

- `- [, P  k# G: Y4 {CString str("This is a test");
9 d% b: Z- o1 z# Z, TBSTR bstrText = str.AllocSysString();2 r6 b1 g% l* i
! V! Y4 {, {. o9 ^
SysFreeString(bstrText); // 用完释放  
( F; M% O' v- `+ J
+ ]7 H8 ~2 @3 k' h, b2 i  (6) BSTR转换成CString
( b  I# I5 @6 I
! F& M! R, v. u9 [% F  一般可按下列方法进行:
4 c' z9 w6 e5 L4 ^9 z' \9 ^& H
  s3 |3 ?( [, ~/ qBSTR bstrText = ::SysAllocString(L"Test");  H& t) @2 }! c' P
CStringA str;4 g; V: W  l8 G6 N/ A5 U$ ^
str.Empty();
: [+ |! c" ^2 ~% F6 h  K+ Xstr = bstrText;  3 a. i% n1 d+ ~: n! k& X
( X. M+ t# |! P# p3 A/ x
  或
% y+ w$ w' L" @8 H! `: l* S9 S) @' H- c6 p7 d
CStringA str(bstrText);
' v2 w% k  `' ]. q' p5 Q( j$ z# ~5 u+ s# h5 U$ \
  (7) ANSI、Unicode和宽字符之间的转换( Z; O) o3 d$ r
% c7 h2 r3 m+ v7 l0 E" f% U, s3 o
  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
/ O, B) }, k# w8 h" ^
2 l* ^2 y" S6 {1 T  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:# J5 @  j  X9 C6 E
+ I# K4 i0 G7 g2 i9 l
TCHAR tstr[] = _T("this is a test");
0 j% o# s" ^6 i6 t7 \wchar_t wszStr[] = L"This is a test";
( K$ k- A1 O/ k( \String* str = S”This is a test”; ( r/ `8 m( P" a* E6 e! V

5 @" P+ T* c: o3 @- e  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
1 N# ^: G9 Q" E& Y9 ~& t1 a8 J; [& J" ^6 r% V4 F! Y) P3 \1 f
  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:) i& F; f- D0 f7 J# L

+ A" C! m: k2 p$ D: }- Y6 rLPTSTR tstr= CA2TEX<16>("this is a test");" a% k9 w. N, s) L/ ?# q( ^
LPCTSTR tcstr= CA2CT("this is a test");! m" V% i: t& n' f( Z/ \3 m
wchar_t wszStr[] = L"This is a test";
/ g6 `: q! |) K2 M0 Qchar* chstr = CW2A(wszStr);  ; z) ^/ a9 r, r# q+ T
( i8 c8 e& C2 S3 q  _, n
  六、结语' ^" j0 H8 r0 S5 Q7 g$ n2 i

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

本版积分规则

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

GMT+8, 2025-8-9 03:57 , Processed in 0.034972 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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