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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky; ^! Z2 o/ q/ }/ L

1 ?' P. Y( F/ ?$ R
/ _. d& u8 L  S9 o, I( s6 j* U6 r  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。3 c4 }( x8 N: e6 N

% l& ^1 @2 v, w  一、BSTR、LPSTR和LPWSTR
6 E8 H  r2 r1 ]5 N9 a' Y
+ v4 S, X3 J! ]6 I2 E. g1 V  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。
/ ]% r5 R  [3 q8 X, d) ^% }7 a$ E: W% V
  那么什么是BSTR、LPSTR以及LPWSTR呢?6 K) f0 S. i! ?" m6 s8 A; X
9 l& P/ p" I8 Q! Q6 X
  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。# J) O6 t2 h  K

, y: r6 ?; a- x3 f  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
. d8 h: B$ |. h" K: o; S/ v' q. I- d  H' u" T
  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
1 a! \& e- j) _
) Z) z) N! w7 i  一般地,还有下列类型定义:
' c- Z: C" R5 N: Z2 W4 F2 ]3 Y( J* Q/ h' m6 F
#ifdef UNICODE ! q$ l8 |6 m6 E& M
 typedef LPWSTR LPTSTR;
0 T% K- l6 H- F: {- E% i typedef LPCWSTR LPCTSTR;
% u1 \- F; ^9 e8 m8 |#else ) M/ L8 P" }; b. A: b
 typedef LPSTR LPTSTR; - l  c9 @4 @/ W7 K7 i& {
 typedef LPCSTR LPCTSTR;
) c" y8 y& b, C; X; M1 s. Q1 q) f% \#endif  + m! L* I; q; \0 J) U9 v; U; Z% r1 ^8 _0 ]
+ a+ j* X6 s% i! |$ k
  二、CString、CStringA 和 CStringW+ E4 r' A+ z- |! S1 V

$ c2 E0 [/ T8 R. M  _  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应用程序中经常用到,这里不再重复。
% o: Q8 o, t4 b& z" j4 {. ^; ~5 E8 h5 @" x! [5 w" m
  三、VARIANT、COleVariant 和_variant_t# \: L) h: w3 \" Z/ x

/ j0 L8 T2 p, m% K( ~6 ]  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
: P. A; G( c4 ?9 s% d
1 ?5 [5 L& V2 V/ C/ hstruct tagVARIANT {. T# Q; }& l8 v% ]  T
 VARTYPE vt;3 E7 {0 t$ c) i& j* V! i# S: o) s
 union {
/ k/ E) L$ u5 G6 g3 q3 s) i  short iVal; // VT_I2.
# k  {5 D# C4 R8 o7 w  long lVal; // VT_I4.* X; n, X/ N3 O* a" a
  float fltVal; // VT_R4.
$ Q0 O$ O) E9 y3 z/ r) s8 o7 P/ A0 P  double dblVal; // VT_R8.! F$ R- ~8 e* a& W
  DATE date; // VT_DATE.& \; h  T" C7 t4 _! W
  BSTR bstrVal; // VT_BSTR.
" W8 A- T2 D0 u$ U- G% E" t  …+ _; C  s" o. G* H# ~
  short * piVal; // VT_BYREF|VT_I2.. p5 n4 o8 k" h# v) M
  long * plVal; // VT_BYREF|VT_I4.
# D& t! W' q3 V" {  float * pfltVal; // VT_BYREF|VT_R4.
2 N* |, Q0 p* Z0 O) l% i  double * pdblVal; // VT_BYREF|VT_R8.' P3 D9 C8 I2 b( e' R( E2 J( ~
  DATE * pdate; // VT_BYREF|VT_DATE.
" a5 s1 w& z8 I% Y  BSTR * pbstrVal; // VT_BYREF|VT_BSTR./ M1 Y6 ]+ m) I3 T; C* w7 H5 e/ {
 };# }* o/ ~5 j; t" E1 T+ ?
};
4 m7 d; ]7 m5 {  l: w
: \# P& P8 M+ r  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:; V7 ~* ]8 K( S( N  v# Z! G
& @/ s, {4 f0 B; p6 O% m% r5 t: D
VARIANT va;
, \4 j" X# L. ]+ r! k- c4 U:: VariantInit(&va); // 初始化
& h9 S% Y. p/ Z" E# O# Y* N9 pint a = 2002;
4 x) D9 f' S3 C- Q8 i0 |va.vt = VT_I4; // 指明long数据类型
( b* e0 X/ _% K- X, c5 S$ Z2 Uva.lVal = a; // 赋值 3 [4 Q. y& c' _
2 ~; N$ q! d$ t
  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:. X, m$ y  @. B0 R

% f! {3 B9 c- H' q  VariantInit —— 将变量初始化为VT_EMPTY;: y4 c" e4 C- I0 x# f5 W/ |
" U" [% ?8 r  |" ^8 @' H
  VariantClear —— 消除并初始化VARIANT;! r% m% v! P) G6 n
* B2 M6 P+ ?, I: k8 j  Y9 l
  VariantChangeType —— 改变VARIANT的类型;
3 q! X; |/ x3 g; i) B4 R- p8 j% Z( T
  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。/ V1 @( [# Z" M+ A" {: k

% A# h% i& O# J/ k& o  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:
) e1 [$ ~& {1 C4 F
% ]% a9 x1 r* m0 hCOleVariant v1("This is a test"); // 直接构造7 C9 |( q6 ^# q% B
COleVariant v2 = "This is a test";
8 o$ I6 {1 v0 V// 结果是VT_BSTR类型,值为"This is a test"
0 P; _3 z; Y$ {COleVariant v3((long)2002);6 d3 P: ?: Z# ]7 M
COleVariant v4 = (long)2002;7 i3 `% t) Y# V% {5 @3 ^
// 结果是VT_I4类型,值为2002
5 F6 V3 [  `' I: }
6 b, a4 u, ^5 X6 [" k- P  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:
( {; Z" i- ~8 L- G4 X9 T5 p$ w3 i1 S
  #include "comutil.h"
4 B  d; _0 h3 l
* p# W9 k+ M7 K. a) i: C; f4 Z  #pragma comment( lib, "comsupp.lib" )7 p* s. b: x! j, C5 [" c, Q6 }

& T4 F% C: {, A+ P  四、CComBSTR和_bstr_t, ]* D' k/ Y4 C: o0 v1 F

8 ]% X8 I0 G* J" Q# }  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:4 }+ c# ?6 y0 ]& e1 a

! _1 O" Y# o3 v$ M4 z' mCComBSTR bstr1;
: W* F- k/ ?# I% p$ @5 u' lbstr1 = "Bye"; // 直接赋值) r9 W+ a) Z! k
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符% ~" B+ Z3 d8 |; _6 N
CComBSTR bstr2(wcslen(str)); // 定义长度为5
7 N' B/ J/ _3 w6 T0 P4 ywcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
1 {# S& K! Z  {  |: H( r7 u2 Y0 JCComBSTR bstr3(5, OLESTR("Hello World")); 3 O$ q% K5 k5 R, `6 b0 a
CComBSTR bstr4(5, "Hello World"); 9 m9 E4 K  L- a) W7 {
CComBSTR bstr5(OLESTR("Hey there"));
1 l' I/ A6 i/ |* B2 Q- JCComBSTR bstr6("Hey there");
* b$ J2 f5 E8 [CComBSTR bstr7(bstr6);
! t( [! C- I- X: ~// 构造时复制,内容为"Hey there" - n# E' e8 [0 f( I
0 b6 H4 f. ^% t5 p
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。: I* {7 v: x) X0 A

0 L0 A# K. k- U5 ~8 K, @( w  五、BSTR、char*和CString转换" d) [9 z' I5 Y& o9 K9 Y

0 F" ^/ z7 O5 s  (1) char*转换成CString
/ O8 Q3 j) ~) j" D$ P8 Q
$ J8 {* N2 g( l* {6 q/ M6 z  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:/ s  H3 W  Y/ z7 \6 Q; A. M1 }7 N

: n# f  b0 Y( `% B  e3 ochar chArray[] = "This is a test";
, P1 J- r8 H! V: @' o0 achar * p = "This is a test";
/ s# W+ m: |4 P  R5 R9 c: S. b
  R. {2 u; m; f  D: i  或
' a( E3 D5 b& e1 \* M( N: n3 h; Z5 Z! G& u( I: y0 i5 _& I" K
LPSTR p = "This is a test";
: V* s. ^: U& M9 K$ B+ y
+ W3 J2 c/ T" K5 z  或在已定义Unicode应的用程序中
8 o6 r) C; N" k/ m  M
; M: @: f1 e$ R2 ETCHAR * p = _T("This is a test"); - t, v2 {' ^& E$ Q0 y6 P/ g

) n6 Q3 C7 J" e4 N8 U  或% E  s; p$ G6 ~; L
: ^  ~0 X  b5 Z! z
LPTSTR p = _T("This is a test");1 _& M. q3 ~. G5 Y+ \* X
CString theString = chArray;+ `: U6 i2 ~6 k
theString.Format(_T("%s"), chArray);
5 W" p6 Z, V- @: w& v/ TtheString = p;
6 p9 x" Y+ @- {  T1 W/ M( Q0 q1 q' T  b
  (2) CString转换成char*
2 r7 C' i  s: M
3 y- Z) P9 B( M5 Y1 A  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:; Q% @) D& S% V

' l# y5 i( ?& c! K% n. e  方法一,使用强制转换。例如:3 b$ s3 i# T7 X4 H! G  o

7 V$ g  E; L# L3 Y( j2 Y& VCString theString( "This is a test" );
! n& I- Z0 ]1 H$ X: i" Y( ]; h$ FLPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  
* x0 q/ b  r3 V0 R0 K. f
, L9 A& W+ |1 t& @$ ^% a/ |4 e  方法二,使用strcpy。例如:4 {! \4 J) `/ X' K! v- _3 o' g7 n. h
  ~; T5 M$ E1 t
CString theString( "This is a test" );+ i8 Q% [3 }0 g  v
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];( O) b7 c. n: f% S/ o
_tcscpy(lpsz, theString);
6 m, U; T  z% ]0 y' H. K( l) ~$ s( c) ?$ b; c& e7 v
  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。8 i! t3 P2 O1 t, `
0 E. W( y. F7 x" m5 b+ Z$ l
  方法三,使用CString::GetBuffer。例如:1 q5 _& [- }- ^

0 [- F3 o0 l: ?  A( R* \CString s(_T("This is a test "));8 m0 ^+ }, ]$ E  c* U
LPTSTR p = s.GetBuffer();
3 d: q$ C5 g, G5 ~# @6 z- B! t7 |- D// 在这里添加使用p的代码9 ^. g: `/ X; X- r0 `) N; \  g
if(p != NULL) *p = _T('\0');
8 @2 ?2 E( A7 \! |9 i2 g0 ^s.ReleaseBuffer(); 8 v6 u# L0 m! E# K& F
// 使用完后及时释放,以便能使用其它的CString成员函数
8 Z. B! A% F* P! T: t
8 V& @& g. R) x# P+ X  (3) BSTR转换成char*1 ~6 j6 {  }% ?6 O! R" }

: h2 D* a: ~; V  f6 ]) Y  方法一,使用ConvertBSTRToString。例如:8 S4 u2 i+ L$ y. y7 P" U) ]
" `4 p0 w7 I  K% X' {3 C$ d
#include
6 Y2 X: H' q, q! G' ]#pragma comment(lib, "comsupp.lib")
5 s0 J0 r) O8 p% S5 r& Y5 R  ~, kint _tmain(int argc, _TCHAR* argv[]){
3 _( G/ T7 u! g8 G- ^% G" [$ }! OBSTR bstrText = ::SysAllocString(L"Test");
. j( E; u$ V2 f) V2 K+ ochar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
( g, l, W0 t; q* e9 B# DSysFreeString(bstrText); // 用完释放1 R  L4 i( s0 [7 K( r) P
delete[] lpszText2;( V: F7 u" j9 ?; z" P5 X  H$ |2 O
return 0;
1 b% d$ D/ k9 X! A9 d6 Q+ e# V}  " K( _, M8 S/ D/ ?6 [
$ K2 u: b$ b0 ?( ]
  方法二,使用_bstr_t的赋值运算符重载。例如:" d- {" C  K& R! A: R, W  S4 T: \

- K4 o7 ~  ^- c7 T0 U. I_bstr_t b = bstrText;
4 d8 O+ ~1 b6 ]1 c, h. X/ A% achar* lpszText2 = b; 8 _1 N. s& [+ U* m# T0 Q
! l: F* e( Y2 f0 P. U- p
  (4) char*转换成BSTR
& V& D* U# l" n! T) V
4 ?2 |9 Y6 A% L8 E% w, f  方法一,使用SysAllocString等API函数。例如:. d1 z  x# e- b8 s" m& N

% x5 q% H5 ]$ Z, pBSTR bstrText = ::SysAllocString(L"Test");
! m0 Q, X/ ^+ _. b( m# zBSTR bstrText = ::SysAllocStringLen(L"Test",4);
) x* f) `! a3 t4 Z7 F4 PBSTR bstrText = ::SysAllocStringByteLen("Test",4);
  m! Y* I% p0 O2 f2 k% A4 s' Y0 a+ e; R- K. D
  方法二,使用COleVariant或_variant_t。例如:- Z" i& @# o  Q5 y9 t( I) U' R, u" n
9 ]5 R; C! |6 n5 H; g
//COleVariant strVar("This is a test");8 a+ L% N% W' g& O3 v$ J
_variant_t strVar("This is a test");" J% F8 z: |4 `0 \4 g
BSTR bstrText = strVar.bstrVal;
6 l9 e  `% `- Q! A. g: }& n
" p9 Q; h! p# b0 b* z  方法三,使用_bstr_t,这是一种最简单的方法。例如:; i) a7 `' ^  R1 x, z- ~0 f# r

6 a& j' b0 I! `: P/ W; }' v2 [BSTR bstrText = _bstr_t("This is a test");
1 p% t9 J9 q* E! B: q0 D* h4 K# f
  方法四,使用CComBSTR。例如:
( v! _4 n$ E% X5 @+ ~% n0 m- M/ q4 P  L
BSTR bstrText = CComBSTR("This is a test"); $ A& c! v1 k4 A9 `+ M) x

; X; f( O( q- g& A" J# A' A  或
4 d3 A# E: u/ ^- ^9 J$ n& X! ]0 l2 Q" `: n( u
CComBSTR bstr("This is a test");
3 p' q7 K6 H9 b8 E! Q$ ^BSTR bstrText = bstr.m_str; 5 z3 o7 L  Z( z! J) ?3 {+ M$ J, Y- `
: p# e: k/ N6 B$ M3 ^
  方法五,使用ConvertStringToBSTR。例如:& t# t- |1 c! }$ I4 z. O
7 n. T3 b1 g7 {
char* lpszText = "Test";- ?7 g# d' I6 {, m4 N
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); % D+ y4 B8 c  s
7 A+ h# R0 U# P( E: X  S
  (5) CString转换成BSTR
7 y5 k. B: B. i4 s; C% ~8 L$ w% M% p- c; S5 U* s  j+ N/ u
  通常是通过使用CStringT::AllocSysString来实现。例如:
9 i) m( P; X8 J; O* L" M
, g5 s6 f# H5 \) V& Y* w) n: u3 U$ b1 qCString str("This is a test");( z: c- Y' \  G) y
BSTR bstrText = str.AllocSysString();- s# \* h% ~7 H. M, I! `; F+ k7 \

" F3 A# d4 c3 a+ J+ N) qSysFreeString(bstrText); // 用完释放  , W0 X8 w' V' \! V0 N1 [7 o% P
8 r: q7 e7 T/ O" Y
  (6) BSTR转换成CString- O7 G( S2 s2 A( T- I2 Y
" O3 Z0 ?1 H) F/ _. z6 A  j
  一般可按下列方法进行:
; i+ B7 U5 x, `# d' h' z9 |4 m) ^! b) ]7 p1 u+ G
BSTR bstrText = ::SysAllocString(L"Test");0 Q6 c# y- i( V& c
CStringA str;
- f/ U, \, x: D; z$ Sstr.Empty();+ R; ~# _/ i2 i; _! g9 _2 E* e
str = bstrText;  
9 v  \# ]) M1 \5 I/ ^1 e: @% R" W3 k( u
, [9 A( L1 ^% ~" A( b$ J  或
- C2 Q8 o/ i3 G' d
+ V) m; C7 q& \7 P, L9 HCStringA str(bstrText);
* ^/ [. ]( l" X3 G$ @7 T* Q5 L
, z. s! _& U7 x& \7 R! X1 ^  (7) ANSI、Unicode和宽字符之间的转换. H8 Y. H# f2 S, N2 w) E/ _
* i# k  D1 x& y# {" U( H. e. w  |
  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。  c/ U. C+ Q% c7 z0 g

7 {- ?, ?: S/ a! m, Z* O  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:
% B2 W/ ~+ J8 H6 H/ [+ i5 A. G+ t+ t
TCHAR tstr[] = _T("this is a test");
# }' v4 E1 O; N+ k. Z# vwchar_t wszStr[] = L"This is a test";$ M6 s# M6 G) S: w4 e+ h1 l
String* str = S”This is a test”; 0 v( V* d7 Z' F3 H
7 u% q* P# o2 R+ f5 S# E8 U% U
  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:1 F0 u( y$ }( U! A

/ m$ R/ c; }+ W* V6 X  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
8 ^9 B: j0 H- ~! {8 l6 B4 _8 n% |. T& \, a) ~
LPTSTR tstr= CA2TEX<16>("this is a test");1 m+ ]3 H1 q: L% [
LPCTSTR tcstr= CA2CT("this is a test");% t0 Q0 [  W3 e
wchar_t wszStr[] = L"This is a test";( `) e( `( f! a' s
char* chstr = CW2A(wszStr);  
7 ^! b. @0 E* Y6 J* L( m0 j( N+ ]5 j/ ^# P" w
  六、结语
) K# F9 s6 e8 |' q- v* \; w( S3 R0 [; ~+ z1 e9 y/ E8 \
  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-5-2 08:58 , Processed in 0.019673 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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