Xerces-C 2.1.0 本身是不支持GB2312编码的,就是说你不能直接
用它去操作GB2312编码的xml文件,需要打patch。
问题是出在Win32TransService、Win32LCPTranscoder、Win32Transcoder
上(别的平台是其相应的Transxxxxxx)
我只说说Win32平台的修改办法,其他平台如果有问题,你需要自己
去跟踪解决。
下面是Xerces的程序流程:
首先,Xerces做parse前第一步是将xml文件从原来的编码转换成UTF-16。
而在Win32下的代码转换工作( XML文件的原始编码<-> UTF-16 ) 是由
Win32TransService、Win32LCPTranscoder、Win32Transcoder完成,都在
Win32TransService.hpp中定义。
其中LCPTranscoder是为XMLString::transcode服务的,而在将xml文件
内容转换成为UTF-16中用的是Win32TransService,TransService会去new
一个Win32Transcoder。XMLRecognizer会去比较XML文件的前若干字节,
试图判断出文件的编码是ASCII/UTF-16LE/UTF-16BE/UTF-32中的那一种。
这个很大程度上是靠BOM和前面的<?xml这几个字符来判断的。
对于GB2312编码的xml文件来说,Xerces都应该会识别为ASCII(MBCS?),
因为GB2312没有BOM,前面的"<?xml"和普通的ASCII也一样(这个我是一
半跟踪,一半靠猜的)。
然后xerces会去查找"encoding=LANG"这个声明。如果没有encoding声明,
比如:
<?xml version="1.0" ?>
<AB>一二三四五六七八九12345678</AB>
parse的时候就会出错,自己用Xerces的例子DOMPrint试试就知道了。
但如果上面的例子加入了"encoding=GB2312",那么xerces就可以知道这是
什么多字节编码,然后用注册表的API去HCR\MIME\Database\Charset中查找
encoding后面那个字符串的Section(即"GB2312"),从而得到GB2312对应的
Codepage(936)。有了codepage代号,在Win32Transcoder里面的就可以调用
WideCharToMultiByte和MultiByteToWideChar两个API函数进行
GB2312<->UTF-16(Unicode)之间转换,而其中最重要的参数codepage就是上
面提到的那个文件编码对应的代码页(对GB2312就是936)。
至此,xml文档由原来编码转换成UTF-16就可以顺利完成了。其核心是将编码
命名(GB2312)对应的codepage代号936找到,然后调用Windows API实现
GB2312<->UTF-16。
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
(以上部分是以前写的,应该都是准确的,下面的是现在写的,因为现在很久
没玩了Xerces-C++了,所以靠的是回忆。不一定准确,如果有出入,请自行解决)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
只要你的Win32上装了cp936,这个转换都是会成功的。就是说,Xerces-C++
能正确读入和解析GB2312的文件。但是当你用XMLString::transcode会友问题。
上面说过,XMLString用的是LCPTranscoder。而Win32LCPTranscoder有问题。
它用wcstombs和mbstowcs去做Local codepage <-> Unicode,但是却没有首先
setlocale,所以wcstombs,mbstowcs根本根本不知道local codepage是什么。
所以,加入以下一行
Win32LCPTranscoder::Win32LCPTranscoder()
{
#ifdef XERCES_DEBUG
cout << "Setlocale(LC_CTYPE):" << setlocale(LC_CTYPE,"") << "\n" << endl;
#else
setlocale(LC_CTYPE,""); //选用系统default codepage
#endif
}
上面代码是在Win2k CHS版本中的,因为简体中文版的default codepage就是cp936,
即GBK。如果你是Win2K Englist版本,想用中文,应该
setlocale(LC_CTYPE, ".cp936");
下面是一个以前写的XMLString测试程序,你可以在在patch前后运行一下,看看
XMLString的效果。
test.cpp
------------------------------------
int main(int argc,char* argv[])
{
//Sets the locale to the default, which is the system-default //code page obtained from the operating system.
setlocale(LC_CTYPE,"");
// Initialize the XML4C2 system
try
{
XMLPlatformUtils::Initialize();
}
catch (const XMLException& toCatch)
{
cerr << "Error during initialization! :\n"
<< StrX(toCatch.getMessage()) << endl;
return 1;
}
//a Chinese Character in MBCS, 2 bytes(0xB5,0xE7).
// in Unicode, it should be 0x7535(2 bytes)
char szTEST[] = "电";
//so: szTEST[0]:0xB5, szTEST[1]:0xE7, szTEST[2]:0x00, strlen(szTEST) = 2;
assert(strlen(szTEST)== 2);
XMLCh* xTEST = XMLString::transcode(szTEST);
//the character is 0x7535 in unicode, wcslen(xTEST)=1
assert(xTEST[0] == 0x7535 && xTEST[1] == 0); //error in here
//actual result:xTEST[0]:0x00B5 , xTEST[1]:0x00E7 , xTEST[2]: 0x0000 ,
//wcslen(xTEST) = 2
//but, the functions: mbstowcs & wcstombs is correct
wchar_t wcs[2] = L"";
return 0;
}
在main()第一行,我加入了setlocale(),但是如果LCPTranscoder中的mbstowcs依然不能
正常工作,必须patch.但是main()中直接调mbstowcs是正常的。
写得有点乱,因为最近忙,而且很久没用Xerces-C++,好多东西忘了。有问题自己去跟踪
吧。
各平台下测试Xerces-C对特定编码的支持,可按照以下步骤:
先测试XMLString是否正确支持 Local codepage <-> UTF-16,如果OK,
说明LCPTranscoder没有问题。
然后测试Xerces-C++能否正确读入指定编码的xml文件,如果OK就说明Transcoder没有
问题。自己去给有问题的地方打patch。
LCPTranscoder的问题一半出在mbstowcs,wcstombs,往往是locale不对。
Transcoder的问题出在系统没有提供 xml文件编码<->UTF-16 的库,或者查找/调用这个
库的时候有问题。
Win32平台的问题就是出在LCPTranscoder
而Linux平台下是因为Transcoder和LCPTranscoder都有问题。
需要注意的是,Linux的wchar_t是32位的UTF32,如果要转换成UTF16,有两个方法:
1) 用iconv库,直接从 xml文件编码<->UTF16, IconvGNU TransService用的就是这个
2) 用mbstowbc和wcstombs
mbstowcs 位操作
xml文件编码 <---------> UTF-32 <----------> UTF-16
wcstombs 位操作
"AS-IS" article
用它去操作GB2312编码的xml文件,需要打patch。
问题是出在Win32TransService、Win32LCPTranscoder、Win32Transcoder
上(别的平台是其相应的Transxxxxxx)
我只说说Win32平台的修改办法,其他平台如果有问题,你需要自己
去跟踪解决。
下面是Xerces的程序流程:
首先,Xerces做parse前第一步是将xml文件从原来的编码转换成UTF-16。
而在Win32下的代码转换工作( XML文件的原始编码<-> UTF-16 ) 是由
Win32TransService、Win32LCPTranscoder、Win32Transcoder完成,都在
Win32TransService.hpp中定义。
其中LCPTranscoder是为XMLString::transcode服务的,而在将xml文件
内容转换成为UTF-16中用的是Win32TransService,TransService会去new
一个Win32Transcoder。XMLRecognizer会去比较XML文件的前若干字节,
试图判断出文件的编码是ASCII/UTF-16LE/UTF-16BE/UTF-32中的那一种。
这个很大程度上是靠BOM和前面的<?xml这几个字符来判断的。
对于GB2312编码的xml文件来说,Xerces都应该会识别为ASCII(MBCS?),
因为GB2312没有BOM,前面的"<?xml"和普通的ASCII也一样(这个我是一
半跟踪,一半靠猜的)。
然后xerces会去查找"encoding=LANG"这个声明。如果没有encoding声明,
比如:
<?xml version="1.0" ?>
<AB>一二三四五六七八九12345678</AB>
parse的时候就会出错,自己用Xerces的例子DOMPrint试试就知道了。
但如果上面的例子加入了"encoding=GB2312",那么xerces就可以知道这是
什么多字节编码,然后用注册表的API去HCR\MIME\Database\Charset中查找
encoding后面那个字符串的Section(即"GB2312"),从而得到GB2312对应的
Codepage(936)。有了codepage代号,在Win32Transcoder里面的就可以调用
WideCharToMultiByte和MultiByteToWideChar两个API函数进行
GB2312<->UTF-16(Unicode)之间转换,而其中最重要的参数codepage就是上
面提到的那个文件编码对应的代码页(对GB2312就是936)。
至此,xml文档由原来编码转换成UTF-16就可以顺利完成了。其核心是将编码
命名(GB2312)对应的codepage代号936找到,然后调用Windows API实现
GB2312<->UTF-16。
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
(以上部分是以前写的,应该都是准确的,下面的是现在写的,因为现在很久
没玩了Xerces-C++了,所以靠的是回忆。不一定准确,如果有出入,请自行解决)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
只要你的Win32上装了cp936,这个转换都是会成功的。就是说,Xerces-C++
能正确读入和解析GB2312的文件。但是当你用XMLString::transcode会友问题。
上面说过,XMLString用的是LCPTranscoder。而Win32LCPTranscoder有问题。
它用wcstombs和mbstowcs去做Local codepage <-> Unicode,但是却没有首先
setlocale,所以wcstombs,mbstowcs根本根本不知道local codepage是什么。
所以,加入以下一行
Win32LCPTranscoder::Win32LCPTranscoder()
{
#ifdef XERCES_DEBUG
cout << "Setlocale(LC_CTYPE):" << setlocale(LC_CTYPE,"") << "\n" << endl;
#else
setlocale(LC_CTYPE,""); //选用系统default codepage
#endif
}
上面代码是在Win2k CHS版本中的,因为简体中文版的default codepage就是cp936,
即GBK。如果你是Win2K Englist版本,想用中文,应该
setlocale(LC_CTYPE, ".cp936");
下面是一个以前写的XMLString测试程序,你可以在在patch前后运行一下,看看
XMLString的效果。
test.cpp
------------------------------------
int main(int argc,char* argv[])
{
//Sets the locale to the default, which is the system-default //code page obtained from the operating system.
setlocale(LC_CTYPE,"");
// Initialize the XML4C2 system
try
{
XMLPlatformUtils::Initialize();
}
catch (const XMLException& toCatch)
{
cerr << "Error during initialization! :\n"
<< StrX(toCatch.getMessage()) << endl;
return 1;
}
//a Chinese Character in MBCS, 2 bytes(0xB5,0xE7).
// in Unicode, it should be 0x7535(2 bytes)
char szTEST[] = "电";
//so: szTEST[0]:0xB5, szTEST[1]:0xE7, szTEST[2]:0x00, strlen(szTEST) = 2;
assert(strlen(szTEST)== 2);
XMLCh* xTEST = XMLString::transcode(szTEST);
//the character is 0x7535 in unicode, wcslen(xTEST)=1
assert(xTEST[0] == 0x7535 && xTEST[1] == 0); //error in here
//actual result:xTEST[0]:0x00B5 , xTEST[1]:0x00E7 , xTEST[2]: 0x0000 ,
//wcslen(xTEST) = 2
//but, the functions: mbstowcs & wcstombs is correct
wchar_t wcs[2] = L"";
return 0;
}
在main()第一行,我加入了setlocale(),但是如果LCPTranscoder中的mbstowcs依然不能
正常工作,必须patch.但是main()中直接调mbstowcs是正常的。
写得有点乱,因为最近忙,而且很久没用Xerces-C++,好多东西忘了。有问题自己去跟踪
吧。
各平台下测试Xerces-C对特定编码的支持,可按照以下步骤:
先测试XMLString是否正确支持 Local codepage <-> UTF-16,如果OK,
说明LCPTranscoder没有问题。
然后测试Xerces-C++能否正确读入指定编码的xml文件,如果OK就说明Transcoder没有
问题。自己去给有问题的地方打patch。
LCPTranscoder的问题一半出在mbstowcs,wcstombs,往往是locale不对。
Transcoder的问题出在系统没有提供 xml文件编码<->UTF-16 的库,或者查找/调用这个
库的时候有问题。
Win32平台的问题就是出在LCPTranscoder
而Linux平台下是因为Transcoder和LCPTranscoder都有问题。
需要注意的是,Linux的wchar_t是32位的UTF32,如果要转换成UTF16,有两个方法:
1) 用iconv库,直接从 xml文件编码<->UTF16, IconvGNU TransService用的就是这个
2) 用mbstowbc和wcstombs
mbstowcs 位操作
xml文件编码 <---------> UTF-32 <----------> UTF-16
wcstombs 位操作
"AS-IS" article