【问题标题】:XMLCh to wchar_t and vice versaXMLCh 到 wchar_t,反之亦然
【发布时间】:2014-11-08 11:43:25
【问题描述】:

我的配置:

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

我想用 wchar_t* 提供一个方法,并在许多需要 XMLCh* 的 xecerces 库方法中使用它,但我不知道如何从一个转换到另一个。如果您使用 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;
}

【问题讨论】:

标签: c++ wchar-t wstring


【解决方案1】:

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

不幸的是,gcc 4.8.2 不支持 std::wstring_convert 转换 unicode 字符串的编码。但是您可以使用Boost 的 locale::conv::utf_to_utf() 与 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();

【讨论】:

    【解决方案2】:

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

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

    • char16_tXmlCh 是等价的,但不能隐式转换;您仍然需要在它们之间进行转换。但至少这比转码便宜。
    • std::basic_string&lt;char16_t&gt; 是等效的字符串类型。
    • 使用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 文字,我才能看到它被整理出来,从而允许直接用 char16_t 重写 Xerces。

    【讨论】:

    • 没问题。但是请注意,如果没有一些额外的魔法,流将无法工作,特别是 std::ctype、std::numpunct 和 std::codecvt 的实现(最后一个可能是错误的方式)。然后,您需要将这些方面以及 std::num_get 和 std::num_put 添加到全局语言环境。
    • 可以使用 Xersesc 定义 _NATIVE_WCHAR_T_DEFINED
    【解决方案3】:

    我最近处理了这个问题,现在 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 将失败并引起对问题的关注。

    【讨论】:

    • 非常有趣。哪个 c++ 版本会支持它?
    • UTF-16 文字和 char16_t 原始类型被添加到 C++11 的标准中(所以几乎任何现代版本的 GCC 或 Clang 都应该有它们),但 MSVC 没有支持他们直到 VS2015。
    猜你喜欢
    • 2014-01-23
    • 2012-02-16
    • 2013-08-16
    • 2012-06-27
    • 2017-02-05
    • 2012-12-01
    • 2012-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多