14.2 使用Xerces字符串
问题
如何安全地和容易地使用Xerces类库来处理宽字符串?特别地,你需要能够存储由Xerces函数返回的字符串,并且在Xerces字符串和C++标准类库字符串之间相互转换。
解决方案
你可以使用由Xerces宽字符类型XMLCh特殊化模板std::basic_string的Xerces类库函数来存储宽字符串。
typedef std::basic_string<XMLCh> XercesString;
为了在Xerces字符串和窄字符串之间相互转换,可以使用来自类xercesc::XMLString的重载静态方法transcode(),这个方法定义在头文件xercesc/util/XMLString.hpp中。示例14-4定义了两个重载函数toNative和fromNative函数,它们使用transcode函数来在窄字符串和Xerces字符串之间相互转换。每个函数都有两个变体,一个带有C风格的字符串,而另一个带有C++风格的字符串。你可以使用这些函数在Xerces字符串和窄字符串之间相互转换;一旦定义了它们,你就可以不用再直接调用transcode方法。
示例14-4 头文件xerces_strings.hpp,它用于在Xerces字符串和窄字符串之间相互转换
#ifndef XERCES_STRINGS_HPP_INCLUDED
#define XERCES_STRINGS_HPP_INCLUDED
#include <string>
#include <boost/scoped_array.hpp>
#include <xercesc/util/XMLString.hpp>
typedef std::basic_string<XMLCh> XercesString;
// Converts from a narrow-character string to a wide-character string.
inline XercesString fromNative(const char* str)
{
boost::scoped_array<XMLCh> ptr(xercesc::XMLString::transcode(str));
return XercesString(ptr.get());
}
// Converts from a narrow-character string to a wide-charactr string.
inline XercesString fromNative(const std::string& str)
{
return fromNative(str.c_str());
}
// Converts from a wide-character string to a narrow-character string.
inline std::string toNative(const XMLCh* str)
{
boost::scoped_array<char> ptr(xercesc::XMLString::transcode(str));
return std::string(ptr.get());
}
// Converts from a wide-character string to a narrow-character string.
inline std::string toNative(const XercesString& str)
{
return toNative(str.c_str());
}
#endif // #ifndef XERCES_STRINGS_HPP_INCLUDED
为了在Xerces字符串和std::wstring字符串之间相互转换,简单地使用std::basic_string构造函数,它带一对迭代器作为参数。例如,你可以如下这样定义两个函数:
// Converts from a Xerces String to a std::wstring
std::wstring xercesToWstring(const XercesString& str)
{
return std::wstring(str.begin(), str.end());
}
// Converts from a std::wstring to a XercesString
XercesString wstringToXerces(const std::wstring& str)
{
return XercesString(str.begin(), str.end());
}
这些函数都依赖于一个事实,那就是wchar_t和XMLCh都是完整的类型,它们都可以隐式地相互之间转换;并且不用考虑这个wchar_t的宽度大小,只要它的值没有超过XMLCh使用的范围就可以。你也可以使用带一个字符数组和它的长度作为参数的std::basic_string构造函数来定义带C风格字符串作为参数的类似函数。
讨论
Xerces使用以null结尾的XMLCh字符类型序列来代表Unicode字符串。XMLCh是一个实现定义好的完整类型的typedef,这个完整类型具有最少16位的字符宽度,这样足够代表现在在任何语言当中使用的已知字符。Xerces使用UTF-16字符编码,理论上就意味着一些Unicode字符必须通过多个XMLCh字符的序列来代表;然而实际上,你可以把一个XMLCh字符直接想象成一个Unicode编码点,例如一个Unicode字符的数字值。
曾经,XMLCh被定义成一个XMLCh的typedef,这就意味着你可以很容易地作为一个std::wstring来存储一个Xerces字符。然而同时,在所有的平台上Xerces类库又把XMLCh重定义成一个无符号短整型。也就意味着在一个平台上XMLCh和wchar_t不具有相同的宽度。由于Xerces可能在将来改变这个XMLCh的定义,所以你不能指望XMLCh与任何特殊的类型都相同。因此如果你需要存储一个Xerces字符串的副本,你应该使用std::basic_string<XMLCh>。
当使用Xerces类库时,你需要经常在窄字符串和Xerces字符串之间相互转换;为了这个目的,Xerces提供了一个重载函数transcode()。transcode()函数可以把一个Unicode字符串转换成一个使用本地字符编码的窄字符串或者把一个使用本地字符编码的窄字符串转换成一个Unicode字符串。这个本地编码并没有精确的定义,然而,如果你在一个有多个经常使用的字符编码的环境中编程时,你就需要考虑你自己的处理并执行一些你自己的转换,或者通过使用std::codecvt功能,或者使用支持Xerces可插拔的代码转换服务,这个服务在Xerces的文档中有相应的描述。在很多情况下,transcode()可以满足你的需求。
这个由transcode()返回的一个null结尾的字符串是使用new操作符分配的一个数组形式,因此你需要使用delete[]来删除它。这导致了一个轻微的内存管理问题,由于典型地你需要在删除这个字符串之前复制它或把它输出到一个流中去,而这些操作是有可能抛出异常的。在示例14-4中我通过使用模板boost::scoped_array来阐述这个问题,这个模板可以动态地分配一个数组并且当它超出了它的生存空间时自动地删除它,即使是在抛出异常的情况下也能删除它。例如,看一下fromNative函数的实现:
inline XercesString fromNative(const char* str)
{
boost::scoped_array<XMLCh> ptr(xercesc::XMLString::transcode(str));
return XercesString(ptr.get());
}
这里,ptr拥有这个由transcode()返回的以null结尾的字符串,并且即使是XercesString构造函数抛出一个std::bad_alloc异常的情况下也能释放它。






