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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky, x! K- C/ {# S' w$ L- w$ C

8 `* d) Z4 L; m# }* z$ F9 {9 ~$ y
! _$ Z8 f0 ~3 @7 ]7 d/ u  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
1 n" p0 W% e3 b  W* \
  x' Y5 g& g% c1 \! u1 m  一、BSTR、LPSTR和LPWSTR( {+ D2 j  A3 A0 ]5 `! _0 b

7 U7 [+ r1 n. q& b# I: J  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
: U, a- P5 ?% I! E6 c$ h% s1 v5 U3 r$ Y3 p$ b
  那么什么是BSTR、LPSTR以及LPWSTR呢?
3 J4 [; }3 I, Y8 Q; V' a5 J: _" p4 ~! _  C$ e+ U( b
  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
+ i) ~1 B6 y6 z, ~5 C* m" E
2 S$ {& f1 i) u+ _! [! c) B$ ~  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。$ v! Y' v7 s9 Q$ H8 v6 H
- A3 f$ L- Z/ V* G
  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
# Q: |+ K+ u+ `' }, z& I7 `3 H4 w! e3 L9 _5 z' W+ W
  一般地,还有下列类型定义:1 S4 g6 l& v% K! Y* m0 c

9 u* a9 e, c0 J1 n#ifdef UNICODE ' W. U* S; r' t3 f; u( h; g5 V
 typedef LPWSTR LPTSTR;$ F, j/ g+ h) I2 c2 W- m
 typedef LPCWSTR LPCTSTR;
" K$ F8 q3 q0 u) J#else
! |6 g% |0 m( m typedef LPSTR LPTSTR; 7 b1 N% K( t; e. {2 l8 U
 typedef LPCSTR LPCTSTR; $ q4 A  ~' }! F9 Q( w
#endif  & y' o7 m+ L- A: w6 J# K3 \
0 U/ [6 N% f0 }0 A4 v  B! \  R6 r
  二、CString、CStringA 和 CStringW
- z% z4 r! ?* A* F, b3 M4 }! I) j; l9 c7 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应用程序中经常用到,这里不再重复。
  K. `) _0 _, R) Q- E' |# B, O
: D% n' A" v) k1 b% w% f  J  三、VARIANT、COleVariant 和_variant_t, l4 B1 n% L+ T0 ^& W/ X, c
  f4 N% J( V4 T5 \# o, A6 a
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:8 J! s$ Q% |0 f' p# c  E% D

% @, n+ Y' g5 `6 cstruct tagVARIANT {
. g/ }& Z8 C0 y7 T, ]5 b VARTYPE vt;
8 o0 Z) C2 f6 \7 |+ ~ union {# f& L4 k+ J8 P+ I& V
  short iVal; // VT_I2.
# H8 O; s+ _( L0 p  long lVal; // VT_I4.
- b' t* t4 o5 W  float fltVal; // VT_R4.
7 ^( h% P) t0 M: O2 B  k6 ]  double dblVal; // VT_R8.6 \. W* u+ }8 C) r& W# G
  DATE date; // VT_DATE.
8 _2 K  ]8 R: U  BSTR bstrVal; // VT_BSTR.
  s, Y% ]" w- s0 [1 B" V5 R2 k: Z  …% b" D6 x% y/ H" [/ o3 I
  short * piVal; // VT_BYREF|VT_I2.8 l' j& [. G: Q
  long * plVal; // VT_BYREF|VT_I4.$ }! R, ~- D  r; a4 v: {) @
  float * pfltVal; // VT_BYREF|VT_R4., w8 [' s, @% a
  double * pdblVal; // VT_BYREF|VT_R8.
8 r: b' _1 I. m5 I6 _  g* H7 O5 G  DATE * pdate; // VT_BYREF|VT_DATE.
& n7 `5 A3 V1 L( l  BSTR * pbstrVal; // VT_BYREF|VT_BSTR./ q1 x! _1 V. U' q& N6 w
 };
/ ?1 x/ ~. o0 L$ `+ A};
4 I! A  I# B7 f+ W# g  \" ?! B; ?" \
1 b) H2 q- K% }, `7 Y; s  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
% A- K" c# H0 I* ^$ H4 w9 D, P- R! v( L* Q
VARIANT va;
% I0 y- C2 m# r:: VariantInit(&va); // 初始化, V9 |) Q4 O. J3 Q
int a = 2002;
& p  ~9 j7 s5 w! w, E: `. Jva.vt = VT_I4; // 指明long数据类型
+ Z7 }$ C7 e( `$ _6 o. zva.lVal = a; // 赋值 & S$ C% ~. A& u+ i: S# S
0 c. S4 q, D2 i( _) h4 @" I6 E
  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:
! q& Y5 k; C' C. @7 V, m3 w' E' x" L4 y
, G+ O6 U: N0 I( {& j$ S5 A  VariantInit —— 将变量初始化为VT_EMPTY;
( E# L" e1 Z% U$ X
, m- o/ ^+ j! k! u, ]3 B  d  VariantClear —— 消除并初始化VARIANT;
2 W( s+ @  L% w. r, N% |0 y
7 |3 J2 o$ ~8 M4 ~; k  VariantChangeType —— 改变VARIANT的类型;
' W! v" ]4 I/ l0 q1 \; d3 U- W6 j- s- J; M: b6 ^
  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。/ [/ \. ]# Q1 k! Z
6 b6 i7 d2 q6 `, J3 V
  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
' w! N- B* H; T+ o
' U8 b& e5 Z( o) E  [7 \4 X+ E) OCOleVariant v1("This is a test"); // 直接构造
& |. e  ]- p8 x# {0 q2 E$ QCOleVariant v2 = "This is a test";
9 w! U, U. |% f// 结果是VT_BSTR类型,值为"This is a test"
6 x, v/ G+ B" ?# ?/ L- _  cCOleVariant v3((long)2002);
4 ]3 y$ i& D8 J: a' j$ ^' K) DCOleVariant v4 = (long)2002;
+ [7 x/ z2 e  r' W// 结果是VT_I4类型,值为2002
$ b0 e  w+ D, w  C7 X, x9 J; k- T% d' ?
  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:2 t. W/ O* T4 }
/ ~4 \( u- G+ {5 j# i( q- d- g
  #include "comutil.h"
/ W7 K, F9 b1 X9 p! Z4 i
- d# @; L, h# p. d9 O! `  t  #pragma comment( lib, "comsupp.lib" )
: a( c4 ?: W, S2 x$ g) v9 @+ Y0 t$ ]' U. J
  四、CComBSTR和_bstr_t+ v* J2 P+ }  |% ]- L

' U2 }0 L9 E: X  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
, k2 t; q9 W! C5 Z0 M% I" O' w+ T# T& l0 p5 z
CComBSTR bstr1;
. Z  i  s/ p" e) W' _. s% m& ebstr1 = "Bye"; // 直接赋值
* k& K: c+ R0 e! \* \) A2 }  ?OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符7 k: I: R6 F) q, o, |% _
CComBSTR bstr2(wcslen(str)); // 定义长度为5
. h1 D* i: \: |  Z/ z; L' R* owcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
+ K, p2 _) M2 p$ h, w% V  [CComBSTR bstr3(5, OLESTR("Hello World"));
5 x9 ~0 p5 z$ cCComBSTR bstr4(5, "Hello World");
' g- u; U( B7 G$ l! y- \9 \CComBSTR bstr5(OLESTR("Hey there")); 5 }. W; R  G9 P3 [: T/ u
CComBSTR bstr6("Hey there");
4 K2 Q; n% g& i1 A" mCComBSTR bstr7(bstr6); " w; A0 P7 z! j  Y- i. a0 H1 [
// 构造时复制,内容为"Hey there" 1 z2 y5 s# F  Q) r

. _; V" C1 i3 P  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。
5 Z1 ^( s8 Y7 W) F2 [& d3 q3 x% R3 u1 N. }( b
  五、BSTR、char*和CString转换) I" S" D+ d4 `+ D/ {4 y

  l0 T  m8 D2 n' W  (1) char*转换成CString
6 i+ b4 P, G- f0 X2 k0 Y. ~3 c  p% Z
; O3 H4 u4 r( g' i7 X4 {! P  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:, B$ b0 O; L! {

/ V5 }) A$ r5 e- n4 O+ G1 H' gchar chArray[] = "This is a test";
+ j" u" V4 `" d/ C$ Kchar * p = "This is a test";
& q8 O- [& n1 H+ Q. I1 I5 j6 K4 e/ q! h. b: |3 g- k& B
  或2 F, r/ U" u! ~- G4 \+ u; s+ F3 j

+ q$ N1 C. ^6 ?# a0 K! ZLPSTR p = "This is a test";
2 U) p( T- n* H& Q/ g% e/ K  Q" }4 |
' z# ]! d8 k9 q- `  或在已定义Unicode应的用程序中/ P; y2 A  B" U
4 X# Q+ a# l; @4 Z6 p' {; a
TCHAR * p = _T("This is a test");
( T" W) J5 m3 d6 W; P2 H2 q! W5 |9 E! k/ v' m
  或: T  |: f3 }+ {6 A3 E' V2 t, j
! x$ M! }, f+ B! c
LPTSTR p = _T("This is a test");# ^( }4 n" i7 \& f1 ~7 ?6 h
CString theString = chArray;
" g: P! ^$ a# x2 i* j/ A/ PtheString.Format(_T("%s"), chArray);; M9 ]3 x$ T9 ?. r
theString = p;
( Z4 R3 G3 U5 k1 A& P: |1 ~# `2 c1 y
  (2) CString转换成char*) L1 G0 W( x, l" y

) T5 l% c5 t- L1 b  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
% R) i- x$ ~* P! ^
# {0 f1 y" p! V2 `  方法一,使用强制转换。例如:
8 ^: Z" {1 E0 v7 _+ s# ?. m  b' M1 l9 F1 I
CString theString( "This is a test" );9 `% j/ J; d1 j9 t6 ^0 }; j
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  
4 z# X; v% G# Z0 S( _: e4 Y
' o9 [3 A: V: B! l- A  方法二,使用strcpy。例如:* c; t! W( ^; {; V4 E" k( f6 k) Q6 I
1 v9 f6 s5 u' O$ K2 Y
CString theString( "This is a test" );9 C( b! K; c0 e$ y2 A, t; f* a
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
( A7 G- R% v% R5 W+ b_tcscpy(lpsz, theString); 0 @% |5 F' _: s9 |- o
4 B1 x6 Y) c+ o7 b
  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。
' `6 v3 b7 q6 m8 P* }6 K/ f) w
% E! Z) a  I, ~6 x  方法三,使用CString::GetBuffer。例如:
8 t! O6 m* ], R5 ?! @
1 y! E* F0 ]" ^) g( u4 d% lCString s(_T("This is a test "));
, L5 J: B) P1 m0 Z& u4 lLPTSTR p = s.GetBuffer();
) Z' e+ r! \0 z, j// 在这里添加使用p的代码
" n+ f( h9 `5 S0 M8 l( v" T# wif(p != NULL) *p = _T('\0');/ w2 _$ f0 V. j7 ]
s.ReleaseBuffer(); 0 z* K; k% O- b3 f) f2 y
// 使用完后及时释放,以便能使用其它的CString成员函数 ! B( z+ }/ N6 y2 u  j

& d$ o+ O5 j! t$ n% p1 j) G+ O  (3) BSTR转换成char*  m" @3 I& ^  h4 n' G! d) F0 m

, j+ u, {) _! R( c9 a8 y  方法一,使用ConvertBSTRToString。例如:% S0 t' Z) Q; m1 [2 i
3 D' n" n' K: g# {6 N
#include
) Y, s  |, \9 L% m  J! B#pragma comment(lib, "comsupp.lib")4 Y1 C+ J! X2 G6 [$ @7 w
int _tmain(int argc, _TCHAR* argv[]){% d0 f: z$ v, c- C' ], G3 y
BSTR bstrText = ::SysAllocString(L"Test");8 A" f7 r/ _. H3 w8 y2 f. M9 Q
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
0 n- L7 H% w1 c" K4 MSysFreeString(bstrText); // 用完释放
" a( }; }7 M0 h. zdelete[] lpszText2;
8 \0 x  o4 Z# h2 r; G7 d- T3 kreturn 0;. L9 r4 H7 l" n' ^% i+ g
}  
4 \# c2 M9 J" l5 o8 W
2 l# T! ~1 n. n5 E  方法二,使用_bstr_t的赋值运算符重载。例如:. M8 A  N: I( x3 z1 e" d+ j% m7 [) o0 j

( a3 V2 r# i$ m# V" E! h* c_bstr_t b = bstrText;( D! O4 k: ^! d# a
char* lpszText2 = b; " G1 q/ o2 ?  S( a

6 G$ }. |: ?1 z  (4) char*转换成BSTR5 r: Z% o- _5 ]7 S; j' {  x
3 P# n6 `' P2 U2 B- Y7 E
  方法一,使用SysAllocString等API函数。例如:  B& b7 q" y8 L: @. [& J

8 T& W; v- w! u' T! |! QBSTR bstrText = ::SysAllocString(L"Test");
9 {( G  ~; q: ?0 ^2 h; R/ GBSTR bstrText = ::SysAllocStringLen(L"Test",4);
' @) b5 B  s. EBSTR bstrText = ::SysAllocStringByteLen("Test",4); % G( V5 J2 D1 C6 y

/ j; j/ @$ N8 S1 e  方法二,使用COleVariant或_variant_t。例如:
: ]9 _6 ?+ n8 T% C& R* b; j9 c* Y: q1 m
//COleVariant strVar("This is a test");7 H9 G$ I/ \8 R. T( d
_variant_t strVar("This is a test");) F" w5 |6 _6 }  f; {9 N( B. E
BSTR bstrText = strVar.bstrVal;
# n$ \$ X/ t( p  F2 G! ]. o1 q
( I/ K% w2 K7 u. C2 u2 k  方法三,使用_bstr_t,这是一种最简单的方法。例如:
- x2 @* w) P# O  l8 e1 h9 B' p% c: C( [4 M4 b% X5 S3 l4 e; F
BSTR bstrText = _bstr_t("This is a test"); 6 F. F- }" b/ H  O# U7 {+ b& p/ _9 ]: U
8 j, o# }. h4 r0 o3 _
  方法四,使用CComBSTR。例如:* ^' b* S- w% y: [0 _9 S

  r  Q& t" N, R8 R% \BSTR bstrText = CComBSTR("This is a test"); 4 a8 f% T7 @5 F2 P% d
" A5 l8 J$ B$ E  b* ]
  或( A& l: G# K% \0 @0 _- ~( M' h2 F# A
1 m3 i5 W6 y( G2 O3 C2 K
CComBSTR bstr("This is a test");. H+ B2 W4 M6 B. n% G, k& `5 f
BSTR bstrText = bstr.m_str;
6 `+ X/ ^0 C& \% D2 ?+ E1 M% C% \$ `1 ~+ |
  方法五,使用ConvertStringToBSTR。例如:6 L' Y4 F1 q, R6 t+ J* p9 t
2 N  W8 O# [6 E: j" T; m* E8 w
char* lpszText = "Test";
6 {! {( L/ x& L; y1 \& TBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
/ J) t, C1 s/ x3 q" u
9 F, L& y; Y5 E8 ~+ s7 \$ U7 b4 Q  (5) CString转换成BSTR
. l/ w  ~! x/ q" q( n% t  C
8 L9 C( [' E* u. L/ b3 l0 y# i: A  通常是通过使用CStringT::AllocSysString来实现。例如:+ z/ z3 @7 O9 F: T

+ w' G! P1 V' J2 S# zCString str("This is a test");
/ ~% V; `1 L8 n1 lBSTR bstrText = str.AllocSysString();! ?8 A/ X1 a# ^; f

: Y7 |2 J, L1 V8 _2 {5 `SysFreeString(bstrText); // 用完释放  6 i: j) u. E& L% h9 j0 l) {

( w/ R/ A6 }- O' R& H2 o* C  (6) BSTR转换成CString
8 T/ W0 D; f% {& z
0 C( t# k  ]% P  一般可按下列方法进行:2 L8 ?6 b* L$ b4 x9 C* e, n

1 G9 \7 Y- T; g" G+ ^3 d- ^BSTR bstrText = ::SysAllocString(L"Test");) ~! L0 S+ S! g: y# ~' o
CStringA str;4 L. E  G, x3 J/ ]3 T4 H
str.Empty();0 f4 J, _+ V, m: ^: k  t! u
str = bstrText;  
7 k9 S7 m8 w' y$ ?6 O) K
4 ~4 V- Q) @% T' R2 l  或& ?; ]1 h/ [0 F+ h5 _. a4 p( |, V
. A9 c3 J: g0 l& t& @; i; p
CStringA str(bstrText);
' I3 d+ I( E7 P6 v. I1 a: a! C" W; I0 j! o+ E& w! l
  (7) ANSI、Unicode和宽字符之间的转换
9 A6 z" ^6 |! k+ g4 S9 @. g0 Z* d( ~. Q
  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。/ A! ?7 t& J- `( t! n

6 p) k4 s" V; z/ W7 e  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:! F1 A/ A- r$ W$ V6 @/ U; P
  F: U. g. P2 L' f0 a7 v
TCHAR tstr[] = _T("this is a test");
7 g( s6 S; c/ i( S5 _. Gwchar_t wszStr[] = L"This is a test";
7 o' Z  p6 X# Q1 g1 l' MString* str = S”This is a test”; 5 p$ d* y( ~2 N6 W4 k$ G+ @; v
9 {" B+ s0 T  e. `+ M
  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:  }# X# p7 A7 Q0 n& e

! ~3 b. p! ?& Q  k  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
! D$ F! u1 R- T0 Z" D8 Z3 e, n9 f! I  T3 @3 T& ]3 m
LPTSTR tstr= CA2TEX<16>("this is a test");1 }9 A- Q- t7 w5 I! @- W" J
LPCTSTR tcstr= CA2CT("this is a test");
# \# u6 Z! {5 ~wchar_t wszStr[] = L"This is a test";
6 v8 e% D1 L6 Fchar* chstr = CW2A(wszStr);  
4 b$ T. O2 d  l1 t% A1 O! f+ [$ i
  六、结语3 t  n" z3 x$ g: u' m$ v

: q* @( C# R5 D7 l6 `& L+ w/ A  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-5-2 10:20 , Processed in 0.020683 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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