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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky
4 [) S- e0 k5 j$ }. u$ g
$ z7 R% ~5 f( P) D( f0 ?! r: w4 y# z7 b% b. v* G
  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。6 v! U, |+ \3 X  o

0 l9 Z  Z6 H1 m- ]  n* g& g$ S  一、BSTR、LPSTR和LPWSTR
" X- B' E$ {9 z) T* n3 U* u! s) Z* ?0 G  M. r
  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
! @" c: w. z$ y2 {+ P9 b5 x9 v5 m9 q& ]* \% g! s# A2 u: o
  那么什么是BSTR、LPSTR以及LPWSTR呢?4 ?; J) k( W0 I/ t8 }3 r) S

% Q+ q! T) J3 W. Q2 L0 h: t; m  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
7 a) r- \/ \/ y9 V4 @5 r. T4 S$ T0 w, a4 j% P' c, u
  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
0 I7 w. }: D' X6 w, g. d6 \! R/ {9 Y
  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。: _1 q* k( n1 N) w- D1 J' ^
3 G/ V6 |3 ?  I
  一般地,还有下列类型定义:$ P7 d3 M9 F2 g

2 T5 d, y5 X; f# `& ~/ q3 v  {) _#ifdef UNICODE ! {4 I2 H8 u% w) `; W
 typedef LPWSTR LPTSTR;
1 c- \% [$ F5 t- d: ~, e* {( ^+ [ typedef LPCWSTR LPCTSTR; : h8 t: q3 V0 b. H9 s  N' q
#else
7 ?3 B, d1 g! [5 {3 `- D5 E typedef LPSTR LPTSTR;
9 P( _3 B- w5 N typedef LPCSTR LPCTSTR; 3 G0 n6 y( \! U# G4 A
#endif  
" y% e$ d- A$ O3 G! l, |
. [: I: V) @  T6 X  二、CString、CStringA 和 CStringW4 M. A) z. H% c1 A3 P

: a4 Y( ]# q  Y" [3 a* r  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应用程序中经常用到,这里不再重复。+ a  q5 e: ^% {$ ^' `$ l0 U2 \9 }

2 [' o) H# n# P2 A+ X! Z# \% x8 b3 O  三、VARIANT、COleVariant 和_variant_t
( q( F, I1 X) c# c- a) \. U$ S9 g$ j) ]# g* [1 u9 {7 f
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
# f8 e* U0 D4 O: C* K% c9 ^% G/ @1 y
struct tagVARIANT {
- g# \, ^1 V% G VARTYPE vt;
! Q: M0 A& h$ Y# P( ]" B' s union {
2 q2 h9 ^: W" X+ V3 g5 h. X' I  |  short iVal; // VT_I2.- y3 t( S( z/ `- D$ j
  long lVal; // VT_I4.; I+ Q# H5 D) H3 H) o
  float fltVal; // VT_R4.
- }: M; K* I: X  double dblVal; // VT_R8.. `3 ~. b8 R$ _/ u* O& y
  DATE date; // VT_DATE.
& }/ }# C' Y$ A  v) ~" J  BSTR bstrVal; // VT_BSTR.
/ s3 x  W2 j+ O$ U' f# u0 I  …
2 s, C9 W0 s/ t# }8 S$ g7 s/ A  short * piVal; // VT_BYREF|VT_I2.
& U0 _/ _: U0 j3 _7 ^! G  long * plVal; // VT_BYREF|VT_I4.  x/ w9 l$ k* Y! s4 \
  float * pfltVal; // VT_BYREF|VT_R4.7 _; f* m/ \! H  ?8 F
  double * pdblVal; // VT_BYREF|VT_R8.
- ]( ]" \8 @, g6 ~- r$ _  DATE * pdate; // VT_BYREF|VT_DATE.6 k1 O, e$ l4 _6 N2 X8 ^
  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.4 ]  g' h$ ~$ d/ E2 C7 R6 [, K
 };2 }6 W, m6 O  H& a+ Q, e
}; 7 B: P- N1 d- s3 k
" L8 M8 l+ @$ v( ^
  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
, ]( A$ y$ Z. s3 j6 c; h$ Y7 I1 O/ U7 \
, f# t% E4 y1 rVARIANT va;- K- w# D) v, s9 f; t
:: VariantInit(&va); // 初始化4 @5 c3 m/ }, P2 P' m& F3 B# }
int a = 2002;" h! G$ e7 P& t5 T8 q
va.vt = VT_I4; // 指明long数据类型
1 S. B  _1 n$ u4 r4 Yva.lVal = a; // 赋值 " U. n2 {6 ~. H& X0 O2 M

, d8 H$ M6 c3 D- S1 X  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:9 j% O6 I/ F/ v' O& E- f, [
& e7 r) G9 J1 r/ s2 D, r9 E) u% S& t
  VariantInit —— 将变量初始化为VT_EMPTY;
' K5 ~3 ]+ J$ Y3 N  d
* s6 e% @* P9 S: @3 G( {  VariantClear —— 消除并初始化VARIANT;
. q* a: r, J6 y. J+ p/ K5 |, F' M# f3 Y
  VariantChangeType —— 改变VARIANT的类型;
! O: |3 x. Z/ v, M# K# w8 R
. R0 b+ o# S& n  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
: D# \2 W0 J. J5 X! f2 L1 S' B% F* L6 S( F* V# R
  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:$ d9 R: D5 Y( I( _% V9 c
8 X3 j9 m" h7 o
COleVariant v1("This is a test"); // 直接构造, z, e3 W" \/ I: D* Y' g
COleVariant v2 = "This is a test";
8 K. I* o+ E' u& f: _) }// 结果是VT_BSTR类型,值为"This is a test"
: o; `! J$ j# |$ |0 T# RCOleVariant v3((long)2002);, Q& h; P9 T2 `6 X, ?5 @2 Y5 V1 v) ~
COleVariant v4 = (long)2002;3 b7 ?. W* j) j  r4 J" }
// 结果是VT_I4类型,值为2002   S' r. E, z8 s4 p7 n

. u8 ?5 X- H4 t8 B4 \  Y: a  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:% D+ H  |+ e$ a/ A3 j' C+ ?# e

7 G+ ~  z9 x; Y$ h  #include "comutil.h"
  L* v9 V! p9 \. E& S$ `' Q
( s. B# E9 h3 f1 l  #pragma comment( lib, "comsupp.lib" )- z7 o2 v5 U4 ]) h
0 f& ?; d  |+ j% h' \
  四、CComBSTR和_bstr_t
4 W" I' w' ]- r# k6 w% m' F  \! b+ V1 Z* \/ X
  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
+ O: ]' R6 i& O/ j$ Z( z& F9 L4 G' R  |/ ?, R
CComBSTR bstr1; 7 ?# ^9 n! U$ H+ M% J) C0 C0 M4 n$ R
bstr1 = "Bye"; // 直接赋值
  H! p% T4 n4 Q6 Y  V0 U0 k, fOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符
" i1 O6 N/ i0 c% O& MCComBSTR bstr2(wcslen(str)); // 定义长度为5+ p' o2 }/ O) w# l: W6 Z
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中( G0 h- {$ f# v4 t
CComBSTR bstr3(5, OLESTR("Hello World")); ! Q% C( _  }0 A1 {
CComBSTR bstr4(5, "Hello World"); ) L' b* r. R- v- q
CComBSTR bstr5(OLESTR("Hey there")); ! _6 H6 e7 N) U! I
CComBSTR bstr6("Hey there"); ) i2 n, p* R4 \
CComBSTR bstr7(bstr6);
; c8 b* V6 |- [! S) V// 构造时复制,内容为"Hey there" 1 Z, l& Q5 D: O1 N3 O! j) G7 A
$ {4 Y/ [% ]: v; l6 x
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。3 @/ q/ u1 z+ L/ D$ d5 \6 g
. m" h8 S0 A2 c) f  ~
  五、BSTR、char*和CString转换: j: m; O. ^7 G8 b  Y# G" j
9 f1 p# s/ [7 B- ?" O7 D, ]3 L
  (1) char*转换成CString
( `3 W7 l6 t0 x2 v
9 w; v# h9 Y  s8 h0 o" U7 I5 ]9 i8 ]& }  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:0 C' B% _. F6 s  @) n

# {+ D# L& a. D2 x& x4 ~char chArray[] = "This is a test";
3 ^# p1 P2 `/ O$ [/ Dchar * p = "This is a test";
& n, }; m) R. \; Q  x+ [
- g6 X5 r; a7 X" b, Q  或
, @; O) S0 \' s% A5 H. W2 J6 `
2 Q0 u% W1 T- B5 i& O& P8 `4 a7 o7 eLPSTR p = "This is a test"; 4 W5 {, x: A4 E
& _% F4 s! l1 q7 g
  或在已定义Unicode应的用程序中' `+ V- r' o. s: [& O2 ]  q
3 O- Y5 Y  z8 Y8 x
TCHAR * p = _T("This is a test");
: d' g6 ?1 `/ C$ E1 f' C' i/ B- K' h: d) d" o
  或$ ~2 L2 a/ [# E* t

% V% L* C/ m9 `5 C+ i. KLPTSTR p = _T("This is a test");! u6 S4 j3 \9 F
CString theString = chArray;
/ ]/ q" K! L: i8 [$ _( ~2 k0 ~. G2 ?& R6 ctheString.Format(_T("%s"), chArray);( m2 P) h6 U: Y0 T: k- Q$ p
theString = p;
9 a* U* a. P+ g1 ^) k9 B5 c! D: R0 [+ d$ y+ I9 E
  (2) CString转换成char*6 y" D4 f# b4 q7 q+ ~0 [
5 m) ]) f- x0 S  A; M
  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
' p1 ~! ^; S1 r7 D9 |" ^2 V
9 z, l$ E5 Q" L1 x  方法一,使用强制转换。例如:
  u4 G+ k$ F4 t9 c3 k) }
& l# V' ]$ p) \7 K  p# |CString theString( "This is a test" );8 y* f5 s0 g( G$ a& h# d
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  & V: c$ C+ L. \& f# S: @) y' t

& l6 h: G4 L/ h, B# h2 J, k! m- L5 a  方法二,使用strcpy。例如:4 Q, C  x1 c7 `$ u8 X4 P
7 a* Q& M& Y; k; ?" M
CString theString( "This is a test" );8 J$ Q$ @% ]- h! f% [  C( }
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
3 `- i' H# ]0 r5 n$ t_tcscpy(lpsz, theString);
/ C7 G1 |  g2 L! M' |: M4 v
2 {- g/ e1 B. ]! ]$ w  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
% Z6 [+ W* E- x/ Q, m- ]9 S' K$ V6 G$ {* ]9 b
  方法三,使用CString::GetBuffer。例如:, n& R0 r- s$ t3 h& l2 [
! X2 i: Y  A: j/ W2 l5 S- W
CString s(_T("This is a test "));' M, ^( D2 Q( j1 c! }5 \: M
LPTSTR p = s.GetBuffer();
1 z+ f" ^4 R9 R0 [// 在这里添加使用p的代码
  N/ u/ [2 F) G& }if(p != NULL) *p = _T('\0');
5 m4 n( ]+ j  m& m7 T5 Hs.ReleaseBuffer();   B2 d* `2 S) a
// 使用完后及时释放,以便能使用其它的CString成员函数
# J- N+ |; }: {4 @) O0 b! V7 m5 U) b1 O$ D# i$ c. }
  (3) BSTR转换成char*3 p$ [# J: m2 z, C7 d' Q; f
+ w$ }0 G% _. R0 g% e- w+ ]
  方法一,使用ConvertBSTRToString。例如:- B7 {& J0 h5 B3 p6 G: n

! A+ _/ I+ z) I2 C/ |) ?! h#include
; R$ o5 c: n6 F9 z$ G1 V# a. O#pragma comment(lib, "comsupp.lib")
. b/ ]7 F0 P# Mint _tmain(int argc, _TCHAR* argv[]){; V# l- v3 g& n1 p/ \
BSTR bstrText = ::SysAllocString(L"Test");
% r  h4 x- O( v$ y: g* ?  Qchar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);0 N1 }+ E/ q, O( p/ F
SysFreeString(bstrText); // 用完释放* B1 [/ z9 r" t9 z2 A0 h
delete[] lpszText2;
7 P# l4 Z; z( G/ A: H& U: F' qreturn 0;
+ w- t, Z7 v1 H3 M/ V0 {# I}  
' V0 q" k2 y# g/ @2 d! \
- x) t6 H/ g6 t3 ^2 C  方法二,使用_bstr_t的赋值运算符重载。例如:9 n: D, o1 q# Z0 f$ F& d! m) P
. j: H: [3 X& n! F* A3 Z$ a0 ~7 N
_bstr_t b = bstrText;
9 p2 s& o, x8 O8 uchar* lpszText2 = b; 3 o. o! n  `9 Q% O8 }# V' [
- R( R( V- e( c
  (4) char*转换成BSTR
& x4 `- C1 t: N' W" Z, Z' Q5 r& N, t" ?. u' p9 K' o* _9 X
  方法一,使用SysAllocString等API函数。例如:
& H+ i+ u& X7 g$ P
. }  _8 Q* \( |3 ?; B4 ~" b/ \$ QBSTR bstrText = ::SysAllocString(L"Test");$ M4 d$ L: n8 v
BSTR bstrText = ::SysAllocStringLen(L"Test",4);% x( |$ S4 T/ a2 s* R/ c; Q
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
) @* f* `) X+ w  K: I" x" _2 T
2 I" i3 V$ V4 C6 A) v  方法二,使用COleVariant或_variant_t。例如:
/ v: G6 O( {+ T. s) a8 @" f
" R) e) U5 _4 k7 Q9 a$ b# R//COleVariant strVar("This is a test");
- U) z8 l2 @1 t8 h' i* t! r_variant_t strVar("This is a test");) O4 p3 v9 G( ~5 \. Q( _$ ]  ^- j, i$ p
BSTR bstrText = strVar.bstrVal;
0 P5 K6 |, a" Z
' P" i4 D" `# n+ K# }: S! {. [- j$ i  方法三,使用_bstr_t,这是一种最简单的方法。例如:
& e& x% v8 \& V0 f
; }* `* E5 W6 ^, lBSTR bstrText = _bstr_t("This is a test"); 6 D1 Q# ~' S0 x! I% n3 e' R  R

. ^9 _0 Z. P! W+ U+ N" J( t  方法四,使用CComBSTR。例如:: t4 t9 K3 e0 v/ a
4 ^  {7 d* u! s" @* e1 H
BSTR bstrText = CComBSTR("This is a test"); ( z# O- F. d( c) k& x  K
- O+ V# G$ p, @. ?
  或
6 ~( f) T0 W5 Y) N3 T. @/ U: u  t4 k8 b" H2 y( b* X" i8 |1 E3 B  Z
CComBSTR bstr("This is a test");! q9 o8 u" b- `. D; ^6 ?: N
BSTR bstrText = bstr.m_str;
$ g& m9 L- L' X/ C% @0 K
5 C$ @# b0 y% T! b9 T* g+ [  方法五,使用ConvertStringToBSTR。例如:
2 {  v. a% b# j. l) K: j! M
$ r% O5 `5 q% B5 [7 Echar* lpszText = "Test";7 g2 c2 N/ ~: a: U) J4 F  Q
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
/ _" X2 h( T6 S; O) i5 ~! U
* {3 I; b- Q0 ?7 w9 t' D0 K  (5) CString转换成BSTR
0 p/ l* v2 d# X1 K6 F" K! b+ U8 r; T  J4 o. U* W
  通常是通过使用CStringT::AllocSysString来实现。例如:
' S9 X4 N4 \& X) L7 f+ i9 D
* A9 b7 t) u* {5 HCString str("This is a test");
2 }5 u7 J5 w# z9 u+ MBSTR bstrText = str.AllocSysString();4 d3 Z8 Z7 [  \% U
+ c, L7 _; e+ W6 T* l
SysFreeString(bstrText); // 用完释放  
+ W2 P+ _& T0 N. S# j4 T8 N9 h/ g; Z" s; i" |
  (6) BSTR转换成CString
& P  _- G3 B* F& S' o' F! s6 E3 |$ Q
  一般可按下列方法进行:
7 D4 @+ h% V  x- d9 R* l1 `% k. k/ ?4 {# l/ O* L
BSTR bstrText = ::SysAllocString(L"Test");
2 k& O( T( \8 X4 j, H9 F" uCStringA str;$ ^+ Y7 R& D- `( _2 m
str.Empty();
2 M4 ]0 H+ A! r8 G" s# h2 ostr = bstrText;  % f3 k! x1 c& ]$ z- [

- u) C* B' D- t1 g- m$ O  或
" R: c0 f4 b) ?! w/ w; f& e% S: V3 J- }6 ]2 f. [
CStringA str(bstrText);
) |0 y6 M' G5 q/ c  O+ ^3 a5 t9 W/ Z% B( L' m; K% j  V% P0 i
  (7) ANSI、Unicode和宽字符之间的转换
4 ~& a2 T8 I# D6 q+ [/ s. j4 W$ N2 s/ I# E' P
  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
, W$ Q; M, g- W6 A! w
$ i9 w/ h2 W- `4 T" U8 D  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:& A+ l9 G5 E1 O1 ~3 G

. u; f+ y$ V9 OTCHAR tstr[] = _T("this is a test");  t7 Y3 Q- T; P% b5 H5 Q, f) ~$ C
wchar_t wszStr[] = L"This is a test";" h! r# N; P) `8 _
String* str = S”This is a test”; & K/ Q! Y. Y5 L# L# u  j

4 x6 v8 g3 `6 C! M8 K  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:. N# [7 w6 t) `
( a- e- n4 ]' d+ r0 M
  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:# ]- u+ K1 |. J( E+ i6 k/ q* c

  z+ M6 B: q7 S; E, e  RLPTSTR tstr= CA2TEX<16>("this is a test");
/ s2 ^  P' m- O7 G# M- FLPCTSTR tcstr= CA2CT("this is a test");
) y4 N" N' L+ c8 Hwchar_t wszStr[] = L"This is a test";
5 ^9 j# w, I- f" S0 f/ F9 cchar* chstr = CW2A(wszStr);  
2 R* [4 i5 W3 v, o" U0 G
) Q$ c$ X2 X- k. p  六、结语
6 E+ a9 J5 t& T/ R0 X+ |& F, M, f$ v& e& z9 f6 u% B  M
  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-11-14 18:10 , Processed in 0.020959 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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