根据Oracle的人说WE8ISO8859P1是最大的字符集,所以这里采用的就是它。但是用ADO来操作这种数据库有点头痛,MSDAORA可以正确读写汉字,但是不能操作BLOB字段,OraOLEDB可以操作BLOB字段,但是写汉字会乱码。这个问题困扰我们三年多了,最近再次发起冲击终于找到一个变通的办法了。
WE8ISO8859P1是一个单字节的西文字符集,为什么可以用来保存汉字呢?主要是因为WE8ISO8859P1用完了一个字节的所有码点,这样一个GBK编码的汉字由两个合法的WE8ISO8859P1字符组成,这就是什么可以用来保存汉字的原因。也因为它用完所有码点,所以它可以是任何字符集的超集。因为客户端和服务器端的字符集都是WE8ISO8859P1,所以在Oracle Net层面不需要字符集的转换,也不会有信息丢失的问题。汉字保存在数据库中编码是GBK。SQL Plus等OCI程序直接把GBK编码发送到服务器端。OLEDB程序因为COM的原因,所以出现数据丢失,但是微软的Provider是对的。
COM中字符串都是Unicode编码的BSTR,程序必须把SQL语句转换成Unicode才能调用COM组件的方法,组件内部又要转换一次,不同的Provider转换的方式不一样。具体如下:
OraOLEDB:
GBK-->Unicode-->WE8ISO8859P1------->WE8ISO8859P1(服务器端)
MSDAORA:
GBK-->Unicode-->GBK------>WE8ISO8859P1(服务器端)
两种实现都没有错,Oracle根据客户端NLS_LANG的设置来决定如何转换。Microsoft根据操作系统的字符集来转换,谁叫他们一个是做数据库,一个是搞操作系统的呢。其实也与我们超常规使用数据库也有些关系,用本来不支持中文的字符集来存储中文,有些难为Oracle了。
经过验证的确是那么回事“中国”的GBK为(D6D0 B9FA),用SQL Plus写入数据库,然后用PL/SQL Developer查看16进制的值的确如此(还可以用SQL函数RAWTOHEX()查看)。而用OraOLEDB写入的为汉字“靠”(BFBF),BF在WE8ISO8859P1中为INVERTED QUESTION MARK一个加粗的问号,每个汉字变成一个问号,连续两个就是一个靠字。主要是因为OraOLEDB 把Unicode的汉字转为WE8ISO8859P1时,汉字是无效字符,于是统统用?表示。而微软默认转回操作系统字符集GBK,所以没有问题。
现在问题找出来了,怎么解决呢?一开始我想到的方案是重写一个Data Provider或者对OraOLEDB的DLL做些小的手术,直到今天中午吃饭时我还在考虑怎么写OLEDB Data Provider。吃饭时我给同事介绍我的最新进展,原因找出来了,但是解决方案还没有。吃着吃着,我想到一个办法,既然原因是一个汉字对应不上一个西文字符,但是半个汉字可以存储,我可以在转Unicode前把汉字拆分为两半,不用MultiByteToWideChar转,因为它太老实,不会作弊。比如,中国作弊后的Unicode字符串为(00D6 00D0 00B9 00FA),算法实现也非常简单:
STDMETHODIMP COLEDBUtil::prepareWE8ISO8859P1(BSTR
*
bstrSrc, VARIANT_BOOL
*
bRet)
{
// TODO: Add your implementation code here
int buffer_len = WideCharToMultiByte(CP_ACP, 0, *bstrSrc, wcslen(*bstrSrc),
NULL, 0, 0, 0);
char *strBuffer = new char[buffer_len+1];
int retVal = WideCharToMultiByte(CP_ACP, 0, *bstrSrc, wcslen(*bstrSrc),
strBuffer, buffer_len, 0, 0);
if(retVal == 0)
{
*bRet = VARIANT_FALSE;
delete [] strBuffer;
return E_FAIL;
}
else
strBuffer[buffer_len] = '';
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
//Convert it to Unicode manually
const unsigned char *str;
OLECHAR *wcsBuffer;
OLECHAR *pWC;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
SysFreeString(*bstrSrc);
wcsBuffer = new OLECHAR[strlen(strBuffer)+1];
pWC = wcsBuffer;
for(str = (const unsigned char *)strBuffer; *str != ''; str++)
{
*pWC = *str;
pWC++;
}
*pWC = L'';
//*bstrSrc = bstr.copy();
*bstrSrc = ::SysAllocString(wcsBuffer);
*bRet = VARIANT_TRUE;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
delete [] wcsBuffer;
delete [] strBuffer;
return S_OK;
}
VB中的代码如下:
没有造成太大的额外负担,效果不错。也许还有更好的办法,但是现在能想到的也就这个了,而且的确能够解决问题。困扰我们三年多了,一直找不到很好的解决方案,我花了好几天看文档,没有直接的答案,但是加深了我对Oracle多语言支持的理解。其实我一开始死死盯着Unicode看,后来突然发现与Unicode一点关系都没有,主要是最大的字符集在误导我。Oracle上真正最大的字符集是AL32UTF16,AL32UTF8四个字节的补充字符不支持。
解决了这个问题,似乎我很厉害,其实不是那么回事。我之所以能够解决这个问题,主要还是我比较闲,才有大量的时间在网上泡着,看文档什么的,我们领导也比较宽松,不管我。如果我的另外两个同事也有这么多的时间,同样也可以解决这个问题。他们实在太忙了,经常加班,尤其是我们领导,实在太忙不可能钻得很深,能够到他那个深度和广度,实在是非常的不容易。去年我当了几个月的研发部经理,我比他懒多了,真是惭愧!
同时我也在想我的价值何在,开出10k、15k的工资,别人会问我为什么值这个价钱。如何提高自身的价值,一个人的精力是有限的,要把有限的精力花在最值得花的地方。还有非常多的东西想学习,但我必须作出选择,我没有那么多的时间。
WE8ISO8859P1是一个单字节的西文字符集,为什么可以用来保存汉字呢?主要是因为WE8ISO8859P1用完了一个字节的所有码点,这样一个GBK编码的汉字由两个合法的WE8ISO8859P1字符组成,这就是什么可以用来保存汉字的原因。也因为它用完所有码点,所以它可以是任何字符集的超集。因为客户端和服务器端的字符集都是WE8ISO8859P1,所以在Oracle Net层面不需要字符集的转换,也不会有信息丢失的问题。汉字保存在数据库中编码是GBK。SQL Plus等OCI程序直接把GBK编码发送到服务器端。OLEDB程序因为COM的原因,所以出现数据丢失,但是微软的Provider是对的。
COM中字符串都是Unicode编码的BSTR,程序必须把SQL语句转换成Unicode才能调用COM组件的方法,组件内部又要转换一次,不同的Provider转换的方式不一样。具体如下:
OraOLEDB:
GBK-->Unicode-->WE8ISO8859P1------->WE8ISO8859P1(服务器端)
MSDAORA:
GBK-->Unicode-->GBK------>WE8ISO8859P1(服务器端)
两种实现都没有错,Oracle根据客户端NLS_LANG的设置来决定如何转换。Microsoft根据操作系统的字符集来转换,谁叫他们一个是做数据库,一个是搞操作系统的呢。其实也与我们超常规使用数据库也有些关系,用本来不支持中文的字符集来存储中文,有些难为Oracle了。
经过验证的确是那么回事“中国”的GBK为(D6D0 B9FA),用SQL Plus写入数据库,然后用PL/SQL Developer查看16进制的值的确如此(还可以用SQL函数RAWTOHEX()查看)。而用OraOLEDB写入的为汉字“靠”(BFBF),BF在WE8ISO8859P1中为INVERTED QUESTION MARK一个加粗的问号,每个汉字变成一个问号,连续两个就是一个靠字。主要是因为OraOLEDB 把Unicode的汉字转为WE8ISO8859P1时,汉字是无效字符,于是统统用?表示。而微软默认转回操作系统字符集GBK,所以没有问题。
现在问题找出来了,怎么解决呢?一开始我想到的方案是重写一个Data Provider或者对OraOLEDB的DLL做些小的手术,直到今天中午吃饭时我还在考虑怎么写OLEDB Data Provider。吃饭时我给同事介绍我的最新进展,原因找出来了,但是解决方案还没有。吃着吃着,我想到一个办法,既然原因是一个汉字对应不上一个西文字符,但是半个汉字可以存储,我可以在转Unicode前把汉字拆分为两半,不用MultiByteToWideChar转,因为它太老实,不会作弊。比如,中国作弊后的Unicode字符串为(00D6 00D0 00B9 00FA),算法实现也非常简单:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
Dim
conn
As
New
ADODB.Connection
Dim oraUtil As New ORACLEUTILLib.OLEDBUtil
Dim strSQL As String
conn.Open " Provider=OraOLEDB.Oracle.1;Data Source=QDESTATE;User ID=landuse;Password=landuse "
strSQL = " insert into a values ('中国') "
Debug.Print oraUtil.prepareWE8ISO8859P1(strSQL)
conn.Execute strSQL
Dim oraUtil As New ORACLEUTILLib.OLEDBUtil
Dim strSQL As String
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
conn.Open " Provider=OraOLEDB.Oracle.1;Data Source=QDESTATE;User ID=landuse;Password=landuse "
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
strSQL = " insert into a values ('中国') "
Debug.Print oraUtil.prepareWE8ISO8859P1(strSQL)
conn.Execute strSQL
解决了这个问题,似乎我很厉害,其实不是那么回事。我之所以能够解决这个问题,主要还是我比较闲,才有大量的时间在网上泡着,看文档什么的,我们领导也比较宽松,不管我。如果我的另外两个同事也有这么多的时间,同样也可以解决这个问题。他们实在太忙了,经常加班,尤其是我们领导,实在太忙不可能钻得很深,能够到他那个深度和广度,实在是非常的不容易。去年我当了几个月的研发部经理,我比他懒多了,真是惭愧!
同时我也在想我的价值何在,开出10k、15k的工资,别人会问我为什么值这个价钱。如何提高自身的价值,一个人的精力是有限的,要把有限的精力花在最值得花的地方。还有非常多的东西想学习,但我必须作出选择,我没有那么多的时间。