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

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

[复制链接]
发表于 2003-10-19 13:10:41 | 显示全部楼层 |阅读模式
2002-12-05· ·丁有和··yesky, n9 k+ a' w1 F, E1 m
3 E- L$ E: y' j) r! q! X% j
" U7 i  Y) n) l% ^
  Visual C++.NET涉及到ATL/ATL Server、MFC和托管C++等多种编程方式,不仅功能强大而且应用广泛。在编程中,我们常常会遇到ANSI、Unicode以及BSTR不同编码类型的字符串转换操作。本文先介绍基本字符串类型,然后说明相关的类,如CComBSTR、_bstr_t、CStringT等,最后讨论它们的转换方法,其中还包括使用最新ATL7.0的转换类和宏,如CA2CT、CA2TEX等。1 b% g  Z" U: i6 n  J

! E" y, |8 N) S7 f0 x& Y  一、BSTR、LPSTR和LPWSTR
/ b2 _  z; P3 B& j0 k6 y8 x& ~; l* S) [* W/ Z) p* b4 B
  在Visual C++.NET的所有编程方式中,我们常常要用到这样的一些基本字符串类型,如BSTR、LPSTR和LPWSTR等。之所以出现类似上述的这些数据类型,是因为不同编程语言之间的数据交换以及对ANSI、Unicode和多字节字符集(MBCS)的支持。9 D; s9 b: O  I7 `6 S8 \/ h

$ H* V6 R# T7 w! U& d+ B  那么什么是BSTR、LPSTR以及LPWSTR呢?
1 ^8 ]& n; \2 }9 N6 O0 H1 H0 X/ X% i. C1 N3 L% Q
  BSTR(Basic STRing,Basic字符串)是一个OLECHAR*类型的Unicode字符串。它被描述成一个与自动化相兼容的类型。由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。图1描述了BSTR的结构,其中DWORD值是字符串中实际所占用的字节数,且它的值是字符串中Unicode字符的两倍。$ E  O0 n' \- z
6 J) @! ?5 m' ?6 a
  LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的8位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的16位双字节字符数组指针。在VC++中,还有类似的字符串类型,如LPTSTR、LPCTSTR等,它们的含义如图2所示。
5 @8 F: P/ O1 j' u5 A8 `
5 P$ ~' k  n- H, D3 v; x$ w: s  例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一个指向一般字符串常量的长指针类型”,与C/C++的const char*相映射,而LPTSTR映射为 char*。* G6 [6 d' a% h8 L8 y
: p0 i- G5 Q" h- J( O2 h* n& k. V
  一般地,还有下列类型定义:
+ J/ Y' F0 \. ]  D! ?. F1 @9 o1 m" n6 i
0 a* f. V. t* O1 s4 r" u#ifdef UNICODE 4 Q5 U/ g2 A% N
 typedef LPWSTR LPTSTR;3 n' p1 i8 a# J: o3 R, _% y
 typedef LPCWSTR LPCTSTR;
9 T$ y4 M& g/ [9 b4 `#else
5 s1 V% s: f8 P typedef LPSTR LPTSTR;
0 p* N& u! }4 Z3 e( c  V! f typedef LPCSTR LPCTSTR;
# q" w3 v: q9 T. J  i& \* U! x#endif  ! U& Y7 Y0 }2 D4 ]9 i& @
; y* U3 t) l8 o; H# O. {
  二、CString、CStringA 和 CStringW
& W: y2 J. g5 \8 s3 z
. D. _( }6 F# A: 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应用程序中经常用到,这里不再重复。% i' L/ z# n( M) P
6 _, u1 y7 ^7 X" N
  三、VARIANT、COleVariant 和_variant_t, Y+ w. }! a1 N6 e. x
& _. }9 M& o; l8 R
  在OLE、ActiveX和COM中,VARIANT数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。下面让我们来看看OAIDL.H文件中VARIANT定义的一个简化版:
( M6 ~% O8 O' j1 _2 C/ _# q2 w+ R' x# n- i
struct tagVARIANT {. r) Y' V. l1 y6 v+ m
 VARTYPE vt;2 q1 B6 ^" |% t. q( t  N2 e2 C; Z
 union {
6 f- Y1 o& n+ _  short iVal; // VT_I2.: ~8 v2 U/ N1 v6 E1 I0 R
  long lVal; // VT_I4., t3 R0 L) }! \2 g
  float fltVal; // VT_R4.: t+ E( ?4 \: [
  double dblVal; // VT_R8.
) H! u$ D- w, D- c  DATE date; // VT_DATE.; n6 k" Y1 E7 [- A
  BSTR bstrVal; // VT_BSTR.
1 t! ]: U. I6 }) T9 |9 l  …) J, w7 V$ W* {8 ^  ]0 m
  short * piVal; // VT_BYREF|VT_I2.( E/ L8 W. K' j3 P4 e# y
  long * plVal; // VT_BYREF|VT_I4.$ Y0 E; J) ]# y2 }! o6 d
  float * pfltVal; // VT_BYREF|VT_R4.
1 p- ]* c2 h- Y8 u9 A  double * pdblVal; // VT_BYREF|VT_R8., _4 d" E  ?; a( F
  DATE * pdate; // VT_BYREF|VT_DATE.
' ~* J/ v. h3 q+ o; O1 r$ b  BSTR * pbstrVal; // VT_BYREF|VT_BSTR.! Q- L' t0 m* x! L
 };4 H& K+ j3 D6 ~# H# E0 t  A; B
};
1 [4 ]# F9 V# O1 r2 F
  |" r- [# ]! s/ c  显然,VARIANT类型是一个C结构,它包含了一个类型成员vt、一些保留字节以及一个大的union类型。例如,如果vt为VT_I2,那么我们可以从iVal中读出VARIANT的值。同样,当给一个VARIANT变量赋值时,也要先指明其类型。例如:) F: e: u; n, I$ b' y7 V: Z

3 @# x, I/ `. ?+ d( r) C" F, q& eVARIANT va;. B( y6 v8 D+ h4 \
:: VariantInit(&va); // 初始化
: ~. F3 w$ ~  ]1 s3 Eint a = 2002;
5 {# j$ V2 M1 gva.vt = VT_I4; // 指明long数据类型
% l0 k/ b7 {" W- h8 r( Vva.lVal = a; // 赋值 5 U6 \( W: o8 Y! j% ~* g

/ ^) G0 O5 p' @. U2 n  为了方便处理VARIANT类型的变量,Windows还提供了这样一些非常有用的函数:' W, S. I' y+ {4 h: P

4 o/ u) K; z; h: P* q. z2 O  VariantInit —— 将变量初始化为VT_EMPTY;
9 I3 g4 z& R5 A# Z: k/ [% L. G! O* A8 L/ D
  VariantClear —— 消除并初始化VARIANT;
4 _4 b/ M- L$ E3 ]' d: J( N% I* O% z% X) h2 @
  VariantChangeType —— 改变VARIANT的类型;4 Q5 n/ _! [1 N9 R( V, t

* l& }: _( `# C% _0 n! C  VariantCopy —— 释放与目标VARIANT相连的内存并复制源VARIANT。) X) ?: m3 S$ Z9 s

4 [  X; U. Y: N: A. Q  COleVariant类是对VARIANT结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用VariantInit进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用VariantCopy进行转换赋值操作,当VARIANT对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant的赋值操作符在与VARIANT类型转换中为我们提供极大的方便。例如下面的代码:: a  L+ H3 C* d/ \
+ o; m% G, J! j1 O6 o/ d* k
COleVariant v1("This is a test"); // 直接构造& [! y& C4 [3 v9 x" [- n8 {
COleVariant v2 = "This is a test";
  I5 b' s" i1 x+ l4 \. `. u// 结果是VT_BSTR类型,值为"This is a test"- T: ?" O- N$ a; y5 G
COleVariant v3((long)2002);
/ v3 X. ~( r. G2 N$ \" D$ |COleVariant v4 = (long)2002;
3 `- J9 M/ d7 E9 _. ?9 ]// 结果是VT_I4类型,值为2002 ( L) \4 j* Q6 R" o. R/ b
- e' o! v3 ]0 W6 @- a
  _variant_t是一个用于COM的VARIANT类,它的功能与COleVariant相似。不过在Visual C++.NET的MFC应用程序中使用时需要在代码文件前面添加下列两句:! ?* V% S3 v8 N3 y% i, N- }

( u! d" `- u+ g9 ]  #include "comutil.h"
, [, B9 p- d/ T! p- Z8 V' n6 }7 y% W4 l
  #pragma comment( lib, "comsupp.lib" )4 C. U6 g2 ?& N" r- z
# i) ?$ w1 u* S
  四、CComBSTR和_bstr_t
- h& O% ^$ Q+ l9 c. H: p  q' p) {- c3 U( G% }0 S  k' K
  CComBSTR是对BSTR数据类型封装的一个ATL类,它的操作比较方便。例如:
$ G0 G3 `$ Z) |/ I, N2 h
; _0 n! s3 A  ?1 aCComBSTR bstr1; 2 W1 i0 y2 v; k0 r+ G
bstr1 = "Bye"; // 直接赋值7 A+ A4 D/ P* h$ t+ k
OLECHAR* str = OLESTR("ta ta"); // 长度为5的宽字符
4 b, Q2 X. n% e' Q1 JCComBSTR bstr2(wcslen(str)); // 定义长度为5" r7 B- i$ e* ^) m6 M
wcscpy(bstr2.m_str, str); // 将宽字符串复制到BSTR中
/ n/ ^. e, j8 v, f7 y# OCComBSTR bstr3(5, OLESTR("Hello World"));
$ l4 S  y' z* f' O/ }CComBSTR bstr4(5, "Hello World");
; A/ N0 z- H9 T) s3 l/ sCComBSTR bstr5(OLESTR("Hey there")); " m/ [1 I9 d9 F. G2 g- Y
CComBSTR bstr6("Hey there"); - d; I4 v9 C# m
CComBSTR bstr7(bstr6); $ ~& b) F) b( Y8 Z& {
// 构造时复制,内容为"Hey there" ) `% x% M7 K) p! \
1 Y8 j' X) U# k% |6 G
  _bstr_t是是C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTR API函数。与_variant_t相似,使用时也要添加comutil.h和comsupp.lib。4 c: J* u9 Y" g; O- E
2 x- F$ ^, H% H9 n  \
  五、BSTR、char*和CString转换
( G& X7 H7 a0 w+ h# Z5 E- r$ j! I( Y# c" M' V4 {. u
  (1) char*转换成CString
% h2 ]+ O% R' K  |8 q# J% i$ [4 G4 {0 E6 `$ a
  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:
5 z( S6 q3 O  Y/ u9 P
- r3 C% \9 L( N2 e  G2 P/ xchar chArray[] = "This is a test";9 \, s1 V. h8 n) g
char * p = "This is a test";
0 M7 @0 C5 ]0 \& D2 ~: f' B3 E. T* {% ]5 C. F! f) c1 N
  或1 s# ?0 l& N( s7 J

) U" A6 C  F2 }# |6 P1 iLPSTR p = "This is a test";
2 G6 |, A# u  O. b- ?- F+ |  ]6 ^7 v( k0 ^) P9 k$ n
  或在已定义Unicode应的用程序中, h/ O: x4 R; b/ V% `

6 j0 B, c" i0 F* W! kTCHAR * p = _T("This is a test");
# Y7 Y# b4 \; f4 j
+ Z" o6 V1 L/ D4 b3 H7 _4 M6 K  或
7 B- X  D; g% Y1 V- d1 a* T4 h- I) K/ E* h
LPTSTR p = _T("This is a test");
2 |' S$ v$ X% u+ q( r& y  H( g8 uCString theString = chArray;
/ [4 h# c( e# C- ~' z. c8 c  ptheString.Format(_T("%s"), chArray);
! r/ c3 \- e/ {9 O% ftheString = p;
. n4 a, L  t- S) i  A! T1 L6 `) x& V3 C& |% {& L
  (2) CString转换成char*5 i6 [$ B- N2 S# x3 n: S6 O. N
* |7 s$ b7 [! b7 p0 R
  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:' I, x4 R( B, C7 _
: e  U' o" q8 [! i" s. F! n, f2 G
  方法一,使用强制转换。例如:
4 L3 |$ v1 N+ ^' r* r4 H# x; F( A# V
/ V: r* J9 b" ^6 G" ~% I2 |) ACString theString( "This is a test" );
! v$ ]7 R- B8 B: P7 z8 @LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  1 J+ [4 z# z3 I- L7 X

* S# l2 J& e. e  方法二,使用strcpy。例如:
: O1 b' E% e+ F% l; w2 }5 g; v4 f. G
CString theString( "This is a test" );0 B* I$ G" m* Y4 h) p) q% h4 `
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
9 y: n. G' K! K4 n' r# D_tcscpy(lpsz, theString);
/ n# M+ j: N0 k  I( Y3 {( e( i9 u
  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。6 {% R+ S! G6 x- {6 M# o- P
) j7 x. ^5 ~( q5 M' V6 v6 S
  方法三,使用CString::GetBuffer。例如:6 z# R2 ?" S! G8 i" a3 V

- m4 ~3 L) g( R' }CString s(_T("This is a test "));/ h7 S) T7 a% g) h) e/ j- {
LPTSTR p = s.GetBuffer();
4 w1 D! `+ x- H// 在这里添加使用p的代码  e7 b6 `( k5 _
if(p != NULL) *p = _T('\0');
$ ]4 F- Q6 ^/ ws.ReleaseBuffer(); . Q( F9 R, ~1 O4 ~
// 使用完后及时释放,以便能使用其它的CString成员函数
5 h: U& ]$ x9 M+ @
& X* D- T; S7 y! Z6 t  s: y- [  (3) BSTR转换成char*, \2 L9 g4 w9 w- q5 Y9 m
6 s. w% _% B* G- X0 M6 X3 N
  方法一,使用ConvertBSTRToString。例如:
& L  A! K; `# a8 X- Y, H+ g* C* @8 k
#include
  W+ }& O6 E4 A$ @#pragma comment(lib, "comsupp.lib"). P# b' ^" k# u5 r6 h/ a+ r$ q
int _tmain(int argc, _TCHAR* argv[]){$ {$ M2 w7 |/ e) f, Z$ `' g
BSTR bstrText = ::SysAllocString(L"Test");
1 i) {% @& G* i6 j" x4 Y" C5 T/ j& zchar* lpszText2 = _com_util::ConvertBSTRToString(bstrText);# v# ~" T) e, F
SysFreeString(bstrText); // 用完释放2 M3 a1 n& @: w7 O
delete[] lpszText2;9 x6 }5 K) t9 ^5 Y
return 0;
# w5 ^- s; K3 `0 C; F% M}  ; O8 C" X3 C5 w! `

: X0 ^: B2 V# w9 F8 D  方法二,使用_bstr_t的赋值运算符重载。例如:! a' i  S5 o$ n2 {* e, f

9 r- K5 I( h1 ?' Q) Q_bstr_t b = bstrText;4 I/ X* h9 U" a& {
char* lpszText2 = b; 0 ~8 ^3 D, Q( V

2 q# q, M9 {! c" d  (4) char*转换成BSTR1 t# X6 g) u0 D  D

( Q/ U5 A" z  N  方法一,使用SysAllocString等API函数。例如:$ f2 L6 |8 ~5 x9 l
0 E9 _4 W) Y% D
BSTR bstrText = ::SysAllocString(L"Test");
+ n6 _6 f& T5 j  ^. v! q$ x5 ^BSTR bstrText = ::SysAllocStringLen(L"Test",4);$ ]# {3 z" \3 V4 k4 o: D
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
: e/ W" y6 D' A' Y$ [5 V
( b9 S6 I4 {9 ?+ L$ g) `+ \  方法二,使用COleVariant或_variant_t。例如:
1 J4 B$ @. r1 |5 }3 m" v! j2 M
* I6 f' Q. L; ~* H6 i+ I# K//COleVariant strVar("This is a test");
1 T; [/ ]; \0 d+ w! e8 J_variant_t strVar("This is a test");
- J) x, N* P& H: r2 PBSTR bstrText = strVar.bstrVal;
2 ?3 f; C( P0 v3 \6 |  Y9 W& L; Q% V- c" V/ m/ a
  方法三,使用_bstr_t,这是一种最简单的方法。例如:
2 Z3 X- \  K" m/ C
$ L2 D* @$ _( e+ hBSTR bstrText = _bstr_t("This is a test"); ; e! w5 R* ~; U. r3 {

( b6 N1 F; R& a: N) {  方法四,使用CComBSTR。例如:
4 L* `3 Q) D. G& F
1 A+ @) V) x5 \- ]8 @7 HBSTR bstrText = CComBSTR("This is a test");
: `7 z7 @% O% c& R' B1 b) r2 g' _  o8 n2 G6 a7 U
  或9 P% R" `0 [; u. z
" r* O. z8 M, B$ c% L
CComBSTR bstr("This is a test");2 v  l( F! M, k- X
BSTR bstrText = bstr.m_str; 6 {& g1 G1 \! i$ x
! o2 `' u$ b) g) J! N; M9 c( t
  方法五,使用ConvertStringToBSTR。例如:: B! F. n  s2 a0 a( U4 R; G
2 j/ ^0 h6 [) K& B
char* lpszText = "Test";
. d9 L3 W- W2 I- u- LBSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); ' |7 r! H& s2 n9 F$ p3 ?' Y

( J  ~0 w, [- [7 }5 }" ^  (5) CString转换成BSTR
2 ]  E) c( M& w9 O, L1 ~! h* N0 d9 J) P- C# S
  通常是通过使用CStringT::AllocSysString来实现。例如:
/ |9 M: m& [7 N: g; ^* Q: O! S! D1 X& z
CString str("This is a test");5 m) C+ P5 O2 n1 j
BSTR bstrText = str.AllocSysString();
. C$ k, I9 \! `5 a: J  K4 P4 h/ A3 j
SysFreeString(bstrText); // 用完释放  1 l% d7 F$ c& g0 j* ?
& q2 M! ~' p, M% c2 D
  (6) BSTR转换成CString
9 O4 y) _* ^& K
. x8 q* ?$ X& [* n& v' B  一般可按下列方法进行:) ^- W6 v! r. Q1 D- h9 \4 m
1 S4 g4 y. O" ]$ ^4 o
BSTR bstrText = ::SysAllocString(L"Test");
# ^  w; H2 }4 C+ q$ t: F$ b: ^" I/ F( A0 BCStringA str;
1 j3 i. f( n1 I9 }/ q" B4 H5 {str.Empty();1 z# V8 k4 Z0 {$ X: @2 t- E- s5 N
str = bstrText;  
4 ?. ~! C0 [& s# m4 Z( Q. _5 z' ^; |9 U
  或
- O& H$ |  ~6 k  C2 N3 a" N- _0 J5 i8 l& b/ Z$ ]" H' y% r" ]$ N
CStringA str(bstrText);
8 j/ f+ h# Z. u& n% [' v2 e& K; @6 R0 z( \9 O" |# o) |
  (7) ANSI、Unicode和宽字符之间的转换
9 ]; n& X9 M! r
; z) ?/ |% K0 X  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。
5 F" h( h4 C' b( a9 Q9 A' O+ q1 u/ B& t% B7 X( k1 [
  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:+ E0 |1 s6 o" [% T! Z
) Y0 M: G7 E# L$ b& k. q; F+ y  o
TCHAR tstr[] = _T("this is a test");
& l- O, X' ~% S2 j* lwchar_t wszStr[] = L"This is a test";
1 Y" L- \  m+ O& qString* str = S”This is a test”;   l& o( Y" ?8 E) ?; a  |1 v+ G# _* [

6 t# @" M4 V" R- g" h# G  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:
; k* ~4 [8 N8 Y+ J: V: @7 K: A$ H7 m5 m! n; c! ~
  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:
0 o6 p/ W. C6 o) N+ d
# [" d4 ]* @; ]8 B- H  T" j$ |2 qLPTSTR tstr= CA2TEX<16>("this is a test");# v' ~4 H# O: p* a- E3 D2 Y
LPCTSTR tcstr= CA2CT("this is a test");7 N) z' O- `  J& h) d5 [9 d
wchar_t wszStr[] = L"This is a test";
5 \: c. t% z9 g; W+ J# kchar* chstr = CW2A(wszStr);  ' w7 {, ^- M' R( u' P; d
( {5 h: d: |6 O8 H$ i
  六、结语
" a/ Z8 U) C2 w  k
, g2 F2 v6 a( @* Q# D  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-8-14 00:06 , Processed in 0.021962 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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