分享

字符编码问题摘要

 amom 2005-10-06

 UCS只是规定如何编码,并没有规定如何传输、保存这个编码。
 例如“汉”字的UCS编码是6C49,我可以用4个ASCII数字来传输、保存这个编码;也可以用UTF-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。
 UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。
 UTF是“UCS Transformation Format”的缩写。UCS可以看作是"Unicode Character Set"的缩写。
 UTF-8就是以8位为单元对UCS进行编码。


---------------
内码和"code page"
---------------
 目前Windows的内核已经支持Unicode字符集,这样在内核上可以支持全世界所有的语言文字。
 但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。Windows使用代码页("code page")来适应各个国家和地区。"code page"可以被理解为前面提到的内码。GBK对应的"code page"是CP936。
 微软也为GB18030定义了"code page":CP54936。但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个"code page"是无法真正使用的。

----------------
UTF的字节序和BOM
----------------
 UTF-8以字节为编码单元,没有字节序的问题。
 UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?
 Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。
 UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。Windows就是使用BOM来标记文本文件的编码方式的。

--------
关于内码
--------
 内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的。
 现在的Windows在系统内部支持Unicode,然后用代码页适应各种语言,“内码”的概念就比较模糊了。
 微软一般将缺省代码页指定的编码说成是内码。
 内码这个词汇,并没有什么官方的定义,代码页也只是微软这个公司的叫法。作为程序员,我们只要知道它们是什么东西,没有必要过多地考证这些名词。
 所谓代码页("code page")就是针对一种语言文字的字符编码。例如GBK的"code page"是CP936,BIG5的"code page"是CP950,GB2312的"code page"是CP20936。
 Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件,里面的内容是字节流:BA、BA、D7、D6。Windows应该去怎么解释它呢?
 是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释,还是按照ISO8859-1去解释?如果按GBK去解释,就会得到“汉字”两个字。按照其它编码解释,可能找不到对应的字符,也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符,这时就产生了乱码。
 答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI,其实就是按照缺省代码页的编码方法保存。
 Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码,用户又安装了对应的代码页,Windows就能正确显示,例如在HTML文件中就可以指定charset。
 有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定CharSet。如果他使用了0x80-0xff之间的字符,中文Windows又按照缺省的GBK去解释,就会出现乱码。这时只要在这个html文件中加上指定CharSet的语句,例如:
 
 如果原作者使用的代码页和ISO8859-1兼容,就不会出现乱码了。
 再说区位码,啊的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII编码,我们在区位码的高、低字节上分别加上A0。这样“啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码,虽然GB2312的原文根本没提到这一点。
 为了识别 Unicode 文件,Microsoft 建议所有的 Unicode 文件应该以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符开头。这作为一个“特征符”或“字节顺序标记(byte-order mark,BOM)”来识别文件中使用的编码和字节顺序。
 但是,Linux/UNIX 并没有使用 BOM,因为它会破坏现有的 ASCII 文件的语法约定。在 POSIX 系统中,选中的语言环境识别了在一个过程中的所有输入输出文件期望的编码形式。

 

 

把UTF-8编码转换为GB2312编码

2005年5月19日 10:44

最近在做的广告系统中,碰到了一个问题,广告系统采用的UTF-8编码,而一些使用这套广告系统的频道页面使用的是GB2312编码。当然也有使用UTF-8编码的频道使用这套广告系统。

频道页面是通过嵌入类似如下的代码方式,来调用广告的。具体那个时间显示那个广告,或者那些广告组合是广告系统自己处理的。

<script type="text/javascript"> script> <script type="text/javascript" src="http://ads.csdn.net/AD/Show_JavaScript_AD.js" > script>


不同编码的页面、脚本之间互相引用,就会产生乱码的问题,解决方法就是统一成一种编码。
asp.net 中,如果要修改输出页面的编码,可以通过修改web.config中以下配置信息

<globalization requestEncoding="utf-8" responseEncoding="utf-8" />

以上只是修改整体的默认编码,如果只有某个页的编码需要修改,ASP.net 中则可以简单的使用下面代码:

Encoding gb2312 = Encoding.GetEncoding("gb2312"); Response.ContentEncoding = gb2312;

在非ASP.net 应用中,可能你读到的数据是UTF-8编码,但是你要转换为GB2312编码,则可以参考以下代码:

 

string utfinfo = "document.write(\"alert(‘aa你好么??‘);\");"; string gb2312info = string.Empty; Encoding utf8 = Encoding.UTF8; Encoding gb2312 = Encoding.GetEncoding("gb2312"); // Convert the string into a byte[]. byte[] unicodeBytes = utf8.GetBytes(utfinfo); // Perform the conversion from one encoding to the other. byte[] asciiBytes = Encoding.Convert(utf8, gb2312, unicodeBytes); // Convert the new byte[] into a char[] and then into a string. // This is a slightly different approach to converting to illustrate // the use of GetCharCount/GetChars. char[] asciiChars = new char[gb2312.GetCharCount(asciiBytes, 0, asciiBytes.Length)]; gb2312.GetChars(asciiBytes, 0, asciiBytes.Length, asciiChars, 0); gb2312info = new string(asciiChars);

当然,其他各种编码之间的转换,跟上述代码也类似的,就不描述了。

 

   有一个Web项目,Web.Config中requestEncoding和responseEncoding都是gb2312,而从数据库中取出的简介数据可能是中文和韩、日文混合的内容,这时候如果直接输出到页面上,其页面会出现乱码,其中的韩文内容无法正确显示。当然如果项目的编码都使用Utf-8的话将没有这个问题,但这个项目是一个老项目,为了尽量不要影响已有的程序,所以无法将编码改为Utf-8,只能在本页面上动脑筋。

   经过研究,发现这个问题可以通过Html实体的方法解决。

对于Html实体请参考:

Character entity references in HTML 4

HTML Document Representation

测试代码:

Byte[] bComments = Encoding.UTF8.GetBytes("一ンブル????中文");
   char[] cComments = Encoding.UTF8.GetChars(bComments);
   
   StringBuilder charBuilder = new StringBuilder();   
   foreach(char c in cComments)
   {
    if(c > ‘\u0800‘)
    {
     charBuilder.Append("&#");
     charBuilder.Append((int)c);
    } 
    else
    {
     charBuilder.Append(c);
    }
   }
   Response.Write(charBuilder.ToString());

这段代码的作用是将所有的中文、韩文、日文字符通过硬编码输出成为html实体。而Html实体是不受ResponseEncoding和页面编码集影响的。

说明:

\u0800 以上的为中、韩、日字符。

中文的范围:\u4e00 - \u9fa5,日文在\u0800 - \u4e00,韩文为\u9fa5以上。

这个方法仅仅是为了解决小范围问题,如果各位有更好的办法请指教。

 

ms的Best Practices Analyzer Tool for Microsoft SQL Server 2000 1.0上个月发布了1.0版本,和beta版相比,看起来没有什么太大的变化。Best Practices Analyzer Tool for Microsoft SQL Server 2000检查的一些rules可以做为t-sql编程的checklist

sql server数据库编程指导以及最佳实践

原则

编写高可读的代码:遵循命名原则和代码风格约定
开始就要关注t-sql代码性能的影响:减少网络流量,减少磁盘IO,利用索引,避免lock
编写安全的代码

------------------------------------------------------------------------------

命名数据库对象时,采用统一的前缀或者后缀

采用统一的前缀或者后缀是为了提高代码的可读性,但是
存储过程不要使用sp_作为前缀,函数不要使用fn_作为前缀。
如果sql server发现存储过程以sp_作为前缀,都会先到master数据库中查询这个存储过程


添加必要的注释

存储过程或者函数,视图前应该注释创建者, 创建时间,修改者,修改时间,功能注释,使用说明,同时包含一到多条执行该对象的语句

及时检查执行状况

默认情况下,如果一条sql语句执行错误,sql server不会自动roll back前面的执行(可以设置:SET XACT_ABORT ON),sql语句执行完毕后,需要及时通过全局变量@@error和@@rowcount来检查执行状况。

用标准的join方式

标准的join方式是指while语句中只包含过滤条件,不包含join条件

尽量避免客户端程序直接通过select,insert,update等直接操作数据库

用存储过程封装数据访问,存储过程是经过编译的,不用每次计算execute plan。而且封装了逻辑,同时增加了安全性。

存储过程如果需要返回数据,使用output关键字

不要用return返回数据,存储过程的return应该返回存储过程的执行状况,如果需要返回数据,使用带有output关键字的参数

谨慎使用IDENTITY作为表的主键的数据类型

IDENTITY会给客户端程序和database交互带来很多影响,而且在数据导入导出时也会带来麻烦,需要仔细评估这些影响。但是IDENTITY和guid相比也有优点,就是可读性。

尽量避免使用NULL

如果没有特别设置,null参与的运算结果都为null,如果疏忽这一原则,会对程序逻辑的正确性带来影响,而且,客户端程序需要额外的步骤来处理NULL。需要设置ANSI_NULLS为ON。

在insert语句中,使用确定的列名

insert语句中,使用确定的列名以间少表结构变化对t-sql代码带来的影响

尽量使用外键,约束检查来保证数据的完整性

数据完整性至关重要,外键,约束检查可以避免另外写代码来保证数据完整性

不要在查询时用select * ,用确定的列名来代替 *

查询结果中冗余的信息影响整体的性能

尽量避免使用服务器端游标

服务器端游标对性能有严重的影响,应当尽量避免,比如可以用while循环来代替游标,如果不能避免,则应选择最合适的游标类型

尽量避免使用临时表

临时表会发生磁盘IO操作,影响性能,可以用嵌套查询,view,或者table变量来代替临时表。如果需要缓存大量的数据,临时表优于table变量,同时注意为临时表建index

如果需要执行一系列sql命令,在前面添加SET NOCOUNT ON

执行SET NOCOUNT ON,sql命令执行影响的行数不会传回客户端,减少网络流量,提高性能

在字符串匹配查询时,避免在第一个字符位置使用通配符

如果第一个字符位置使用通配符,则index不起作用

避免使用IN 或者NOT IN

使用IN 或者NOT IN,则index不起作用

事务处理时,尽可能的占用最少的资源

事务处理时,尽可能的占用最少的资源以减少资源的锁定,提高数据库整体性能。同时检查加锁类型,尽量使用低级别的加锁类型。

匹配的事务处理

如果存储过程开始了事务处理,应该负责结束这个事物处理,submit或者rollback

操作NCHAR或者NVARCHAR数据类型的列时,使用N关键字

使用N关键字,sql server会使用unicode编码,避免出现乱码

尽量避免使用TEXT,NTEXT,binary,image数据类型的字段,尽量避免将文件或者图片直接存入数据库
 
这些数据类型访问方式不同于普通的数据类型。数据库也不是存储文件或者图片内容合适的地方

尽量使用VARCHAR代替CHAR,用NVARCHAR代替VARCHAR,

用VARCHAR代替CHAR是为了节省数据库空间,NVARCHAR代替VARCHAR是为了避免unicode带来的麻烦

如果某个table中的数据对不同用户的可见性是不一样的,使用view来隔离用户对table的直接访问

检查sql注入式攻击对代码的影响。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多