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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky- W# n  b! |7 B( }( K

2 Z, R- c6 G& b: y
$ |( Q+ O) `4 j  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。
+ O9 f2 D9 x+ _; O. l- ]
- o- ?- B& x$ F- b  一、BSTR、LPSTR和LPWSTR$ {, P$ q$ U' Q$ `8 D8 F# E

; C1 s( s) J# b2 [$ ~) X! Q  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。+ i- ?  ]9 ?4 N: K

  T. w$ q/ o8 S* Q& }3 {" Y  那么什么是BSTR、LPSTR以及LPWSTR呢?
- c" {2 B7 H4 s. b' y" u
2 H9 [: e6 r) f/ L& \! d  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。
- g9 J; F- X* s' R- k
% ~7 I) y3 ?6 g% d" R7 o' d2 ]  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。- S& K+ T& D  s

( S# p8 _$ Z! e2 h- F  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。
! T$ X- b$ y! }( X0 ^
+ e) i" X* g) Q+ j( h3 {8 B  一般地,还有下列类型定义:
  ]3 k4 w6 C8 n3 a# X
4 {0 W9 Z+ D7 B/ |. H: I#ifdef UNICODE
( W4 t9 y. c0 D* v/ a8 H) k2 D typedef LPWSTR LPTSTR;5 b# M6 ?( t$ A! p2 H
 typedef LPCWSTR LPCTSTR;
, N6 m  Y& q( U; L6 \0 }+ h#else
1 n. n6 J7 G# m6 i typedef LPSTR LPTSTR; ( m# M2 D# L7 [% x8 t- u6 Y1 I0 `7 ~
 typedef LPCSTR LPCTSTR;
( }3 t- X" q  x) z3 X% d# Q#endif  
6 u' \5 ?6 u) \3 z( o6 z5 d  Q4 m. S6 o+ E: P' s1 Q4 o0 c
  二、CString、CStringA 和 CStringW3 P, R* b% W* Z9 }1 x6 d' p
% j( F+ \* A8 F8 Y1 J- A/ V
  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应用程序中经常用到,这里不再重复。
& t2 {9 I/ t7 S7 z: O# X1 t1 u0 b/ \  g. Z& e. [* y5 a
  三、VARIANT、COleVariant 和_variant_t5 S7 y% ]' i" T! P, Q
$ T( N& k, L0 a' g% g% B, f
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:% F! i9 t& T! B, m: X" h
+ p- S  B' c7 O" _& p: N
struct tagVARIANT {+ E3 p  r( Z7 T4 q4 H: e
 VARTYPE vt;
0 c$ K6 k7 [+ i' u' [ union {
* t5 o8 G" w0 L" ?: E  short iVal; // VT_I2.
* s6 B5 S; v# U. m- G  long lVal; // VT_I4.
' ?+ n% o1 m  S3 U8 `% f, c  float fltVal; // VT_R4.: \- R" U( b4 s3 {
  double dblVal; // VT_R8.- ?1 E* g8 g2 }/ U. @. w5 |% b6 W( o2 S
  DATE date; // VT_DATE.
" H( s% N& {2 I$ ]9 z  BSTR bstrVal; // VT_BSTR.. X* Y% q4 H. ]1 }; L4 s4 {
  …# n1 M/ G; t# O" m7 L7 h6 c
  short * piVal; // VT_BYREF|VT_I2./ `  f' h+ {3 t
  long * plVal; // VT_BYREF|VT_I4.
0 K2 @2 o- e5 ]  float * pfltVal; // VT_BYREF|VT_R4.8 w7 w" V6 [; i9 Z, D9 |
  double * pdblVal; // VT_BYREF|VT_R8.
4 A# D: j+ z# u1 |* r  DATE * pdate; // VT_BYREF|VT_DATE.$ V7 j& O4 m9 I+ N
  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
  _( l3 o, O7 N- r0 r6 q8 w! g! { };- J/ s2 q; A4 x9 ^
};   t4 ?. P2 y* f% \( s' I& x

$ h0 n3 k  ^: D! ?  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:
0 c: s2 g' L9 Y: J9 l
$ k  }) T$ n3 W( e4 C+ ~VARIANT va;
$ d1 t. M# i0 T. i* Z:: VariantInit(&va); // 初始化
; D9 o5 I3 g& b; C* t% ]7 h8 B. ?& Rint a = 2002;
2 H4 P5 k6 Z9 l8 |. [va.vt = VT_I4; // 指明long数据类型
- R* k, @. m/ A" y  Lva.lVal = a; // 赋值
# E$ B" r3 D) E1 {7 p& e* ]/ {" a. a, r8 A& V: \4 J! F
  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:* ~  ?& G* I8 P; ~9 H
5 K) @' U! ?! X4 U8 Q4 e
  VariantInit —— 将变量初始化为VT_EMPTY;
$ S5 J7 e3 x  ]' B4 D/ S: w: D8 y# f/ d/ s
  VariantClear —— 消除并初始化VARIANT;
( d. a# U/ |7 {  t" N! I2 U6 [0 w  F: O( b- s; J
  VariantChangeType —— 改变VARIANT的类型;- D- E& n. w% p( I" p2 K2 Y
  ]) i6 D3 W) L. Z7 T; l4 |0 ^
  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。
7 A. Y# f5 U+ T6 t' h! r: b
3 l% S7 U3 B7 a0 t* D0 e7 e  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:# U" O  G, ^. u
! {' Z; i' D/ }! I
COleVariant v1("This is a test"); // 直接构造
4 C+ R' Z, M4 f: |  K# a0 A! d/ \COleVariant v2 = "This is a test"; ; f2 v5 Y2 o; |5 P+ K2 g
// 结果是VT_BSTR类型,值为"This is a test"+ u6 _3 m% q% K$ ^! A
COleVariant v3((long)2002);
  g. h/ o' a1 u& H1 V: G# H9 ^COleVariant v4 = (long)2002;
- R0 Y' k+ V, G$ x; Q% S$ U) l4 s: d// 结果是VT_I4类型,值为2002 / _& p+ T' H0 T

2 H& B+ E" I8 W# @$ l4 ?& D" H  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:7 A( X& u+ P/ W% g; q5 G& m
" N: I1 t, w* ?! Y8 M& ]6 o
  #include "comutil.h"8 o' H  F8 m$ x" a5 Q
. p& i- y0 x; g% X- ?; V% j
  #pragma comment( lib, "comsupp.lib" )
. d6 e4 Y. `0 ~8 I5 e8 I! b: j" D) M; r2 ^% H
  四、CComBSTR和_bstr_t
) L$ S; _( \  B1 }( W4 S% _- R$ Q. J
  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
! e/ }/ A+ ~' @4 K1 M4 M, @) ?8 c
7 [4 L  G! q* Y- u6 XCComBSTR bstr1; " e$ J; ^9 U4 j, U. a* l5 a* U
bstr1 = "Bye"; // 直接赋值
6 M+ r) T( v: b% [  pOLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符7 N# `3 U/ i* z: z3 \* {& g
CComBSTR bstr2(wcslen(str)); // 定义长度为5
5 B7 Q. n: a$ P5 Kwcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中5 g3 D  }0 a' t' `4 x. J
CComBSTR bstr3(5, OLESTR("Hello World")); . m- A8 H. M2 R5 S4 l/ s% ~% _
CComBSTR bstr4(5, "Hello World");
$ I6 L& n+ y! RCComBSTR bstr5(OLESTR("Hey there"));
, u) \7 F7 x6 e3 Q8 CCComBSTR bstr6("Hey there");
+ i! c$ \+ F" r0 d) g! jCComBSTR bstr7(bstr6);
) g( T& p: D) [% l( I- o// 构造时复制,内容为"Hey there"
9 J7 e; I9 X: J1 v# x! a0 U* w$ s  |* N
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。) z; K+ {8 j! c2 ~

& E) `: P6 d7 [" t  五、BSTR、char*和CString转换- v( d3 f3 M, i. c3 p4 J7 L+ Y
/ H% k4 G. @$ U& P
  (1) char*转换成CString
: K8 u7 u1 N/ Z9 U6 N! o9 h2 [7 K! [0 M, u! \9 T
  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:% k! _2 I1 G  n6 _0 i5 `: ~

( J  ^- H, Y) c* ^9 {8 d* Zchar chArray[] = "This is a test";: J3 J4 n% L# ^; S4 H
char * p = "This is a test"; 2 c! v" r6 L0 f( C3 B

8 \* M  H0 `& l! e  或
1 C) }+ e! E/ G4 Y  ]7 C- e7 \1 d! G" {7 f3 T; y
LPSTR p = "This is a test"; . r/ h2 `+ e1 r1 H6 e& H

6 M( g( `4 ]& y9 F3 q  i  或在已定义Unicode应的用程序中8 l3 }6 _* r! K6 c" h

# B! D8 P7 w4 J- {3 B9 Y- `TCHAR * p = _T("This is a test"); 8 C: z* ~2 e+ Y
' ~4 ]  ]9 t- T) U3 }  I
  或7 m/ n% d. |* O; a
+ L' \9 Q. W* o7 B
LPTSTR p = _T("This is a test");
+ @" T4 [, U$ e2 P( k8 C* w) U3 vCString theString = chArray;' O- Y" I& y, v& m4 T' L! ]
theString.Format(_T("%s"), chArray);& [& D9 ~. @0 y2 U2 G3 V
theString = p;
1 r% G8 M- L" i
9 O2 `/ h% H/ ^" H4 ?( x! S  j) a  (2) CString转换成char*) z4 a1 W$ U9 C4 A* @5 l
4 x. T, I2 _/ N: r  ~0 G5 w
  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:
- x1 H" R- g" B6 N- r3 o) @
9 x- N9 c( Q1 e* G7 U7 C  方法一,使用强制转换。例如:5 H' a, y& j5 a+ u/ j

: W; L& [: c5 mCString theString( "This is a test" );
. s9 }2 ~8 E& c/ M2 lLPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  
4 t( a3 P' |: S9 x+ U
" {, z; ^9 l5 @( g; j8 r& T  方法二,使用strcpy。例如:( w$ p. ?2 R( q/ L6 U& a, M
6 O3 _+ G4 M: m6 l$ C
CString theString( "This is a test" );
/ t( h3 }+ q) }( q# FLPTSTR lpsz = new TCHAR[theString.GetLength()+1];8 m) ^) ]1 L! ~0 O; `) D% o3 f
_tcscpy(lpsz, theString); , E1 ]" Y* ?5 p+ _

% T; x, ]( r2 T  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。7 k" C) z) A, {" F# K$ A& _9 u
/ w8 E9 t* l6 a; ^9 M
  方法三,使用CString::GetBuffer。例如:) `2 q2 i: ^8 g1 ]. z- [7 b+ w
) S+ \4 L1 I: D1 ]( m! c
CString s(_T("This is a test "));
4 V  T* b# b8 mLPTSTR p = s.GetBuffer();0 v* p$ O6 j. w6 ]: ^
// 在这里添加使用p的代码
3 t6 F: d$ B  B, B- Lif(p != NULL) *p = _T('\0');
$ V/ B5 {% [9 ?0 ps.ReleaseBuffer(); ( n' B. d1 G% R$ l# z
// 使用完后及时释放,以便能使用其它的CString成员函数
6 U2 j2 C( y" ]$ G) |. Z& ^8 m  [! L" @3 P+ Y, K/ |# P( r/ z6 C, m
  (3) BSTR转换成char*
# R1 E7 }% }1 Y0 ]/ ~4 k$ W' c% _5 O5 G. D. V
  方法一,使用ConvertBSTRToString。例如:( F" M$ @5 n% M- ?, E6 n
* R4 |% Q+ J" V; \% X
#include . c3 G& g$ J* E1 o, V- K
#pragma comment(lib, "comsupp.lib")
5 K$ p6 m- Z6 t8 R& Q% S$ w% Qint _tmain(int argc, _TCHAR* argv[]){& U5 |' e1 W: D% Z: r
BSTR bstrText = ::SysAllocString(L"Test");
& m& i1 y. R0 F; b, Echar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
4 h1 ^' [4 _# m7 G0 g# t+ S) v4 wSysFreeString(bstrText); // 用完释放1 b* g$ h+ o/ W- y6 c
delete[] lpszText2;& u5 S, p' K3 h) p
return 0;
9 y  r) a* ~/ Y9 p}  
, y) E7 {1 [" K2 F7 Z3 @& v' F
5 F) Q" n- M/ Z1 c! n  方法二,使用_bstr_t的赋值运算符重载。例如:6 S: E% s+ v  _# M
& |4 j  n, G" D+ p; N$ K
_bstr_t b = bstrText;) H3 k; K3 a8 l1 |# L! b
char* lpszText2 = b;
6 P6 n0 b7 m$ S+ X5 W9 O
, G' Z% }; l5 `1 f) r  (4) char*转换成BSTR
$ [) ?. f- S5 e
6 V& ?5 f5 R& p. m5 R6 n' f  方法一,使用SysAllocString等API函数。例如:
* ~& b5 d+ X& B" w# t- n+ w; ~8 d. [0 P
BSTR bstrText = ::SysAllocString(L"Test");- c8 A7 ^" k; B0 u5 G$ y& x
BSTR bstrText = ::SysAllocStringLen(L"Test",4);1 W5 D; G" g5 l" N* ?
BSTR bstrText = ::SysAllocStringByteLen("Test",4); + J, O9 l* ]; O, k

3 A2 ]8 [- U" ~3 j( U7 Z7 G  方法二,使用COleVariant或_variant_t。例如:0 M" N1 E! z% ?; i- j  n, ^) [

' ~1 b8 x" ~5 P) O, s* W7 \6 O//COleVariant strVar("This is a test");. c/ q  M# r0 {; n+ {7 s5 V
_variant_t strVar("This is a test");1 K3 D8 |" h# a7 m' o0 D0 V7 s
BSTR bstrText = strVar.bstrVal;
7 D. n# _# m& O# m5 {6 C6 @8 A; {2 Q* M5 l) |  p3 G' l
  方法三,使用_bstr_t,这是一种最简单的方法。例如:
; M+ \* e8 c9 I! f
8 B+ i1 k$ K* v. H4 A3 {BSTR bstrText = _bstr_t("This is a test"); ! s- z( Z9 a4 I7 }& ?( {: V, O

9 _8 Y; l# \: D! u+ L! Y; @, @" t3 k  方法四,使用CComBSTR。例如:
- U7 J: D7 ^7 w" p# P3 s% ~0 r) }2 @/ b1 w/ M
BSTR bstrText = CComBSTR("This is a test");
5 Y1 V* \! V! {- b) ^
/ i4 D, c- S- m3 g8 H' V( c  或
1 }% V! X) K2 q4 W. {6 B/ D+ j, y/ f0 Q8 g% ], _
CComBSTR bstr("This is a test");
, }$ f8 [- U8 V6 v' KBSTR bstrText = bstr.m_str; 6 Y' |. \: B6 J& |1 _9 k2 ]

; n: i9 X1 v8 c/ T$ s  方法五,使用ConvertStringToBSTR。例如:9 \7 V/ f: o  R4 Y' ^: p
# z1 X( k: L: g* v" b" B
char* lpszText = "Test";
: M4 m* G. `/ \5 A8 v6 Z! sBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
8 C! h. K& _0 P/ X( N
/ ]! C/ ]) f) L  (5) CString转换成BSTR
+ f6 r0 X# C) r/ g# a
  v! `1 J: j- x+ T1 ~9 I5 m  通常是通过使用CStringT::AllocSysString来实现。例如:
7 J; S4 o/ b# Y- C
% z3 A3 E5 R# Y; q9 h7 ACString str("This is a test");* t; j: k* i% v( y
BSTR bstrText = str.AllocSysString();( @1 ]) T: f0 C# I2 {  \% k+ f

& u; v" l" U0 b% QSysFreeString(bstrText); // 用完释放  + W" V; L! U: u0 N5 T/ K! a2 g
4 v) m1 L1 U& O( l. a
  (6) BSTR转换成CString
/ z* B: d* A5 Z; \% \0 H* x1 }# F3 _* C; t5 C
  一般可按下列方法进行:( t) z, g1 A4 d6 Y; p$ h3 |
/ t0 ~! N) b. H  O6 g- g
BSTR bstrText = ::SysAllocString(L"Test");% |% K7 W( `; e9 \; I  _9 H0 i
CStringA str;
4 {3 a6 S3 X4 M8 Jstr.Empty();4 f- ?% b5 `9 J; L5 B& q1 d4 l
str = bstrText;  
4 e8 y0 N+ q; w# v2 i" b4 s  E  Q" q( q7 P5 }* ?9 Y/ y* q, \
  或
! L  r- B% m) W7 O* ?! c/ _2 ~4 Z! e; B! \! d# [( Z- t
CStringA str(bstrText); . m7 `; n1 d' [6 {  G1 J
- f% f& [( P: T5 x) b
  (7) ANSI、Unicode和宽字符之间的转换
1 S" y' ^  O" k' Y8 j5 G# R1 K
, e, v; M8 R# {. M) p; K  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。' N: |/ p+ b) i8 D! i5 J: P5 R
. p' q- U1 e' t
  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:2 ?# z; u2 {0 j9 [7 N' O

) M+ F% P2 I& yTCHAR tstr[] = _T("this is a test");
/ m7 o; D5 E" r/ X& l: \$ rwchar_t wszStr[] = L"This is a test";
$ b( j) x* i4 i# p3 H5 n5 CString* str = S”This is a test”; ' w  X$ ]. S+ \5 c

, z  B+ ^$ W4 d/ I) l$ Q9 J  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
: H6 g, u6 j' J4 k; x! m) z: @) }4 X' u" E" P% Q/ Z9 D
  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
! E, n) }5 J7 Z0 D; {
; t5 p. l$ r( uLPTSTR tstr= CA2TEX<16>("this is a test");
* e2 e9 G" h/ o3 h  U" T: q" xLPCTSTR tcstr= CA2CT("this is a test");0 ^- e' C% w# p7 h0 l; X
wchar_t wszStr[] = L"This is a test";+ C$ q( j/ X+ w  a5 f
char* chstr = CW2A(wszStr);  
8 X+ X. g+ r& A5 j
3 L: T0 S9 P* p5 W- s# C7 Q2 X  六、结语
; B! m4 g: ^! ?0 v( ?- ?- m, `0 U) n9 ~
  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-11-15 12:50 , Processed in 0.018677 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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