MyException - 我的异常网
当前位置:我的异常网» Sql Server » 理解字符编码解决办法

理解字符编码解决办法

www.MyException.Cn  网友分享于:2015-08-26  浏览:352次
理解字符编码
在论坛上不断看到有人受乱码问题困扰。于是整理了一些资料,希望可以帮助大家理解清楚“字符编码”这个不算复杂却很搞人的问题。
本文内容与SQLServer中文处理一文部分内容有重复,但重点不同,可结合着一起看。

下文中,我会用Python(V3.x)和SQLServer(2005)列举一些示例。
Python3对Unicode的支持非常好,清晰地划分字节流和字符串两种不同类型也极大地简化了对字符编码问题的理解,可以交互式执行这一点更是让简单测试一些语句方便到家了。(这正是选用Python列举示例的理由)
原本对Python不了解的朋友可参看Python入门二三事一文。


字节流(byte stream) VS 字符串(string)

要理解字符编码,首先要区分字节(流)与字符(串)这两类不同的事物。计算机内部只存储和处理字节,字符只是人类理解的概念。
在Python中,字节流和字符串分别对应bytes和str类型:(变量名b和s只是巧合,^___^|||)
Python code

# 示例-1
>>> b = b'Unicode\xe5\xad\x97\xe7\xac\xa6\xe4\xb8\xb2'
>>> type(b)
<class 'bytes'>
>>> s = 'Unicode字符串'
>>> type(s)
<class 'str'>


在SQLServer中,字节流和字符串分别对应varbinary/binary和varchar/nvarchar/char/nchar。

为了便于人类与计算机的沟通,人们需要在字节与字符之间建立对应关系,这即是字符编码(Character Encoding)。一种字符编码可以支持(即定义了对应关系)的所有字符称为一个字符集(Charset)。如ASCII编码支持的所有字符被称为ASCII字符集。这两个概念关系如此紧密,以至于可以视之为等价,区别仅在于前者是从对应关系角度命名,后者则是从所有字符集合的角度命名。事实上,在维基百科上这两个关键词指向的是同一个页面。

所谓乱码,或者是字符转换为字节(编码)与字节转换为字符(解码)的过程使用了不同的字符编码,或者是尝试将不是文本的字节流(可能是数据错误,也可能就是二进制数据)解码为字符。理解了这两点,乱码这个概念就没有存在的必要了。

ASCII与扩展ASCII的编码方案

最早形成标准并且至今仍有广泛影响力的字符编码莫过于ASCII。该字符集只使用单字节的低7位,即0x00到0x7f的范围。该字符集中包含了控制字符(现在可能只有其中几个是常用的)、数字、大小写英文字母和英文标点符号。ASCII是一个设计良好的字符编码规则,如充分利用码元、数字和字母的排布是连续的并且有着直观的对应、大小写字母可以通过一个bit的改变而相互转化,等等。其主要局限在于无法支持非英语的字符。ASCII只定义了128个字符,这对于拉丁语系来说尚且太少,更不必说包含成千上万字符的CJK语言和希腊、阿拉伯等世界各国语言。

第一种解决方案是扩展ASCII,把最高位为1的字节(即0x80到0xff的范围)使用上,从而还可以与ASCII保持兼容。Windows和IBM引入的代码页正是这种解决方案,例如,cp1252支持西欧国家的各种字符,cp936和cp950支持中文字符(分别用于简体中文和繁体中文系统)。中文系统中常见的字符编码如国标码(包括gb2312、gbk、gb18030,应用于中国大陆和新加坡)和大五码(big5,应用于台港澳)也是类似的原理。
事实上,许多不同的编码名称之间可能是等价或近似等价的,只是编码的制订方不同而已。例如,西欧语言的cp1252和latin1非常相近,中文的cp936和cp950分别近似等价于gbk和big5。
维基百科的字符编码条目列举了各种常见的字符编码。Python官方文档中还用表格列举了各种编码规则不同名称的对应关系。
对于大陆地区常用的中文编码,简单来说:gb2312支持常用的简体汉字,gbk/cp936支持常用的简体和繁体汉字,gb18030支持更多的汉字和部分少数民族字符,几者之间的关系为:ASCII < gb2312 < gbk/cp936 < gb18030(其中<表示兼容的关系)。虽然gbk/cp936也支持繁体汉字,但不要与big5/cp950混为一谈,二者是不同的编码方案,只是支持的字符有交集而已:
Python code

# 示例-2
>>> s = '中華'
>>> s.encode('gbk')
b'\xd6\xd0\xc8A'
>>> s.encode('big5')
b'\xa4\xa4\xb5\xd8'


想要了解更多相关细节,可以分别查看各种编码的维基条目,或者google相关的文章。

Unicode

前一种扩展ASCII的方案的主要缺点是各种编码各自为政,这便导致不同系统之间字符集可能无法兼容,一个常见的问题便是在一台电脑上保存的文本文件复制到另一台不同代码页设置的电脑上会显示乱码。例如字节流0xa1a2,在latin1、gbk、big5不同的编码方案中对应的就是不同字符:
Python code

# 示例-3
>>> b = b'\xa1\xa2'
>>> b.decode('latin1')
'¡¢'
>>> b.decode('gbk')
'、'
>>> b.decode('big5')
'﹜'


于是人们想要统一,由此产生的第二种方案便是Unicode,一个类似于巴别塔(Babel)的计划(不同的是Unicode成功了)。关于Unicode组织与国际标准化组织的ISO-10646工作组分别不约而同地制订统一字符编码方案、后来发现彼此的存在从而开始协作和共享、最终却依然各自发布标准的迭事可以参看Unicode维基条目。二者的编码方案是兼容的,差异主要是实现方式。我们接下来只谈如何理解Unicode。

Unicode可以分为编码方案和实现方式两个层面来看。

编码方案定义了字符与码位(Code point,可理解为一个整数值)的对应关系。Unicode定义了1114112个码位(当然并非所有码位都对应有字符,有些是保留为特殊用途,有些是暂时尚未定义,有些是预留为私有空间),划分为17个字符平面,编号0-16,每个平面包含65536个码位,其中编号为0的平面最为常用,称为基本多文种平面(Basic Multilingual Plane, BMP)。Unicode码位的表示方式是“U+”加上十六进制的码位编号,BMP的码位编号为4位的十六进制数,范围是U+0000到U+FFFF,BMP之外的其它平面的码位,编号为5到6位的十六进制数。
平时我们提到Unicode字符,绝大多数情况指的都是BMP的字符。其中,U+0000到U+007F的范围与ASCII字符完全对应,U+4E00到U+9FA5的范围定义了常用的中文字符(这些字符也都在GBK字符集中)。如在SQLServer上借助自然数辅助表可轻松查看常用Unicode字符的码位:
SQL code

# 示例-4
SELECT
    [码位(dec)] = n,
    [码位(hex)] = CAST(n AS binary(2)),
    [Unicode字符] = NCHAR(n),
    [UCS-2LE编码] = CAST(NCHAR(n) AS binary(2)),
    [GBK/CP936编码] = CAST(CAST(NCHAR(n) AS varchar(2)) AS varbinary(2)) --只在数据库排序规则为Chinese_PRC_XXX时有效
FROM dbo.Nums
WHERE n BETWEEN 32 AND 126 --ASCII,略去控制字符
    OR n BETWEEN 19968 AND 40869 --中文字符
    OR n BETWEEN 65281 AND 65374 --全角标点字母数字,对应半角为n-65248的ASCII字符
    OR n = 12288 --全角空格,对应半角空格为32
--以上查询就是gbk编码的主要字符

文章评论

总结2014中国互联网十大段子
总结2014中国互联网十大段子
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
每天工作4小时的程序员
每天工作4小时的程序员
编程语言是女人
编程语言是女人
我是如何打败拖延症的
我是如何打败拖延症的
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
老程序员的下场
老程序员的下场
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
程序员必看的十大电影
程序员必看的十大电影
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
为什么程序员都是夜猫子
为什么程序员都是夜猫子
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
程序员和编码员之间的区别
程序员和编码员之间的区别
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员应该关注的一些事儿
程序员应该关注的一些事儿
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
那些争议最大的编程观点
那些争议最大的编程观点
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
如何成为一名黑客
如何成为一名黑客
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
鲜为人知的编程真相
鲜为人知的编程真相
代码女神横空出世
代码女神横空出世
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
中美印日四国程序员比较
中美印日四国程序员比较
一个程序员的时间管理
一个程序员的时间管理
我的丈夫是个程序员
我的丈夫是个程序员
 程序员的样子
程序员的样子
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
漫画:程序员的工作
漫画:程序员的工作
10个调试和排错的小建议
10个调试和排错的小建议
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有