XMLCh到wchar_t,反之亦然

XMLCh to wchar_t and vice versa

本文关键字:反之亦然 wchar XMLCh      更新时间:2023-10-16

我的配置:

  • 编译器:gnu gcc 4.8.2
  • 我用C++11编译
  • 平台/OS:Linux 64位Ubuntu 14.04.1 LTS

我想用wchar_t*提供一个方法,并在许多需要XMLCh*的execerces库方法中使用它,但我不知道如何从一个方法转换到另一个方法。如果您使用char*而不是wchar_t*,这很容易,但我需要使用宽字符。在windows下,我可以很容易地从一个转换到另一个,但它在我的linux机器上不起作用。不知怎么的,我不得不手动将wchar_t*转换为XMLCh*

我链接到库libxerces-c-3.1.so,该库专门使用XMLCh*。XMLCh可以处理宽字符,但我不知道如何将其反馈给它,也不知道如何从XMLCh*返回wchar_t*

我开发了这个,但它不起作用(这里我吐出了一个wstring,它在清理内存时比指针更容易管理:

static inline std::wstring XMLCh2W(const XMLCh* tagname)
{
    std::wstring wstr;
    XMLSize_t len1 = XMLString::stringLen(tagname);
    XMLSize_t outLen = len1 * 4;
    XMLByte ut8[outLen+1];
    XMLSize_t charsEaten = 0;
    XMLTransService::Codes failReason; //Ok | UnsupportedEncoding | InternalFailure | SupportFilesNotFound
    XMLTranscoder* transcoder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor("UTF-8", failReason,16*1024);
    unsigned int utf8Len = transcoder->transcodeTo(tagname,len1,ut8,outLen,charsEaten,XMLTranscoder::UnRep_Throw);// XMLTranscoder::UnRep_Throw UnRep_RepChar
    ut8[utf8Len] = 0;
    std::wstring wstr = std::wstring((wchar_t*)ut8);//I'm not sure this is actually ok to do
    return wstr;
}

XMLCh由wchar_t(在windows上)或uint16_t(Linux上)定义,并使用UTF-16进行编码。

不幸的是,gcc 4.8.2不支持std::wstring_convert来转换unicode字符串的编码。但是您可以使用Boost的locale::conv::utf_to_utf()转换为XMLCh或从XMLCh转换。

#include <boost/locale.hpp>
static inline std::wstring XMLCh2W(const XMLCh* xmlchstr)
{
    std::wstring wstr = boost::locale::conv::utf_to_utf<wchar_t>(xmlchstr);
    return wstr;
}
static inline std::basic_string<XMLCh> W2XMLCh(const std::wstring& wstr)
{
    std::basic_string<XMLCh> xmlstr = boost::locale::conv::utf_to_utf<XMLCh>(wstr);
    return xmlstr;
}

如果要使用wchar_t*或XMLCh*,请使用下面的c_str()方法。

const wchar_t* wcharPointer = wstr.c_str();
const XMLCh* xmlchPointer = xmlstr.c_str();

不,在GCC下不能这样做,因为GCC将wchar_t定义为32位UTF-32/UCS-4编码的字符串(出于实际目的,差异并不重要),而Xerces-c将XmlCh定义为16位UTF-16编码的字符串。

我发现最好的是使用C++11支持UTF-16字符串:

  • char16_tXmlCh是等价的,但不能隐式转换;你仍然需要在它们之间做出选择。但至少与代码转换相比,这是便宜的
  • std::basic_string<char16_t>是等效的字符串类型
  • 使用形式为u"str"u's'的文字

不幸的是,VC++不支持C++11 UTF-16文字,尽管wchar_t文字是UTF-16编码的。所以我在一个标题中得到了这样的东西:

#if defined _MSC_VER
#define U16S(x) L##x
typedef wchar_t my_u16_char_t;
typedef std::wstring my_u16_string_t;
typedef std::wstringstream my_u16_sstream_t;
inline XmlCh* XmlString(my_u16_char_t* s) { return s; }
inline XmlCh* XmlString(my_u16_string_t* s) { return s.c_str(); }
#elif defined __linux
#define U16S(x) u##x
typedef char16_t my_u16_char_t;
typedef std::basic_string<my_u16_char_t> my_u16_string_t;
typedef std::basic_stringstream<my_u16_char_t> my_u16_sstream_t;
inline XmlCh* XmlString(my_u16_char_t* s) { return reinterpret_cast<XmlCh*>(s); }
inline XmlCh* XmlString(my_u16_string_t* s) { return XmlString(s.c_str()); }
#endif

IMO,这是一个相当混乱的问题,但在VC++支持C++11 Unicode文本,允许Xerces直接用char16_t重写之前,我看不出有什么问题会得到解决。

我最近处理过这个问题,现在Visual Studio 2015支持Unicode字符和字符串文字,这很容易用跨平台的方式处理。我使用以下宏和static_assert来保证正确性:

#define CONST_XMLCH(s) reinterpret_cast<const ::XMLCh*>(u ## s)
static_assert(sizeof(::XMLCh) == sizeof(char16_t), 
    "XMLCh is not sized correctly for UTF-16.");

用法示例:

const XMLCh* features = CONST_XMLCH("Core");
auto impl = DOMImplementationRegistry::getDOMImplementation(features);

这是因为Xerces将XMLCh定义为16位宽,并保存UTF-16字符串值,这与以u为前缀的字符串文字标准给出的定义完全匹配。编译器不知道这一点,也不会在char16_t*XMLCh*之间隐式转换,但使用reinterpret_cast可以绕过这一点。如果出于任何原因,您试图在大小不匹配的平台上编译Xerces,static_assert将失败,并引起人们对问题的关注。