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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky: n! x/ Y: K7 [0 A' y, {

6 a7 \5 D4 n0 Y3 u: b7 U6 W! j0 f$ V. ?/ ~; t, I
  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
' C% l# A7 a6 Z" v( ^2 c; P+ y' s$ Q# M1 O- n
  一、BSTR、LPSTR和LPWSTR
' u: l# Y$ m; Y* G/ |
) G' M6 J! y" I0 _0 ~* N' d  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。, @1 \1 h. r  g9 b  D7 n

: i7 ~& [! o/ W  那么什么是BSTR、LPSTR以及LPWSTR呢?
; g5 ^6 F& R8 {) i+ w# }8 z- [4 `. {4 b: g* ~
  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
  z" x4 `4 _3 C  @3 s0 e" x+ t8 c+ G& Y6 {9 P2 z2 G; S1 O
  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
- I, Q- G9 N/ v1 u3 W0 p4 T
; F; ?7 n- B4 R4 X' ?& m" z  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。- o. _1 l$ w: ?

; o  ?4 g) Y! j* t2 g6 X  一般地,还有下列类型定义:
" N2 C- @% `0 P  w9 W7 a, O7 `
) i1 S8 ]+ K) v#ifdef UNICODE
/ `3 s: A( t) b& @1 K4 M0 e typedef LPWSTR LPTSTR;+ e% G, s' A5 V5 r! z/ D: _; i; p
 typedef LPCWSTR LPCTSTR; ) A& e" }2 N$ F( E6 v: p! N6 f
#else
0 Q9 A( t% b1 F- j* H, [' g8 T typedef LPSTR LPTSTR;
0 ]" j! h( D, S  y" o- r) \ typedef LPCSTR LPCTSTR; * }' G% Z; H8 B( }; `. D
#endif  
% A8 O3 m7 X  K* _
. N& D! ]7 z  m0 u8 Q+ P9 x+ f  二、CString、CStringA 和 CStringW
3 b- _! h& ?4 f- j$ N8 A8 J9 F4 L
3 `9 Y4 K, k# j6 V/ l  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应用程序中经常用到,这里不再重复。
% R, @9 S$ j- {
1 L9 u& p/ T, x2 f4 b9 {# {  三、VARIANT、COleVariant 和_variant_t7 F9 x, G2 ]* d; L6 P2 W

: V) ~6 h3 l# Z; Y7 E  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:! t. \1 m* c) W3 @7 G. V2 B
' j" E1 R- C- t& h5 W3 R4 J+ s
struct tagVARIANT {
  H4 Y- P* H' I9 v5 H& z VARTYPE vt;; j4 Y! y) ^9 X
 union {
2 c$ p* H$ a- {; ?. o7 l2 T  short iVal; // VT_I2.
) R8 K+ T3 B  U" D1 I" T4 _4 t) i  long lVal; // VT_I4.
# P4 ^; W4 Y+ @+ j9 x, Q# [! {" L  float fltVal; // VT_R4.- D. ]: N- p  O3 f' o+ {
  double dblVal; // VT_R8.
$ l) G2 Y7 K  I# I5 Y) c  DATE date; // VT_DATE.
: B0 X0 i4 r4 \1 ]  BSTR bstrVal; // VT_BSTR.
8 D# T$ a  T1 m# Q+ r  …
1 O2 E1 s* f9 r2 X) Z$ w5 q4 L  short * piVal; // VT_BYREF|VT_I2.$ h' I. t" L4 e+ k" o$ ]) i
  long * plVal; // VT_BYREF|VT_I4.+ Q4 O% D: t8 p9 F
  float * pfltVal; // VT_BYREF|VT_R4.6 K! Y8 C) y$ L' s" j
  double * pdblVal; // VT_BYREF|VT_R8.
: y0 C( g$ R( S9 o! k2 V  DATE * pdate; // VT_BYREF|VT_DATE.
5 a8 M7 q6 q' w' N7 Q* N2 R  _  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
( ~0 b( L' G  R; w };( t( T( M& ?7 |& J( U8 X' j
};
" |7 O+ t" @8 J. ~0 S* N+ S# d: Y
/ F- C/ o7 b, o( [# H( U  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
0 h$ Q2 {7 ^9 f) P+ ^8 Z0 E
+ c: {( z+ b% XVARIANT va;% W; A9 }1 T; l( i
:: VariantInit(&va); // 初始化6 R) Z3 J1 o# z  R
int a = 2002;' m# v& I% |0 k  \
va.vt = VT_I4; // 指明long数据类型
; P+ D5 k) v/ e: [  Z6 D1 Sva.lVal = a; // 赋值 5 G, k6 J# J5 ?, O% z
- Q+ n# C, V2 S
  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:; X! z" l# F. g- x0 i3 K# ?: A6 [

* m8 `' S3 Q6 B  VariantInit —— 将变量初始化为VT_EMPTY;1 s! h: B" L8 z4 I

3 o0 r9 n3 r; e" o7 z! I' @  VariantClear —— 消除并初始化VARIANT;
7 u; [% u2 c) Q' f* J5 D: n0 G: P! S6 s" Y
  VariantChangeType —— 改变VARIANT的类型;
/ V3 Z* Q4 V: F. i. w0 H
+ M& @# Y4 m5 k. N* O  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
' l# v. s8 c. h+ i1 d3 j. _) x) j  z3 S3 P" o9 e7 d
  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
5 {9 O; ?5 Q3 I0 l" D1 q6 l8 y
& u1 G% c. d) }9 K$ MCOleVariant v1("This is a test"); // 直接构造
2 j* L! O& u6 B4 i0 [COleVariant v2 = "This is a test";
  u1 F  Q& `0 C; R// 结果是VT_BSTR类型,值为"This is a test"/ N# S$ y2 a& r0 {% V* k
COleVariant v3((long)2002);6 m- M5 }( p9 Q$ U
COleVariant v4 = (long)2002;
, x  ]7 }# o6 X$ P4 d9 X, Z// 结果是VT_I4类型,值为2002 - x( q$ V  |  O+ e1 J2 b

6 _* A" r8 {+ {  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:/ m9 Y5 Z% h0 @  Z3 v, k2 W! ^

: F, Q2 w( D1 t  #include "comutil.h"
' [9 s% p% H0 y" p! J+ O2 @- @4 @3 L
/ R2 b; s$ n  z  g2 x  #pragma comment( lib, "comsupp.lib" ), k: @. Y9 E7 ]
5 u  ?/ l1 X. l' H3 c
  四、CComBSTR和_bstr_t1 H  I9 Z# d$ @! N& V+ B
# l8 Q5 P3 O1 U! P. w
  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
) u, n' W( z% M/ E0 I- i6 q: o! D8 o1 @! V; Y1 @+ n
CComBSTR bstr1; ! d. `5 S$ `  r& L
bstr1 = "Bye"; // 直接赋值3 y9 T5 {* P8 d- M4 _& E
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符
/ Z/ u2 H" H3 B+ {  OCComBSTR bstr2(wcslen(str)); // 定义长度为5
: b( ]( e$ `1 U" ^; J: p/ ~) Y+ a5 xwcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
+ M4 ], G8 z- ]; ?/ B; M% N& y& vCComBSTR bstr3(5, OLESTR("Hello World")); * l2 B2 H- J. j
CComBSTR bstr4(5, "Hello World");
5 U; R1 i/ h% \. H8 u" sCComBSTR bstr5(OLESTR("Hey there"));
6 Q, U$ z! ?" r5 c1 R  LCComBSTR bstr6("Hey there"); 9 s8 K! S- E' B0 U; @$ z5 ~
CComBSTR bstr7(bstr6); - A# j+ H( t! K# o/ n  c. Z' o
// 构造时复制,内容为"Hey there" ; Y3 V$ W4 N% J: `
/ |. T& k  w0 T
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
' @4 P2 l" I) ^5 V) [; \( [- q  F, {% h& V) ]: {; S* s1 y: R
  五、BSTR、char*和CString转换- g4 L, Z5 r+ Q! L: u

6 B9 P6 |; w. E& V! X  (1) char*转换成CString
9 F& @6 M5 C$ S# j3 E: A7 y. o7 d1 Q, M
  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
: A( ~8 u; |6 w' {- t* H- |' D% |$ m) Q" B8 Y, x
char chArray[] = "This is a test";- Q  W2 Q5 b3 G  W$ Q0 G
char * p = "This is a test";
$ x3 N- A2 E( D& g( H" B4 _7 M3 e" [, L$ S3 ?3 @" o
  或/ L9 s: u9 `9 W' O6 Y; {

/ }, Z0 u1 e; q2 Q2 gLPSTR p = "This is a test";
8 ^. H+ C" k  q- c3 E5 [$ S' y  Q0 [' x
! i  v  H. e# F6 d  或在已定义Unicode应的用程序中7 F: [1 I, o' [/ i

. A, L( ]$ ~3 t! v1 W* gTCHAR * p = _T("This is a test"); . K7 K& X8 m% ]) F0 o0 {
& |4 [7 `7 ]* X9 r% {+ Z$ A0 ^
  或
% d* Z, s  W# h2 r. e! h+ T, O! ~4 a3 A
LPTSTR p = _T("This is a test");
' }) M( V: t2 d! iCString theString = chArray;- k/ ]+ N+ g7 j5 r* C8 [% p
theString.Format(_T("%s"), chArray);9 r& x5 m+ w/ T7 O" L+ ^  O7 T8 ?
theString = p;
6 {( ]8 g% l' g+ v2 X" J
; V' g: c6 _7 j( ?  (2) CString转换成char*1 ]% @1 x; u( G6 `- Z, W
$ u7 O1 z5 r1 b3 S
  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:0 G2 e- t$ Q7 i% x! q

4 o8 C  v$ @3 A/ k$ T4 ]  方法一,使用强制转换。例如:
. T' I( I0 r- E# w# c6 R/ L) ^. c3 c5 e$ m' R+ l. K
CString theString( "This is a test" );8 {1 ^* ]* r; G
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  6 O* y3 S( o$ h: V1 h) Q. k
8 g/ u8 T4 S8 A  D
  方法二,使用strcpy。例如:
( u! h: W2 W! Z% `1 L3 p( w# }0 b/ A/ q. ]
CString theString( "This is a test" );
, Q( ]7 W& o1 ~/ mLPTSTR lpsz = new TCHAR[theString.GetLength()+1];# A" n5 g+ B8 r* f
_tcscpy(lpsz, theString); : |& o2 q% {4 g( W7 i/ l

  A( ]; @  ?$ K9 F) U  R' z$ O  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
; ?# t! Y4 l, _" ~' U5 M" d0 f* d$ }0 I# Q
  方法三,使用CString::GetBuffer。例如:
+ I9 R( k: g9 X4 n) {( H, n2 w3 ]/ g  ~
CString s(_T("This is a test "));
0 L9 {# Z1 @9 X6 i- RLPTSTR p = s.GetBuffer();
. O' i9 O% B4 T3 ]8 d$ W// 在这里添加使用p的代码2 |3 }7 |& b: b+ g4 `
if(p != NULL) *p = _T('\0');1 d7 y5 K& N3 Z( s) O
s.ReleaseBuffer();
% O. H' C3 E+ F$ F9 C% I// 使用完后及时释放,以便能使用其它的CString成员函数
$ g* U. K! v5 J0 Z. o
' M$ s) X3 W" ^* E0 ~# e  (3) BSTR转换成char** T! ]5 f3 {/ S3 w8 ^/ h; Y& A; [

! m6 [2 q) b; e% V3 X* z3 O0 O  方法一,使用ConvertBSTRToString。例如:
& H9 E2 u! N  G2 f( M) O2 h. q1 I+ G6 L' [; ^2 V  Y
#include " A% G7 i+ T8 X2 D
#pragma comment(lib, "comsupp.lib")
5 W1 m7 l8 e0 H3 Pint _tmain(int argc, _TCHAR* argv[]){
6 m5 K% J! B* n" f/ F7 w1 OBSTR bstrText = ::SysAllocString(L"Test");
% B% U  N7 {7 ]char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);# u/ C* \, {# ?" K* K, S
SysFreeString(bstrText); // 用完释放
2 `  V4 F) Z$ J; n' S+ |- tdelete[] lpszText2;
1 u: w  K; J, P* {: Ereturn 0;
& D# G! n, |/ m- b. \& G" R' K; o}  
/ D- j& O' O' K& ?* }+ G7 d
" b* s+ z5 Q5 v# P) Q+ z1 L: _  方法二,使用_bstr_t的赋值运算符重载。例如:3 g. x1 {2 Q+ W9 L; Q2 c
! V* ~; l7 R  P
_bstr_t b = bstrText;
, H# H( u: t. N5 ochar* lpszText2 = b; # u3 Y3 O5 F: k9 E
+ c- l/ z3 F( s* O
  (4) char*转换成BSTR
0 J! W2 b1 F- U+ T% V( r- V
! w4 E; \5 |/ P+ m  方法一,使用SysAllocString等API函数。例如:
/ {# j- e8 D5 k* ^, [+ C, e/ p" D
# P6 X8 ^# Z; z' w4 P# ZBSTR bstrText = ::SysAllocString(L"Test");
7 _5 [# N2 k4 E! }5 n& V( jBSTR bstrText = ::SysAllocStringLen(L"Test",4);
( d: b( `8 j1 b* FBSTR bstrText = ::SysAllocStringByteLen("Test",4);
  f) S( \+ }3 C+ }8 m; h9 Q; d. H1 f1 k- e8 E; j/ d. G1 I
  方法二,使用COleVariant或_variant_t。例如:. A( m7 b; P/ H

7 q2 S% h8 J1 U% Q, d& m//COleVariant strVar("This is a test");: n3 X1 e: n* k3 D, w
_variant_t strVar("This is a test");
9 h7 H  v. G% x# k+ P& Z- j+ lBSTR bstrText = strVar.bstrVal; ' k: y9 L8 r7 b6 A1 e

; t3 |( |9 W( h* B0 x0 l0 J3 D  方法三,使用_bstr_t,这是一种最简单的方法。例如:
4 O, H; y2 t  {  @4 g1 h2 H3 y0 w2 f% D5 a
BSTR bstrText = _bstr_t("This is a test");
; v- k3 p( \! p5 h: H: _. v% @- K- ]% z* ~. Z1 ~7 S+ x& k
  方法四,使用CComBSTR。例如:, v- r  V  U0 U  d+ V# Y* m  i- t, K

2 K+ U) Y9 l/ m: K* mBSTR bstrText = CComBSTR("This is a test");
1 z9 ~* i0 f. q3 C7 X' n6 d6 B% e+ i$ R6 s; l
  或& \4 R; j# x; j" }

' A: v. v: M' JCComBSTR bstr("This is a test");
) R2 X# ?2 F3 w* t9 V7 x6 g- PBSTR bstrText = bstr.m_str;
7 X. z4 o2 H* W% p2 s3 @; g
1 x* ~* D* w1 w1 U  方法五,使用ConvertStringToBSTR。例如:
1 d. q/ w/ f, r3 Q( p4 u! V4 H6 l$ R# u: v  d  v
char* lpszText = "Test";
' E8 P- ~+ p. GBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
% j5 r; j* O6 h/ X5 ^6 v! l! @* d# F/ M! B$ F
  (5) CString转换成BSTR2 K' f/ [# ~+ E! F. n* g

+ j1 r& F. `. w  通常是通过使用CStringT::AllocSysString来实现。例如:
4 ^7 r$ o6 P6 E- e: \; O4 L) H3 B; Q
CString str("This is a test");. v0 W+ v- h: `. m" n$ s5 m, g) M
BSTR bstrText = str.AllocSysString();; w4 D" i! a/ a! h/ c' m- Q

& b4 V+ a( Z0 [( C  [' p+ L9 g1 `4 ]SysFreeString(bstrText); // 用完释放  
6 E  U+ J  p$ |) Q, k4 W
4 r) M$ p& ?: m& V$ ^  (6) BSTR转换成CString, Y4 s+ e  v0 v  d
/ _( L3 R0 ~& u$ ~5 j( t2 ?
  一般可按下列方法进行:- q  R" d& t0 v8 D: ~
2 w# u. u4 a) N3 b1 D4 y, M( [4 i
BSTR bstrText = ::SysAllocString(L"Test");6 b4 o( q( A, D0 J( e
CStringA str;: J; Z3 G* c/ p7 X; s8 h7 n
str.Empty();
. x6 e; i& h5 }! G" J+ m6 Tstr = bstrText;  " E! F" O3 _4 L) w2 L+ s

: U/ b1 r8 B/ S3 C  或
" ~9 e6 W, t+ T* |0 ^1 \4 M
) e# \2 \  d  ^* {2 p& z% tCStringA str(bstrText); 9 Q3 n9 ~/ u5 @& J# k# Z/ F
  }$ `$ G& @6 M, z% K$ U  c8 O# \
  (7) ANSI、Unicode和宽字符之间的转换
/ ?! r0 \: x7 e2 z5 r0 f, {  L  X: `3 Y
  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。* {" _9 `" i3 q  Q9 `
# G" z+ U3 C% e1 s9 e: M' s( N
  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
; r2 S4 _+ k6 R3 ^- n% ?5 @- K* R+ \
TCHAR tstr[] = _T("this is a test");3 G  A% j& Z) ^, l8 s# |
wchar_t wszStr[] = L"This is a test";
+ A# H9 I" `) x" V: ^$ h1 L- zString* str = S”This is a test”;
; @9 T' M' B: f6 }4 O0 C- s# f. O/ X% |$ |8 m
  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
, I8 d, `# P, t+ D) l$ [; l( L: N
% _1 J1 r1 j1 e; H  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:1 G7 y8 T* L: ]

# y( S8 d' @" ILPTSTR tstr= CA2TEX<16>("this is a test");
- s6 K- F$ j) uLPCTSTR tcstr= CA2CT("this is a test");, D# v' M5 ^' k4 l8 |
wchar_t wszStr[] = L"This is a test";
- o6 U8 M" H: ]8 Wchar* chstr = CW2A(wszStr);  9 c1 n) A; T* ^& i; w8 B4 Z; j

+ F% X$ x3 o/ i; I! }3 Q  六、结语; _! _7 E; w. V) R. w/ r7 X6 X- H7 S& ?
4 c* H7 h$ L0 d2 n4 m0 X
  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-9-30 13:15 , Processed in 0.041541 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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