??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
int length = req.getContentLength();
if (length > 0) {
BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(req,length);
InputStream is = bufferedRequest.getInputStream();
byte[] content = new byte[length];
int pad = 0;
while(pad < length){
pad += is.read(content, pad, length);
}
request = bufferedRequest;
}
chain.doFilter(request, response);
}
BufferedRequestWrapper .java
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class BufferedRequestWrapper extends HttpServletRequestWrapper {
ByteArrayInputStream bais;
BufferedServletInputStream bsis;
byte[] buffer;
public BufferedRequestWrapper(HttpServletRequest req,int length) throws IOException {
super(req);
// Read InputStream and store its content in a buffer.
InputStream is = req.getInputStream();
buffer = new byte[length];
int pad = 0;
while(pad < length){
pad += is.read(buffer, pad, length);
}
}
public ServletInputStream getInputStream() {
try {
// Generate a new InputStream by stored buffer
bais = new ByteArrayInputStream(buffer);
// Istantiate a subclass of ServletInputStream
// (Only ServletInputStream or subclasses of it are accepted by the
// servlet engine!)
bsis = new BufferedServletInputStream(bais);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
}
return bsis;
}
}
BufferedServletInputStream .java
import java.io.*;
import javax.servlet.ServletInputStream;
/*
Subclass of ServletInputStream needed by the servlet engine.
All inputStream methods are wrapped and are delegated to
the ByteArrayInputStream (obtained as constructor parameter)!
*/
public class BufferedServletInputStream extends ServletInputStream {
ByteArrayInputStream bais;
public BufferedServletInputStream(ByteArrayInputStream bais) {
this.bais = bais;
}
public int available() {
return bais.available();
}
public int read() {
return bais.read();
}
public int read(byte[] buf, int off, int len) {
return bais.read(buf, off, len);
}
}
// Fill the message
messageBodyPart.setText(body);
mp.addBodyPart(messageBodyPart);
/*发送附?/
if (file != null && file.length > 0) {
//利用枚D器方便的遍历集合
MimeBodyPart mbp = new MimeBodyPart();
// File fileTmp = null;
//得到数据?br />// FileDataSource fds = new FileDataSource(fileTmp);
//得到附g本nq至入BodyPart
mbp.setDataHandler(new DataHandler(new ByteArrayDataSource(file,"application/octet-stream")));
//得到文g名同栯入BodyPart
mbp.setFileName(MimeUtility.encodeWord(fileName,"GB2312",null));
mp.addBodyPart(mbp);
}
//Multipart加入C?br /> msg.setContent(mp);
msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
msg.setSubject(subject);
msg.setHeader("X-Mailer", "personal Email Sender");
msg.setSentDate(new Date());
Transport transport = session.getTransport("smtp");
//d认证信息
transport.connect(Constants.mailhost, Constants.user, Constants.pwd);
transport.sendMessage(msg, msg.getRecipients(Message.RecipientType.TO));
transport.close();
return true;
}
import java.io.*;
import javax.activation.*;
public class ByteArrayDataSource implements DataSource {
/** * Data to write. */
private byte[] _data;
/** * Content-Type. */
private String _type;
/* Create a datasource from an input stream */
public ByteArrayDataSource(InputStream is, String type) {
_type = type;
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int ch;
// XXX : must be made more efficient by
// doing buffered reads, rather than one byte reads
while ((ch = is.read()) != -1)
os.write(ch);
_data = os.toByteArray();
} catch (IOException ioe) {
}
}
/* Create a datasource from a byte array */
public ByteArrayDataSource(byte[] data, String type) {
_data = data;
_type = type;
}
/* Create a datasource from a String */
public ByteArrayDataSource(String data, String type) {
try {
// Assumption that the string contains only ascii
// characters ! Else just pass in a charset into this
// constructor and use it in getBytes()
_data = data.getBytes("iso-8859-1");
} catch (UnsupportedEncodingException uee) {
}
_type = type;
}
public InputStream getInputStream() throws IOException {
if (_data == null)
throw new IOException("no data");
return new ByteArrayInputStream(_data);
}
public OutputStream getOutputStream() throws IOException {
throw new IOException("cannot do this");
}
public String getContentType() {
return _type;
}
public String getName() {
return "dummy";
}
}
前言
字符串的表现形式各异Q象TCHARQstd::stringQBSTR{等Q有时还会见到怪怪的用_tcs起头的宏。这个指南的目的是说明各种字符串类型及其用途,q说明如何在必要时进行类型的怺转换?/P>
在指南的W一部分Q介l三U字W编码格式。理解编码的工作原理是致为重要的。即使你已经知道字符串是一个字W的数组q样的概念,也请阅读本文Q它会让你明白各U字W串cM间的关系?/P>
指南的第二部分,阐q各个字W串c,什么时候用哪U字W串c,及其怺转换?/P>
所有的字符串类都v源于C语言的字W串Q而C语言字符串则是字W的数组。首先了解一下字W类型。有三种~码方式和三U字W类型?/P>
W一U编码方式是单字节字W集Q称之ؓSBCSQ它的所有字W都只有一个字节的长度。ASCII码就是SBCS。SBCS字符串由一个零字节l尾?/P>
W二U编码方式是多字节字W集Q称之ؓMBCSQ它包含的字W中有单字节长的字符Q也有多字节长的字符。Windows用到的MBCS只有二种字符cdQ单字节字符和双字节字符。因此Windows中用得最多的字符是双字节字符集,即DBCSQ通常用它来代替MBCS?/P>
在DBCS~码中,用一些保留值来指明该字W属于双字节字符。例如,Shift-JIS(通用日语)~码中,?x81-0x9F ?0xE0-0xFC 的意思是Q“这是一个双字节字符Q下一个字节是q个字符的一部分”。这L值通常UCؓ前导字节(lead byte)QL大于0x7F。前导字节后面是跟随字节(trail byte)。DBCS的跟随字节可以是M非零倹{与SBCS一PDBCS字符串也׃个零字节l尾?/P>
W三U编码方式是Unicode。Unicode~码标准中的所有字W都是双字节ѝ有时也UnicodeUCؓ宽字W集(wide characters)Q因为它的字W比单字节字W更?使用更多内存)。注意,Unicode不是MBCS - 区别在于MBCS~码中的字符长度是不同的。Unicode字符串用二个零字节字W结?一个宽字符的零值编??/P>
单字节字W集是拉丁字母,重音文字Q用ASCII标准定义Q用于DOS操作pȝ。双字节字符集用于东亚和中东语言。Unicode用于COM和Windows NT内部?/P>
读者都很熟悉单字节字符集,它的数据cd是char。双字节字符集也使用char数据cd(双字节字W集中的许多古怪处之一)。Unicode字符集用wchar_t数据cd。Unicode字符串用L前缀起头Q如Q?/P>
wchar_t wch = L'1'; // 2 个字? 0x0031
wchar_t* wsz = L"Hello"; // 12 个字? 6 个宽字符
单字节字W串序存放各个字符Qƈ用零字节表示字符串结。例如,字符?Bob"的存储格式ؓQ?/P>
Unicode~码中,L"Bob"的存储格式ؓQ?/P>
?x0000 (Unicode的零~码)l束字符丌Ӏ?/P>
DBCS 看上L点象SBCS。以后我们会看到在串处理和指针用上是有微妙差别的。字W串"日本? (nihongo) 的存储格式如?用LB和TB分别表示前导字节和跟随字?Q?/P>
注意Q?ni"的g是WORD?xFA93。?3和FA序l合~码为字W?ni"?在高位优先CPU中,存放序正如上所q??/P>
C语言字符串处理函敎ͼ如strcpy(), sprintf(), atol(){只能用于单字节字符丌Ӏ在标准库中有只用于Unicode字符串的函数Q如wcscpy(), swprintf(), _wtol()?/P>
微Y在Cq行?CRT)中加入了对DBCS字符串的支持。对应于strxxx()函数QDBCS使用_mbsxxx()函数。在处理DBCS字符?如日语,中文Q或其它DBCS)Ӟp用_mbsxxx()函数。这些函C能用于处理SBCS字符?因ؓDBCS字符串可能就只含有单字节字符)?/P>
现在用一个示例来说明字符串处理函数的不同。如有Unicode字符串L"Bob"Q?/P>
x86 CPU的排列顺序是低位优先(little-endian)的,?x0042的存储顺序ؓ42 00。这时如用strlen()函数求字W串的长度就发生问题。函数找到第一个字?2Q然后是00Q意味着字符串结,于是q回1。反之,用wcslen()函数?Bob"的长度更p糕。wcslen()首先扑ֈ0x6F42Q然后是0x0062Q以后就在内存缓冲内不断地寻?0 00直至发生一般性保护错(GPF)?/P>
strxxx()及其对应的_mbsxxx()I竟是如何运作的Q二者之间的不同是非帔R要的Q直接媄响到正确遍历DBCS字符串的Ҏ。下面先介绍字符串遍历,然后再回来讨论strxxx()?_mbsxxx()?/P>
我们中的大多Ch都是从SBCS成长q来的,都习惯于用指针的 ++ ?-- 操作W来遍历字符Ԍ有时也用数l来处理字符串中的字W。这二种Ҏ对于SBCS ?Unicode 字符串的操作都是正确无误的,因ؓ二者的字符都是{长的,~译器能够的正确q回我们L的字W位|?/P>
但对于DBCS字符串就不能q样了。用指针讉KDBCS字符串有二个原则Q打破这二个原则׃造成错误?/P>
1. 不可使用 ++ 子Q除非每ơ都查是否ؓ前导字节?/P>
2. l不可?-- 子来向后遍历?/P>
先说明原?Q因为很Ҏ扑ֈ一个非Zؓ的示例。假设,有一个配制文ӞE序启动时要从安装\径读取该文gQ如QC:\Program Files\MyCoolApp\config.bin。文件本w是正常的?/P>
假设用以下代码来配制文g名:
bool GetConfigFileName ( char* pszName, size_t nBuffSize )
{
char szConfigFilename[MAX_PATH];
// q里从注册表d文g的安装\径,假设一切正常?/FONT>
// 如果路径末尾没有反斜U,加上反斜线?/FONT>
// 首先Q用指针指向l尾Ӟ
char* pLastChar = strchr ( szConfigFilename, '\0' );
// 然后向后退一个字W:
pLastChar--;
if ( *pLastChar != '\\' )
strcat ( szConfigFilename, "\\" );
// 加上文g名:
strcat ( szConfigFilename, "config.bin" );
// 如果字符串长度够,q回文g名:
if ( strlen ( szConfigFilename ) >= nBuffSize )
return false;
else
{
strcpy ( pszName, szConfigFilename );
return true;
}
}
q段代码的保护性是很强的,但用到DBCS字符串还是会出错。假如文件的安装路径用日语表达:C:\ヨウユソQ该字符串的内存表达为:
q时用上面的GetConfigFileName()函数来检查文件\径末是否含有反斜线׃出错Q得到错误的文g名?BR>
错在哪里Q注意上面的二个十六q制?x5C(蓝色)。前面的0x5C是字W?\"Q后面则是字W?3 5CQ代表字W??。可是函数把它误认ؓ反斜U了?/P>
正确的方法是用DBCS函数指针指向恰当的字符位置Q如下所C:
bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )
{
char szConfigFilename[MAX_PATH];
// q里从注册表d文g的安装\径,假设一切正常?/FONT>
// 如果路径末尾没有反斜U,加上反斜线?/FONT>
// 首先Q用指针指向l尾Ӟ
char* pLastChar = _mbschr ( szConfigFilename, '\0' );
// 然后向后退一个双字节字符Q?/FONT>
pLastChar = CharPrev ( szConfigFilename, pLastChar );
if ( *pLastChar != '\\' )
_mbscat ( szConfigFilename, "\\" );
// 加上文g名:
_mbscat ( szConfigFilename, "config.bin" );
// 如果字符串长度够,q回文g名:
if ( _mbslen ( szInstallDir ) >= nBuffSize )
return false;
else
{
_mbscpy ( pszName, szConfigFilename );
return true;
}
}
q个改进的函数用CharPrev() API 函数指针pLastChar向后Ud一个字W。如果字W串末尾的字W是双字节字W,向后移?个字节。这时返回的l果是正的Q因Z会将字符误判为反斜线?/P>
现在可以惛_到第一原则了。例如,要遍历字W串L字符":"Q如果不使用CharNext()函数而?+子Q当跟随字节值恰好也?:"时就会出错?/P>
与原?相关的是数组下标的用:
2a. l不可在字符串数l中使用递减下标?/P>
出错原因与原?相同。例如,讄指针pLastChar为:
char* pLastChar = &szConfigFilename [strlen(szConfigFilename) - 1];
l果与原?的出错一栗下标减1是指针向后Ud一个字节,不符原则2?/P>
现在可以清楚Z么要?_mbsxxx() 函数了。strxxx() 函数不认识DBCS字符?_mbsxxx()认识。如果调用strrchr("C:\\", '\\')函数可能会出错,?_mbsrchr()认识双字节字W,所以能q回指向最后出现反斜线字符的指针位|?/P>
最后提一下strxxx() ?_mbsxxx() 函数族中的字W串长度量函数Q它们都q回字符串的字节数。如果字W串含有3个双字节字符Q_mbslen()返?。而Unicode的函数返回的是wchar_ts的数量,如wcslen(L"Bob") q回3
C++字符串完全指?- Win32字符~码Q二Q?BR>译Q连?
15/11/2002
URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098306,00.htm
也许你没有注意到QWin32的API和消息中的字W串处理函数有二U,一UؓMCBS字符Ԍ另一UؓUnicode字符丌Ӏ例如,Win32中没有SetWindowText()q样的接口,而是用SetWindowTextA()?SetWindowTextW()函数。后~A (表示ANSI)指明是MBCS函数Q后~W(表示宽字W?指明是Unicode函数?/P>
~写WindowsE序Ӟ可以选择用MBCS或Unicode API接口函数。用VC AppWizards向导Ӟ如果不修攚w处理器设|,~省使用的是MBCS函数。但是在API接口中没有SetWindowText()函数Q该如何调用呢?实际上,在winuser.h头文件中做了以下定义Q?/P>
BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );
BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );
#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif
~写MBCS应用Ӟ不必定义UNICODEQ预处理为:
#define SetWindowText SetWindowTextA
然后SetWindowText()处理为真正的API接口函数SetWindowTextA() (如果愿意的话Q可以直接调用SetWindowTextA() 或SetWindowTextW()函数Q不q很有此需??/P>
如果要将~省应用接口改ؓUnicodeQ就到预处理讄的预处理标记中去?_MBCS标记Q加入UNICODE ?_UNICODE (二个标记都要加入Q不同的头文件用不同的标记)。不q,q时要处理普通字W串反而会遇到问题。如有代码:
HWND hwnd = GetSomeWindowHandle();
char szNewText[] = "we love Bob!";
SetWindowText ( hwnd, szNewText );
~译器将"SetWindowText"|换?SetWindowTextW"后,代码变ؓQ?/P>
HWND hwnd = GetSomeWindowHandle();
char szNewText[] = "we love Bob!";
SetWindowTextW ( hwnd, szNewText );
看出问题了吧Q这里用一个Unicode字符串处理函数来处理单字节字W串?/P>
HWND hwnd = GetSomeWindowHandle();
#ifdef UNICODE
wchar_t szNewText[] = L"we love Bob!";
#else
char szNewText[] = "we love Bob!";
#endif
SetWindowText ( hwnd, szNewText );
要对每一个字W串都做q样的宏定义昄是o人头痛的。所以用TCHAR来解册个问题:
TCHAR 是一U字W类型,适用于MBCS ?Unicode二种~码。程序中也不必到处用宏定义?/P>
TCHAR的宏定义如下Q?/P>
#ifdef UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
所以,TCHAR中在MBCSE序中是charcdQ在Unicode中是 wchar_t cd?/P>
对于Unicode字符Ԍq有?_T() 宏,用于解决 L 前缀Q?/P>
#ifdef UNICODE
#define _T(x) L##x
#else
#define _T(x) x
#endif
## 是预处理子Q将二个变量_脓在一赗不什么时候都对字W串?_T 宏处理,q样可以在Unicode~码中给字符串加上L前缀Q如Q?/P>
TCHAR szNewText[] = _T("we love Bob!");
SetWindowTextA/W 函数族中q有其它隐藏的宏可以用来代替strxxx() ?_mbsxxx() 字符串函数。例如,可以?_tcsrchr 宏取代strrchr()Q_mbsrchr()Q或 wcsrchr()函数。_tcsrchr Ҏ~码标记为_MBCS ?UNICODEQ将叛_函数做相应的扩展处理。宏定义ҎcM于SetWindowText?/P>
不止strxxx()函数族中有TCHAR宏定义,其它一些函C也有。例如,_stprintf (取代sprintf()和swprintf())Q和 _tfopen (取代fopen() ?_wfopen())。MSDN的全部宏定义?Generic-Text Routine Mappings"栏目下?/P>
Win32 API 文g中列出的函数名都是通用??SetWindowText")Q所有的字符串都按照TCHARcd处理?只有XP除外QXP只用Unicodecd)。下面是MSDNl出的常用类型定义:
cd |
MBCS ~码中的意义 |
Unicode ~码中的意义 |
|
|
|
|
zero-terminated string of char (char |
zero-terminated string of char (char |
|
constant zero-terminated string of char (constchar |
constant zero-terminated string of char (constchar |
|
zero-terminated Unicode string ( |
zero-terminated Unicode string ( |
|
constant zero-terminated Unicode string (const |
constant zero-terminated Unicode string (const |
|
char |
|
|
zero-terminated string of |
zero-terminated string of |
|
constant zero-terminated string of |
constant zero-terminated string of |
可能会有疑问Q“ؓ什么要用UnicodeQ我一直用的都是普通字W串。?/P>
在三U情况下要用到UnicodeQ?/P>
大部分Unicode API不可用于Windows 9x。所以如果程序要在Windows 9x上运行的话,要强制用MBCS API (微Y推出一个可q行于Windows 9x的新库,叫做Microsoft Layer for Unicode。但我没有试用过Q无法说明它的好?。相反,NT内部全部使用Unicode~码Q用Unicode API可以加速程序运行。每当将字符串处理ؓMBCS APIӞ操作pȝ都会字W串转换为Unicodeq调用相应的Unicode API 函数。对于返回的字符Ԍ操作pȝ要做同样的{换。尽这些{换经q了高度优化Q模块尽可能地压~到最,但毕竟会影响到程序的q行速度?/P>
NT允许使用长文g?长于MAX_PATH 定义?60)Q但只限于Unicode API使用。Unicode API的另外一个优ҎE序能够自动处理输入的文字语a。用户可以合输入英文,中文和日文作为文件名。不必用其它代码来处理Q都按照Unicode~码方式处理?/P>
最后,作ؓWindows 9x的结局Q微软似乎抛弃了MBCS API。例如,SetWindowTheme() 接口函数的二个参数只支持Unicode~码。用Unicode~码省却了MBCS与Unicode之间的{换过E?/P>
如果E序中还没有使用到Unicode~码Q要坚持使用TCHAR和相应的宏。这样不但可以长期保持程序中DBCS~码的安全性,也利于将来扩展用到Unicode~码。那时只要改变预处理中的讄卛_Q?BR>
C++字符串完全指?2) - 各种字符串类Q一Q?BR>译Q连?
19/11/2002
URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098621,00.htm
前言
C语言的字W串Ҏ出错Q难以管理,q且往往是黑客到处寻扄目标。于是,出现了许多字W串包装cR可惜,Zq不很清楚什么情况下该用哪个c,也不清楚如何C语言字符串{换到包装cR?/P>
本文涉及到Win32 APIQMFCQSTLQWTL和Visual C++q行库中使用到的所有的字符串类型。说明各个类的用法,如何构造对象,如何q行c{换等{。Nish为本文提供了Visual C++ 7的managed string cȝ用法?/P>
阅读本文之前Q应完全理解本指南第一部分中阐q的字符cd和编码?/P>
字符串类的首要原则:
不要随便使用cd强制转换Q除非{换的cd是明由文档规定的?/P>
之所以撰写字W串指南q二文章,是因为常有h问到如何Xcd的字W串转换到Zcd。提问者用了强制cd转换(cast)Q但不知道ؓ什么不能{换成功。各U各L字符串类型,特别是BSTRQ在M场合都不是三a二语可以讲清的。因此,我以些提问者是惌强制cd转换来处理一切?/P>
除非明确规定了{换算子,不要Q何其它类型数据强制{换ؓstring。一个字W串不能用强制类型{换到stringcR例如:
void SomeFunc ( LPCWSTR widestr ); main() { SomeFunc ( (LPCWSTR) "C:\\foo.txt" ); // 错! }
q段代码100%错误。它可以通过~译Q因为类型强制{换超了~译器的cd验。但是,能够通过~译Qƈ不证明代码是正确的?/P>
下面Q我指Z么时候用cd强制转换是合理的?BR>C语言字符串与cd定义
如指南的W一部分所qͼWindows API定义了TCHAR术语。它可用于MBCS或Unicode~码字符Q取决于预处理设|ؓ_MBCS ?_UNICODE标记。关于TCHAR的详l说明请阅指南的W一部分。ؓ便于叙述Q下面给出字W类型定义:
Type |
Meaning |
|
Unicode character ( |
|
MBCS or Unicode character, depending on preprocessor settings |
|
string of char (char |
|
constant string of char (constchar |
|
string of |
|
constant string of |
|
string of |
|
constant string of |
另外q有一个字W类型OLECHAR。这是一U对象链接与嵌入的数据类?比如嵌入Word文档)。这个类型通常定义为wchar_t。如果将预处理设|定义ؓOLE2ANSIQOLECHAR被定义为charcd。现在已l不再定义OLE2ANSI(它只在MFC 3以前版本中?Q所以我OLECHAR作ؓUnicode字符处理?/P>
下面是与OLECHAR相关的类型定义:
Type |
Meaning |
|
Unicode character ( |
|
string of |
|
constant string of |
q有以下二个宏让相同的代码能够适用于MBCS和Unicode~码Q?/P>
Type |
Meaning |
|
Prepends |
|
Prepends |
宏_T有几UŞ式,功能都相同。如Q?-- TEXT, _TEXT, __TEXT, ?__Tq四U宏的功能相同?/P>
COM中的字符?/B> - BSTR ?/B> VARIANT
许多COM接口使用BSTR声明字符丌ӀBSTR有一些缺P所以我在这里让它独立成章?/P>
BSTR是Pascalcd字符?字符串长度值显式地与数据存攑֜一?和Ccd字符?字符串长度必通过L到结N字符来计?的合型字符丌ӀBSTR属于Unicode字符Ԍ字符串中预置了字W串长度|q且用一个零字符来结。下面是一?Bob"的BSTR字符Ԍ
注意Q字W串长度值是一个DWORDcd|l出字符串的字节长度Q但不包括结N。在上例Q?Bob"含有3个Unicode字符(不计l尾?Q?个字节长。因为明给Z字符串长度,所以当BSTR数据在不同的处理器和计算Z间传送时QCOM库能够知道应该传送的数据量?/P>
附带说一下,BSTR可以包含M数据块,不单是字W。它甚至可以包容内嵌零字W数据。这些不在本文讨围?/P>
C++中的BSTR变量其实是指向字符串首字符的指针。BSTR是这样定义的Q?/P>
typedef OLECHAR* BSTR;
q个定义很糟p,因ؓ事实上BSTR与Unicode字符串不一栗有了这个类型定义,pq了cd查,可以混合使用LPOLESTR和BSTR。向一个需要LPCOLESTR (?LPCWSTR)cd数据的函C递BSTR数据是安全的Q反之则不然。所以要清楚了解函数所需的字W串cdQƈ向函C递正类型的字符丌Ӏ?/P>
要知道ؓ什么向一个需要BSTRcd数据的函C递LPCWSTRcd数据是不安全的,别忘了BSTR必须在字W串开头的四个字节保留字符串长度倹{但LPCWSTR字符串中没有q个倹{当其它的处理过E?如Word)要寻找BSTR的长度值时׃扑ֈ一堆垃圾或堆栈中的其它数据或其它随机数据。这导致方法失效,当长度值太大时导致崩溃?/P>
许多应用接口都用BSTRQ但都用C个最重要的函数来构造和析构BSTR。就是SysAllocString()和SysFreeString()函数。SysAllocString()Unicode字符串拷贝到BSTRQSysFreeString()释放BSTR。示例如下:
BSTR bstr = NULL; bstr = SysAllocString ( L"Hi Bob!" ); if ( NULL == bstr ) // 内存溢出 // q里使用bstr SysFreeString ( bstr );
当然Q各UBSTR包装c都会小心地理内存?/P>
自动接口中的另一个数据类型是VARIANT。它用于在无cd语言Q诸如JScriptQVBScriptQ以及Visual BasicQ之间传递数据。VARIANT可以包容许多不用cd的数据,如long和IDispatch*。如果VARIANT包含一个字W串Q这个字W串是BSTRcd。在下文的VARIANT包装cM我还会谈及更多的VARIANT?BR>C++字符串完全指?2) - 各种字符串类- CRTc?BR>译Q连?
20/11/2002
URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098682,00.htm
_bstr_t
字符串包装类
我已l说明了字符串的各种cdQ现在讨论包装类。对于每个包装类Q我都会说明它的对象构造过E和如何转换成Ccd字符串指针。应用接口的调用Q或构造另一个不同类型的字符串类Q大多都要用到Ccd指针。本文不涉及cȝ其它操作Q如排序和比较等?/P>
再强调一下,在完全了解{换结果之前不要随意用强制类型{换?/P>
_bstr_t
_bstr_t 是BSTR的完全包装类。实际上Q它隐含了BSTR。它提供多种构造函敎ͼ能够处理隐含的Ccd字符丌Ӏ但它本w却不提供BSTR的处理机Ӟ所以不能作为COMҎ的输出参数[out]。如果要用到BSTR* cd数据Q用ATL的CComBSTRcL为方ѝ?/P>
_bstr_t 数据可以传递给需要BSTR数据的函敎ͼ但必L以下三个条Ӟ
首先Q_bstr_t h能够转换为wchar_t*cd数据的函数?/P>
其次Q根据BSTR定义Q得wchar_t* 和BSTR对于~译器来说是相同的?/P>
W三Q_bstr_t内部保留的指向内存数据块的指?wchar_t* 要遵循BSTR格式?/P>
满q些条gQ即使没有相应的BSTR转换文档Q_bstr_t 也能正常工作。示例如下:
// 构?/FONT> _bstr_t bs1 = "char string"; // 从LPCSTR构?/FONT> _bstr_t bs2 = L"wide char string"; // 从LPCWSTR构?/FONT> _bstr_t bs3 = bs1; // 拯另一?_bstr_t _variant_t v = "Bob"; _bstr_t bs4 = v; // 从一个含有字W串?_variant_t 构?/FONT> // 数据萃取 LPCSTR psz1 = bs1; // 自动转换到MBCS字符?/FONT> LPCSTR psz2 = (LPCSTR) bs1; // cast OK, 同上 LPCWSTR pwsz1 = bs1; // q回内部的Unicode字符?/FONT> LPCWSTR pwsz2 = (LPCWSTR) bs1; // cast OK, 同上 BSTR bstr = bs1.copy(); // 拯bs1, q回BSTR // ... SysFreeString ( bstr );
注意Q_bstr_t 也可以{换ؓchar* ?wchar_t*。这是个设计问题。虽然char* ?wchar_t*不是帔R指针Q但不能用于修改字符Ԍ因ؓ可能会打破内部BSTRl构?/P>
_variant_t
_variant_t
_variant_t 是VARIANT的完全包装类。它提供多种构造函数和数据转换函数。本文仅讨论与字W串有关的操作?/P>
// 构?/FONT> _variant_t v1 = "char string"; // 从LPCSTR 构?/FONT> _variant_t v2 = L"wide char string"; // 从LPCWSTR 构?/FONT> _bstr_t bs1 = "Bob"; _variant_t v3 = bs1; // 拯一?_bstr_t 对象 // 数据萃取 _bstr_t bs2 = v1; // 从VARIANT中提取BSTR _bstr_t bs3 = (_bstr_t) v1; // cast OK, 同上
注意Q_variant_t Ҏ在{换失败时会抛出异常,所以要准备用catch 捕捉_com_error异常?/P>
另外要注?_variant_t 不能直接转换成MBCS字符丌Ӏ要建立一个过渡的_bstr_t 变量Q用其它提供转换Unicode到MBCS的类函数Q或ATL转换宏来转换?/P>
与_bstr_t 不同Q_variant_t 数据可以作ؓ参数直接传送给COMҎ。_variant_t l承了VARIANTcdQ所以在需要用VARIANT的地方用_variant_t 是C++语言规则允许的?BR>C++字符串完全指?2) - STL和ATLc?BR>译Q连?
21/11/2002
URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098845,00.htm
STLc?/FONT>
STL只有一个字W串c,即basic_string。basic_string理一个零l尾的字W数l。字W类型由模板参数军_。通常Qbasic_string被处理ؓ不透明对象。可以获得一个只L针来讉K~冲区,但写操作都是由basic_string的成员函数进行的?/P>
basic_string预定义了二个特例QstringQ含有charcd字符QwhichQ含有wchar_tcd字符。没有内建的TCHAR特例Q可用下面的代码实现Q?/P>
// 特例?/FONT> typedef basic_stringtstring; // TCHAR字符?/FONT> // 构?/FONT> string str = "char string"; // 从LPCSTR构?/FONT> wstring wstr = L"wide char string"; // 从LPCWSTR构?/FONT> tstring tstr = _T("TCHAR string"); // 从LPCTSTR构?/FONT> // 数据萃取 LPCSTR psz = str.c_str(); // 指向str~冲区的只读指针 LPCWSTR pwsz = wstr.c_str(); // 指向wstr~冲区的只读指针 LPCTSTR ptsz = tstr.c_str(); // 指向tstr~冲区的只读指针
与_bstr_t 不同Qbasic_string不能在字W集之间q行转换。但是如果一个构造函数接受相应的字符cdQ可以将由c_str()q回的指针传递给q个构造函数。例如:
// 从basic_string构造_bstr_t _bstr_t bs1 = str.c_str(); // 从LPCSTR构?_bstr_t _bstr_t bs2 = wstr.c_str(); // 从LPCWSTR构?_bstr_t
CComBSTR 是ATL的BSTR包装cR某些情况下比_bstr_t 更有用。最主要的是QCComBSTR允许操作隐含BSTR。就是说Q传递一个CComBSTR对象lCOMҎӞCComBSTR对象会自动管理BSTR内存。例如,要调用下面的接口函数Q?/P>
// 单接?/FONT> struct IStuff : public IUnknown { // 略去COME序... STDMETHOD(SetText)(BSTR bsText); STDMETHOD(GetText)(BSTR* pbsText); };
CComBSTR 有一个BSTR操作ҎQ能BSTR直接传递给SetText()。还有一个引用操?operator &)ҎQ返回BSTR*Q将BSTR*传递给需要它的有兛_数?/P>
CComBSTR bs1; CComBSTR bs2 = "new text"; pStuff->GetText ( &bs1 ); // ok, 取得内部BSTR地址 pStuff->SetText ( bs2 ); // ok, 调用BSTR转换 pStuff->SetText ( (BSTR) bs2 ); // cast ok, 同上
CComVariant
CComBSTR有类g _bstr_t 的构造函数。但没有内徏MBCS字符串的转换函数。可以调用ATL宏进行{换?/P>
// 构?/FONT> CComBSTR bs1 = "char string"; // 从LPCSTR构?/FONT> CComBSTR bs2 = L"wide char string"; // 从LPCWSTR构?/FONT> CComBSTR bs3 = bs1; // 拯CComBSTR CComBSTR bs4; bs4.LoadString ( IDS_SOME_STR ); // 从字W串表加?/FONT> // 数据萃取 BSTR bstr1 = bs1; // q回内部BSTRQ但不可修改Q?/FONT> BSTR bstr2 = (BSTR) bs1; // cast ok, 同上 BSTR bstr3 = bs1.Copy(); // 拯bs1, q回BSTR BSTR bstr4; bstr4 = bs1.Detach(); // bs1不再理它的BSTR // ... SysFreeString ( bstr3 ); SysFreeString ( bstr4 );
上面的最后一个示例用CDetach()Ҏ。该Ҏ调用后,CComBSTR对象׃再管理它的BSTR或其相应内存。所以bstr4必调用SysFreeString()?/P>
最后讨Z下引用操作符(operator &)。它的超得有些STL集合(如list)不能直接使用CComBSTR。在集合上用引用操作返回指向包容类的指针。但是在CComBSTR上用引用操作,q回的是BSTR*Q不是CComBSTR*。不q可以用ATL的CAdaptcL解决q个问题。例如,要徏立一个CComBSTR的队列,可以声明为:
std::list< CAdapt> bstr_list;
CAdapt 提供集合所需的操作,是隐含于代码的。这时用bstr_list p在操作一个CComBSTR队列?/P>
CComVariant 是VARIANT的包装类。但?_variant_t 不同Q它的VARIANT不是隐含的,可以直接操作c里的VARIANT成员。CComVariant 提供多种构造函数和多类型操作。这里只介绍与字W串有关的操作?/P>
// 构?/FONT> CComVariant v1 = "char string"; // 从LPCSTR构?/FONT> CComVariant v2 = L"wide char string"; // 从LPCWSTR构?/FONT> CComBSTR bs1 = "BSTR bob"; CComVariant v3 = (BSTR) bs1; // 从BSTR拯 // 数据萃取 CComBSTR bs2 = v1.bstrVal; // 从VARIANT提取BSTR
跟_variant_t 不同QCComVariant没有不同VARIANTcd之间的{换操作。必ȝ接操作VARIANT成员Qƈ定该VARIANT的类型无误。调用ChangeType()Ҏ可将CComVariant数据转换为BSTR?/P>
CComVariant v4 = ... // 从某U类型初始化 v4 CComBSTR bs3; if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) )) bs3 = v4.bstrVal;
?_variant_t 一PCComVariant不能直接转换为MBCS字符丌Ӏ要建立一个过渡的_bstr_t 变量Q用其它提供转换Unicode到MBCS的类函数Q或ATL转换宏来转换?/P>
ATL转换?/FONT>
ATL的字W串转换宏可以方便地转换不同~码的字W,用在函数中很有效。宏按照[source type]2[new type] ?[source type]2C[new type]格式命名。后者{换ؓ一个常量指?(名字内含"C")。类型羃写如下:
例如QW2A() Unicode字符串{换ؓMBCS字符ԌT2CW()TCHAR字符串{换ؓUnicode字符串常量?/P>
要用宏转换Q程序中要包含atlconv.h头文件。可以在非ATLE序中用宏转换Q因为头文g不依赖其它的ATLQ也不需?_Module全局变量。如在函C使用转换宏,在函数v始处先写上USES_CONVERSION宏。它表明某些局部变量由宏控制用?/P>
转换得到的结果字W串Q只要不是BSTRQ都存储在堆栈中。如果要在函数外使用q些字符Ԍp这些字W串拯到其它的字符串类。如果结果是BSTRQ内存不会自动释放,因此必须返回值分配给一个BSTR变量或BSTR的包装类Q以避免内存泄露?/P>
下面是若q宏转换CZQ?/P>
// 带有字符串的函数Q?/FONT> void Foo ( LPCWSTR wstr ); void Bar ( BSTR bstr ); // q回字符串的函数Q?/FONT> void Baz ( BSTR* pbstr ); #includemain() { using std::string; USES_CONVERSION; // 声明局部变量由宏控制?/FONT> // CZ1Q送一个MBCS字符串到Foo() LPCSTR psz1 = "Bob"; string str1 = "Bob"; Foo ( A2CW(psz1) ); Foo ( A2CW(str1.c_str()) ); // CZ2Q将MBCS字符串和Unicode字符串送到Bar() LPCSTR psz2 = "Bob"; LPCWSTR wsz = L"Bob"; BSTR bs1; CComBSTR bs2; bs1 = A2BSTR(psz2); // 创徏 BSTR bs2.Attach ( W2BSTR(wsz) ); // 同上Q分配到CComBSTR Bar ( bs1 ); Bar ( bs2 ); SysFreeString ( bs1 ); // 释放bs1 // 不必释放bs2Q由CComBSTR释放?/FONT> // CZ3Q{换由Baz()q回的BSTR BSTR bs3 = NULL; string str2; Baz ( &bs3 ); // Baz() 填充bs3内容 str2 = W2CA(bs3); // 转换为MBCS字符?/FONT> SysFreeString ( bs3 ); // 释放bs3 }
可以看到Q向一个需要某U类型参数的函数传递另一U类型的参数Q用宏{换是非常方便的?BR>C++字符串完全指?2) - MFCc?BR>译Q连?
22/11/2002
URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098983,00.htm
MFCc?/FONT>
CString
MFC的CString含有TCHARQ它的实际字W类型取决于预处理标记的讄。通常QCString象STL字符串一h不透明对象Q只能用CString的方法来修改。CString比STL字符串更优越的是它的构造函数接受MBCS和Unicode字符丌Ӏƈ且可以{换ؓLPCTSTRQ因此可以向接受LPCTSTR的函数直接传递CString对象Q不必调用c_str()Ҏ?/P>
// 构? CString s1 = "char string"; // 从LPCSTR构? CString s2 = L"wide char string"; // 从LPCWSTR构? CString s3 ( ' ', 100 ); // 预分?00字节Q填充空? CString s4 = "New window text"; // 可以在LPCTSTR处用CStringQ? SetWindowText ( hwndSomeWindow, s4 ); // 或者,昑ּ地做强制cd转换Q? SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 );
也可以从字符串表加蝲字符丌ӀCString通过LoadString()来构造对象。用Format()Ҏ可有选择C字符串表d一定格式的字符丌Ӏ?/P>
// 从字W串表构?加蝲 CString s5 ( (LPCTSTR) IDS_SOME_STR ); // 从字W串表加? CString s6, s7; // 从字W串表加? s6.LoadString ( IDS_SOME_STR ); // 从字W串表加载打印格式的字符? s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... );
W一个构造函数看上去有点怪,但它的确是文档标定的字符串加载方式?/P>
注意QCString只允怸U强制类型{换,卛_制{换ؓLPCTSTR。强制{换ؓLPTSTR (非常量指?是错误的。按照老习惯,CString强制转换为LPTSTR只能伤害自己。有时在E序中没有发现出错,那只是碰巧。{换到非常量指针的正确Ҏ是调用GetBuffer()Ҏ?/P>
下面以往队列加入元素Z说明如何正确C用CStringQ?/P>
CString str = _T("new text"); LVITEM item = {0}; item.mask = LVIF_TEXT; item.iItem = 1; item.pszText = (LPTSTR)(LPCTSTR) str; // 错! item.pszText = str.GetBuffer(0); // 正确 ListView_SetItem ( &item ); str.ReleaseBuffer(); // 队列返回给str
pszText成员是LPTSTRQ一个非帔R指针Q因此要用str的GetBuffer()。GetBuffer()的参数是CString分配的最缓冲区。如果要分配一?K的TCHARQ调用GetBuffer(1024)。参Cؓ0Q只q回指向字符串的指针?/P>
上面CZ的出错语句可以通过~译Q甚臛_以正常工作,如果恰好是q个cd。但q不证明语法正确。进行非帔R的强制类型{换,打破了面向对象的装原则Qƈ逾越了CString的内部操作。如果你习惯q行q样的强制类型{换,l会遇到出错Q可你未必知道错在何处,因ؓ你到处都在做q样的{换,而代码也都能q行?/P>
知道Z么h们d抱怨有~陷的Y件吗Q不正确的代码就臭虫的滋生地。然道你愿意~写明知有错的代码让臭虫有机可乘Q还是花些时间学习CString的正用法让你的代码能够100%的正吧?/P>
CStringq有二个函数能够从CString中得到BSTRQƈ在必要时转换成Unicode。那是AllocSysString()和SetSysString()。除了SetSysString()使用BSTR*参数外,二者一栗?/P>
// 转换成BSTR CString s5 = "Bob!"; BSTR bs1 = NULL, bs2 = NULL; bs1 = s5.AllocSysString(); s5.SetSysString ( &bs2 ); // ... SysFreeString ( bs1 ); SysFreeString ( bs2 );
COleVariant 与CComVariant 非常怼。COleVariant l承于VARIANTQ可以传递给需要VARIANT的函数。但又与CComVariant 不同QCOleVariant 只有一个LPCTSTR的构造函敎ͼ不提供单独的LPCSTR和LPCWSTR的构造函数。在大多情况下,没有问题Q因为L愿意把字W串处理为LPCTSTR。但你必ȝ道这炏VCOleVariant 也有接受CString的构造函数?/P>
// 构? CString s1 = _T("tchar string"); COleVariant v1 = _T("Bob"); // 从LPCTSTR构? COleVariant v2 = s1; // 从CString拯
对于CComVariantQ必ȝ接处理VARIANT成员Q用ChangeType()Ҏ在必要时其转换为字W串。但是,COleVariant::ChangeType() 在{换失败时会抛出异常,而不是返回HRESULT的出错码?/P>
// 数据萃取 COleVariant v3 = ...; // 从某U类型构造v3 BSTR bs = NULL; try { v3.ChangeType ( VT_BSTR ); bs = v3.bstrVal; } catch ( COleException* e ) { // 出错Q无法{? } SysFreeString ( bs );
WTLc?/FONT>
CString
WTL的CString与MFC的CString的行为完全相同,参阅上面关于MFC CString的说明即可?/P>
System::String ?NET的字W串cR在其内部,String对象是一个不变的字符序列。Q何操作String对象的StringҎ都返回一个新的String对象Q因为原有的String对象要保持不变。StringcL一个特性,当多个String都指向同一l字W集Ӟ它们其实是指向同一个对象。Managed Extensions C++ 的字W串有一个新的前~SQ用来表明是一个managed string字符丌Ӏ?/P>
// 构? String* ms = S"This is a nice managed string";
可以用unmanaged string字符串来构造String对象Q但不如用managed string构造String对象有效。原因是所有相同的hS前缀的字W串都指向同一个对象,而unmanaged string没有q个特点。下面的例子可以说明得更清楚些:
String* ms1 = S"this is nice"; String* ms2 = S"this is nice"; String* ms3 = L"this is nice"; Console::WriteLine ( ms1 == ms2 ); // 输出true Console::WriteLine ( ms1 == ms3); // 输出false
要与没有S前缀的字W串做比较,用String::CompareTo()Ҏ来实玎ͼ如:
Console::WriteLine ( ms1->CompareTo(ms2) ); Console::WriteLine ( ms1->CompareTo(ms3) );
二者都输出0Q说明字W串相等?/P>
在String和MFC 7的CString之间转换很容易。CString可以转换为LPCTSTRQString有接受char* ?wchar_t* 的二U构造函数。因此可以直接把CString传递给String的构造函敎ͼ
CString s1 ( "hello world" ); String* s2 ( s1 ); // 从CString拯
反向转换的方法也cMQ?/P>
String* s1 = S"Three cats"; CString s2 ( s1 );
可能有点qh。从VS.NET开始,CString有一个接受String对象的构造函敎ͼ所以是正确的?/P>
CStringT ( System::String* pString );
Z加速操作,有时可以用基字符?underlying string)Q?/P>
String* s1 = S"Three cats"; Console::WriteLine ( s1 ); const __wchar_t __pin* pstr = PtrToStringChars(s1); for ( int i = 0; i < wcslen(pstr); i++ ) (*const_cast<__wchar_t*>(pstr+i))++; Console::WriteLine ( s1 );
PtrToStringChars() q回指向基础字符串的 const __wchar_t* 指针Q可以防止在操作字符串时Q垃圾收集器去除该字W串?BR>C++字符串完全指?2) - ȝ
译Q连?
23/11/2002
URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39099061,00.htm
对字W串包装cM用printf()或其它类似功能的函数时要特别心。包括sprintf()函数及其变种Q以及TRACE 和ATLTRACE 宏。它们的参数都不做类型检验,一定要l它们传递C语言字符Ԍ而不是整个string对象?/P>
例如Q要向ATLTRACE()传递一个_bstr_t 里的字符Ԍ必须昑ּ?LPCSTR)?(LPCWSTR)q行强制cd转换Q?/P>
_bstr_t bs = L"Bob!"; ATLTRACE("The string is: %s in line %d\n", (LPCSTR) bs, nLine);
如果忘了用强制类型{换,直接把整?_bstr_t 对象传递给ATLTRACEQ跟t消息将输出无意义的东西Q因为_bstr_t 变量内的所有数据都q栈了?/P>
常用的字W串cM间的转换Ҏ是:源字符串{换ؓCcd字符串指针,然后该指针传递给目标cȝ构造函数。下面列出将字符串{换ؓCcd指针的方法,以及哪些cȝ构造函数接受Ccd指针?/P>
Class |
string |
convert to char |
convert to constchar |
convert to |
convert to const |
convert to |
construct from char |
construct from |
|
|
yes, cast1 |
yes, cast |
yes, cast1 |
yes, cast |
yes2 |
yes |
yes |
|
|
no |
no |
no |
cast to |
cast to |
yes |
yes |
|
MBCS |
no |
yes, c_str() |
no |
no |
no |
yes |
no |
|
Unicode |
no |
no |
no |
yes, c_str() |
no |
no |
yes |
|
|
no |
no |
no |
yes, cast |
yes, cast |
yes |
yes |
|
|
no |
no |
no |
yes4 |
yes4 |
yes |
yes |
|
|
no6 |
in MBCS |
no6 |
in Unicode |
no5 |
yes |
yes |
|
|
no |
no |
no |
yes4 |
yes4 |
in MBCS builds |
in Unicode builds |
附注Q?/B>