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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky
9 }/ D$ n1 k2 J
! Z& I# m2 C! j" I2 q5 l$ B0 `
7 S3 x% G# q' _( F  Z  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。9 h/ K% i* p/ Q1 |3 v1 G
! ]. B! ~0 n; o$ Q$ i9 w4 q: I2 n* d
  一、BSTR、LPSTR和LPWSTR
  V: ]) J* `1 k, [  s/ e/ s: s0 Z; e
  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
6 V5 c/ F* @, D- w
3 \! J& ~7 P4 |/ L  那么什么是BSTR、LPSTR以及LPWSTR呢?- h! {& [8 R, S: W; n

5 O% O; d6 |  L% a5 Q  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。  `& C" w8 {: D
2 D: Y6 U9 k8 t* ?5 x
  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
- Q, [0 S5 j; z3 Z1 I- `' {( X9 e6 l" S( j/ v# q7 m* [$ p
  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。5 @; D9 ~5 E4 d' Y# z

1 V) N9 a  p# R9 t6 \4 z  一般地,还有下列类型定义:, I3 \( c% l- H% A! {
+ r1 s2 o6 J6 h: r- S
#ifdef UNICODE
3 h  u' g1 w" l) S! A8 ~* [2 @ typedef LPWSTR LPTSTR;
; l% s. j- A' l! `) Y typedef LPCWSTR LPCTSTR; 0 o. K- O0 k; U! s
#else
. ?7 n1 \0 ~( s6 l& i typedef LPSTR LPTSTR;
1 T1 d/ b* i* Y9 m& z/ a: V" N typedef LPCSTR LPCTSTR; $ b+ m7 D4 I; |3 ?* {  d
#endif  % @! `/ S2 b, q5 a+ t' _

) h% a4 U& N% }8 D0 y: Q7 R: O  二、CString、CStringA 和 CStringW' T6 c8 D7 j6 I8 m
) j% D8 l/ I  ~
  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应用程序中经常用到,这里不再重复。* J1 P8 ]7 \& _: W* ^0 [# I" H
* U# O8 _( B. E7 p( _4 ?3 N
  三、VARIANT、COleVariant 和_variant_t' s- D7 |. y1 K6 K: L; F
" L$ i8 Z3 Z' |: ]" C9 C' l( |6 Q
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:1 U8 f2 E- A9 [- Y7 U! N" p+ _

" q+ ^0 M& y0 @3 d; X. s* N/ ]0 `$ ostruct tagVARIANT {
$ t6 l2 _$ n  O; B6 Q( w+ S VARTYPE vt;
, d- u& q! |6 k) k$ H+ e union {4 V3 P# @& j  M" G* n3 o
  short iVal; // VT_I2.
' L' A9 b/ @1 ]  long lVal; // VT_I4.
) t+ C+ S, [- r2 H9 g7 ]4 T  float fltVal; // VT_R4.
3 \: ^* ]- e1 k' d- n  double dblVal; // VT_R8.& J' F) I1 {6 k
  DATE date; // VT_DATE., Z7 W- |4 A* q6 {+ P
  BSTR bstrVal; // VT_BSTR.
  q- q6 y. T5 W, V3 j* @  H  …+ w2 [7 @' Z" X* K; e& U( F
  short * piVal; // VT_BYREF|VT_I2.; j  n* o2 y+ I8 Y5 ~; y
  long * plVal; // VT_BYREF|VT_I4.
2 r" {; @6 h7 p1 Y! W* A6 d  float * pfltVal; // VT_BYREF|VT_R4.6 A4 P0 @( e; X( b: h! z
  double * pdblVal; // VT_BYREF|VT_R8.
% H& W4 v4 r2 R; _  DATE * pdate; // VT_BYREF|VT_DATE.7 E* t! ?0 `( f6 K+ ^: w& p
  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.' U2 ^7 g2 X& q2 `
 };
) [3 y* N& l% t4 \}; ( u3 y! M7 ?& X" S

$ R/ F! ?, v5 l7 f4 w% J1 r. t1 V  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
8 h. t$ z: X+ ~- `! v0 Q1 J  `, [* }" L" L( F
VARIANT va;
6 k4 J" B/ ?# L:: VariantInit(&va); // 初始化
/ [/ ^. v& U3 g9 y. [; V5 I' Aint a = 2002;
$ l( X8 g1 C: w4 N5 F* e3 X7 Rva.vt = VT_I4; // 指明long数据类型
% v# k' h/ L* W1 _va.lVal = a; // 赋值 9 `5 m. T* B, c8 f1 f9 E+ S

! p, f" X) c$ ~# w  Z# w  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:  R& |# D! d+ y8 t& ^
! E! p6 P' D; j6 r5 c
  VariantInit —— 将变量初始化为VT_EMPTY;
) C1 {" B- S: S# z4 k. J5 [. h
6 ^" e% ^9 Y" S/ U  VariantClear —— 消除并初始化VARIANT;
+ I2 O5 z! D4 [( R- [8 C6 R
% a' a. O% ?3 B- [/ ^) Z6 R  VariantChangeType —— 改变VARIANT的类型;
  B' t2 g: L. Q0 u$ O
+ |* D& v8 Z6 M9 h% q1 l  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。- I1 e, h, S: s0 |; x3 l1 o

$ U& U8 a# M; }7 m) l8 j  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
& q  o  D9 `1 f2 U. h. Z, {" K2 I/ T/ q  m) B
COleVariant v1("This is a test"); // 直接构造6 G; J3 c% c2 Y& _! r5 ]% L, E
COleVariant v2 = "This is a test"; - A) y  ~# q; p3 G+ e
// 结果是VT_BSTR类型,值为"This is a test"4 k  c, r, Z1 u( i7 p
COleVariant v3((long)2002);1 U' j# l6 m. w* _8 u; N  o& e
COleVariant v4 = (long)2002;" [* g3 X; s3 E" o8 i2 a
// 结果是VT_I4类型,值为2002 ) K+ A2 c4 q, @- T; Z! s- R
" E+ \4 P7 |+ I- @
  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:( H5 X4 y; F$ y( U

( x* o4 j; T1 v' E% p; ]  #include "comutil.h"! ]# S0 M9 w: o) i# {
9 Q. c0 {; f7 B1 u& M, e. R
  #pragma comment( lib, "comsupp.lib" )
0 T( }) @. P: p/ @2 j$ |- n1 B7 L& L
  四、CComBSTR和_bstr_t6 e! S5 S2 B: s

* W4 N" F# q& u9 A6 j* o% E7 {8 g+ x  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
9 A  _( w) e3 Q7 X# O- ^& G7 C+ ]
9 Q. x' S- w( I9 c3 _CComBSTR bstr1; $ f( A7 _  v1 y' D4 h# A
bstr1 = "Bye"; // 直接赋值
8 e" v( g) ^7 h* j! X' n6 WOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符
: u. J4 ?: }& k* Z8 f+ O0 e6 pCComBSTR bstr2(wcslen(str)); // 定义长度为5/ a: j6 i; B/ Y' e
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
  R4 \1 l) J% V( i( F7 lCComBSTR bstr3(5, OLESTR("Hello World")); % N/ @' u2 i0 w2 l( N) X
CComBSTR bstr4(5, "Hello World");
2 b. j( L# g- f( F% x- }5 NCComBSTR bstr5(OLESTR("Hey there")); ; [5 ]' A' s& Q7 ~" T+ l
CComBSTR bstr6("Hey there"); . Q5 J3 m  E* k4 N' B" e: \
CComBSTR bstr7(bstr6);
4 `1 _8 g$ M2 O// 构造时复制,内容为"Hey there"
3 D% h3 }' M! B0 W7 a- {+ C0 ^8 C/ M
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。8 J6 F+ F) W$ O0 w& ?) v7 c

# b2 V$ |% A. ]* d& x/ W2 S  五、BSTR、char*和CString转换
' {2 {% E8 B- C1 }9 G) L6 H8 q# Q. p' z( J: ]' f
  (1) char*转换成CString5 Z' _( k% K1 [

; T1 S& O. h3 Y+ E6 s  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
3 K) y% Q/ _& `8 r+ r) ?; }& ]
$ n/ G  ^: ]0 A6 V0 Jchar chArray[] = "This is a test";
- A7 A9 C" @# a& T. M4 kchar * p = "This is a test"; / e- l7 _7 S' l% G- s9 P
* `" ]5 k0 ]4 P
  或
& I  O0 Y1 {, F4 c' o7 L* O0 }* `1 A) l' M! u/ S
LPSTR p = "This is a test";
6 }5 L# Q5 {% U$ |# L$ H1 A3 E8 v, M4 o# V2 ?- F# V
  或在已定义Unicode应的用程序中- w9 F$ p; r0 T+ G9 x
) n) X, b3 O9 a$ f3 ~, n
TCHAR * p = _T("This is a test");
6 q4 s* g- o2 h" [- U: u: P
2 J. [: A0 t2 K" }. D$ p  或
* X, S8 X& N- M1 e% H1 L
. C5 ^0 P9 a  n' M8 Y* K$ wLPTSTR p = _T("This is a test");' Q' B. f" U1 g- X( k! i; t# D
CString theString = chArray;7 ]0 O5 b! M: `) A, K
theString.Format(_T("%s"), chArray);
+ n7 j5 v4 n5 B; H3 Y3 W8 qtheString = p;
9 J+ |& D5 K) o9 B$ H/ s
# Z/ E* `" m! y* p+ H6 l' e3 [  (2) CString转换成char*
- B1 V0 X0 f0 s# t$ J- `3 P  W, b& q/ ?9 J; o
  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:- g, ~5 y; ^- _) h: Q0 U; S

- k+ b. R$ |1 [0 X  方法一,使用强制转换。例如:5 w. U) N, n% S( U: m" l

0 _( f2 r( F% Y* kCString theString( "This is a test" );: r; U5 Z% ^: T! g, B  `8 ^
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  8 h! ?9 y: t7 B( l# N2 Y) A) t

  t2 t/ r+ J# Y2 z: \5 K  方法二,使用strcpy。例如:
( P# `7 _" q! \- c4 S; k! F' D2 H3 B) O( v1 I
CString theString( "This is a test" );
7 Y8 W+ G! ~; y1 K5 f, c0 _! Z% dLPTSTR lpsz = new TCHAR[theString.GetLength()+1];' m: A& d$ W& O3 l
_tcscpy(lpsz, theString); 8 J6 H% M3 K* ~2 B

+ p) F0 ]0 h; H7 S$ ^" p  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。9 [4 ~! I" z) H9 d% J/ I
- p' x! E! A1 }5 @& p$ A
  方法三,使用CString::GetBuffer。例如:
# D/ d7 c/ ~# s( ]/ g% a: x+ S% v  j) _- s5 g$ T  G
CString s(_T("This is a test "));2 P: s: Y$ i# p7 b0 X/ L
LPTSTR p = s.GetBuffer();
1 q# K% w6 D" N% X// 在这里添加使用p的代码  B$ u% d( x# l! x
if(p != NULL) *p = _T('\0');7 |8 X7 g3 s; @" m# S
s.ReleaseBuffer(); / v" I+ A$ V5 R  _1 F: \0 g
// 使用完后及时释放,以便能使用其它的CString成员函数 4 R% p- L- _$ ]

8 ~& ~2 _0 B4 S3 l0 [  (3) BSTR转换成char*$ Z. E( O3 _; \; T
. X. C) D) n+ [
  方法一,使用ConvertBSTRToString。例如:6 B! T/ R6 _  u& f# x: E0 o
5 o/ s' F% u6 O" U0 w: ^+ B7 J
#include ; P- }2 `2 ^. M, U* X
#pragma comment(lib, "comsupp.lib"). H8 q9 C' D  {! B. P
int _tmain(int argc, _TCHAR* argv[]){
5 d+ p4 v( d: Q/ xBSTR bstrText = ::SysAllocString(L"Test");1 ]. M8 \: d' N
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);) N* r' a# o; s
SysFreeString(bstrText); // 用完释放$ p" n* C2 `2 ]7 Z$ m
delete[] lpszText2;
9 S/ X+ R0 a' r. A& sreturn 0;
+ C2 U1 m# B/ X}  . k8 P* M' h  h$ m

- L+ Q1 t1 V0 Q  A, C3 e1 g$ d  方法二,使用_bstr_t的赋值运算符重载。例如:
* D1 q4 p7 J2 j3 l" ~2 ?) }5 [7 x. R4 s, x
_bstr_t b = bstrText;: U+ g6 U, [) K
char* lpszText2 = b;
' g2 ~: y# [0 W3 p
  F" P# t6 q) _  (4) char*转换成BSTR7 x8 Q( L& R/ K( K1 q% w3 \

7 a0 m# {3 t2 P8 C# r: D  方法一,使用SysAllocString等API函数。例如:( d& D, j& u; G+ V, [6 x0 |- y
7 W+ C; }; F) H  ~$ L) ~2 A  A
BSTR bstrText = ::SysAllocString(L"Test");6 `1 x! v( d+ A6 O* @' B
BSTR bstrText = ::SysAllocStringLen(L"Test",4);
  B! L- {6 {# E) g4 qBSTR bstrText = ::SysAllocStringByteLen("Test",4); 0 U3 a9 h% v) L. A/ `4 {& c7 b

' P2 i! A) ?: @( m* A  方法二,使用COleVariant或_variant_t。例如:
0 E$ q6 [$ h' G2 H$ Q/ l0 e: a& S
8 r  h% s3 L' @+ J//COleVariant strVar("This is a test");
' f$ |0 J& Y1 y# I) [6 C+ V9 J_variant_t strVar("This is a test");
5 b6 R/ d: G6 w2 M$ e, WBSTR bstrText = strVar.bstrVal;
+ @/ p' A7 b. D; k6 T" K+ D' F  k' b0 y& H8 }7 @8 d$ ~
  方法三,使用_bstr_t,这是一种最简单的方法。例如:
7 k% E* b* `& H5 J
( I% H& _) W# W/ VBSTR bstrText = _bstr_t("This is a test"); ( m. F0 h2 F9 J

8 U# y" z) {7 H4 g  方法四,使用CComBSTR。例如:
: `2 f3 v& e" B; B6 }* e7 `. ], k2 D
BSTR bstrText = CComBSTR("This is a test");
9 |1 p- V# z, h: J& i; v" X5 l( g+ P( b6 S$ z
  或
, |3 V# m8 [; o. c
& {/ t, \# F# z1 v2 q& XCComBSTR bstr("This is a test");6 P% l8 ?2 J* Z, A& F5 y
BSTR bstrText = bstr.m_str; 9 I* {2 t; Y' g
, N( U* V0 a# |4 v7 L
  方法五,使用ConvertStringToBSTR。例如:
3 N& Q4 I0 @  f3 p8 D6 j2 B- N  d: o0 s6 ?9 t& o
char* lpszText = "Test";- ?) N' c8 ~5 m5 _
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
& V' R# m- B) l) |) E4 n* D+ _: n' [* O9 x4 f! l* W
  (5) CString转换成BSTR
# ]2 V$ C4 W' v4 ?% u
) X" r3 I0 }% I# L2 e  通常是通过使用CStringT::AllocSysString来实现。例如:
# X- w9 N2 Y4 S# @
. J- A/ {9 D: FCString str("This is a test");- G* ~) e! X9 C* |% S& @7 f
BSTR bstrText = str.AllocSysString();
2 C# U& k& U. q1 Z" N6 \' Q( {  J+ q
9 @$ ^" h9 K6 z. h' d5 a/ a' xSysFreeString(bstrText); // 用完释放  
  P4 M& [# D9 @# U& P
# p4 }0 f. M  b3 v& M2 {  (6) BSTR转换成CString3 h7 \/ r2 X5 d0 @
' {* O1 U1 H7 X0 Z! ^' a/ q
  一般可按下列方法进行:
( I' ~8 k& h9 z/ l7 a8 n+ |# p2 s- j+ }6 l! K+ Y. b
BSTR bstrText = ::SysAllocString(L"Test");
2 h9 h$ x# t* C# w  sCStringA str;: j+ ^& Y7 B- `0 O. Q
str.Empty();
9 K/ o! k# h/ p; o  sstr = bstrText;  9 `; Q: f% a/ ~! y

3 z3 C$ b# D. u  m  或
6 m$ M3 K5 J' z3 B4 \6 e
; g; ^) o8 |. S! eCStringA str(bstrText); * `. l$ ^6 M0 m+ p7 p

, s2 u' J5 |, S  (7) ANSI、Unicode和宽字符之间的转换
0 C8 |1 ]3 v; H$ L+ b
9 ~, i# w$ w: u  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
+ C+ f5 ^+ K; P  J; K( M
% B2 G1 ]' C- w9 q  h# [9 p5 m  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:! s- Q4 P; g, m# {3 |1 S, c% j- G
+ o9 y9 a( K' n; g% Q( j
TCHAR tstr[] = _T("this is a test");  x9 S$ N% m4 g3 x% |* j# G/ [2 y
wchar_t wszStr[] = L"This is a test";3 V# o- x1 s0 [/ r. f& E; T
String* str = S”This is a test”;
( H3 C* j6 v7 ~& A
/ V9 }1 W1 s4 r, T3 y" o6 M  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:  U! \6 I) t* M0 y7 s
/ D4 s# w5 |6 N& Z# D
  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
' b5 J0 Z# H/ G. C7 ?
; t/ W4 S* x% W# ULPTSTR tstr= CA2TEX<16>("this is a test");
. m  b, \5 ]3 m; g4 ?$ |9 a& gLPCTSTR tcstr= CA2CT("this is a test");
- w- x8 `2 ^2 t. A* Lwchar_t wszStr[] = L"This is a test";
' M2 B' t5 q( F9 ?char* chstr = CW2A(wszStr);  
5 d5 X' ?9 h; ^+ F" V) N# X5 N. C2 ?, w6 f( v. ?! v6 _; X/ s
  六、结语
+ R9 b0 n- X( X" I) L+ T5 G
& a* w$ k. V( w2 F' ~2 D! n  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-6-20 00:07 , Processed in 0.036426 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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