`

字符编码的理解

阅读更多

原写于2010-10-05

 

一、编码基础知识

 

1.为什么要编码、解码

计算机存储的最小单位是“位bit”,存储的所有内容都是一串二进制的表示。但最小的存储单元是byte,内存的编址也是以字节为单位,大部分的计算机系统都是以字节为单位进行存储、计算、传输。从而必须有:字符(多字节表示)与字节的相互转换。
编码:字符转换为字节;解码:字节转化为字符。

 

2.常见基本字符集

  • ASCII :只使用了一个8位字节中的低7位,总共是128个编码位。最早由美国制定,定义英文字符与二进制位之间的关系
  • Iso8859-1:单字节编码,8位全部使用,涵盖了大多数的西欧文字。(Unicode的最开头256个字符编码和ISO-8859-1是一一对应)
  • gb2312:ISO制定了ISO 2022标准,提供了七位与八位编码字符集的扩充方法的标准,gb2312是根据ISO 2022标准制定的2字节编
  • gbk:2字节编码,是国家技术监督局1995年为中文Windows 95所制定的新的汉字内码规范
  • gb18030:全称是《信息交换用汉字编码字符集》,是我国的强制标准(包含日韩等,支持单字节、双字节、4字节编码,中国大陆的标准)

字符集的概念:在确定的规则下,一个value与一个字符的对应关系

 

3.多字节与ascii关系

多字节编码,只是对中文及相关字符进行编码,可以认为是ascii的扩充。
gb2312规定:2字节编码,只使用每个字节的低7位,高位为0。结果:在计算机内部无法区分ascii码与gb码,出现“机内码(计算机内部表示汉字的编码),gb每个字节最高为置为1”。字符集依次增长的过程,向下兼容。

 

4.Unicode

UTF为UCS Transformation Format的缩写(深入了解:参考UCS的结构)。Unicode只是一个符号集,它只规定了符号的二进制代码,每一个符号都给予一个独一无二的编码,却没有规定这个二进制代码应该如何存储。常用的unicode实现方式有UTF-8和UTF-16。容纳全世界的编码,优点:中文的网页,在英文版的浏览器也能正常显示,不会乱码。
问题:汉字“严”的unicode是十六进制数4E25,两个字节。如何才能区别unicode和ascii,计算机怎么知道2个字节表示一个符号,而不是分别表示2个符号呢?

 

4.1 UTF-16:定长双字节

  • 缺点:数据量增大,如果对于英文字符,增加2倍;属于ascii字符,高字节填补为0×00,在C里面是字符串的结尾,带来无数问题。
  • 优点:直接表现字符编码的整数值,所以UTF-16是最直接的Unicode表示法。它是定长的,这大大简化了字符串的操作,Java语言就是用UTF-16格式将字符存储在内存中。

4.2 UTF-8:变长字节编码(1-6字节)

对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。
对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩余的二进制位,全部为这个符号的unicode码。

  • 如果一个字节,最高位(第8位)为0,表示这是一个ASCII字符(00 – 7F)。可见,所有ASCII编码已经是UTF-8了。
  • 如果一个字节,以11开头,连续的1的个数暗示这个字符的字节数,例如:110xxxxx代表它是双字节UTF-8字符的首字节。
  • 如果一个字节,以10开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节。
  • ascii单字节编码,中文属于3字节范围。

5. 为什么乱码

乱码有很多种情形,但是万变不离其宗,就是“解码所用的charset和编码所用的charset不兼容”。

Unicode规定,编码时,碰到“看不懂”的字符,一律用“?(0x3F)”表示;解码时,发现“看不懂”的字节,一律用“�(0xFFFD)”表示。

 

二、Java编码问题

 

宝宝:分析Java中文乱码问题的根本原因,首先要了解这些中文字符的输入源,其次是了解这些字符被输出到用户浏览器经过了哪些转换和输出环节。

中文字符可以来源于:

1.程序内嵌的中文,源代码里直接写中文字符串。

2.外部读入的中文。

 

1. 程序内嵌中文:

Java源代码(.java)本身是一个文本文件,所以和读普通文本文件一样,编译器(javac)必须以字节流的方式读入文件内容,并以适当的编码转换为Unicode字符存储在Java字节码文件(.class)中。例如 :源代码文件中包含GBK编码的中文字符,则使用下面的命令编译:javac -encoding GBK MyClass.java,如果不指定-encoding参数,javac会使用JVM默认的编码(Java虚拟机取操作系统编码) 在中文Windows上,默认是GB18030,在英文Linux上,默认是ISO-8859-1。因此,如果文件是在英文Linux下编译而未指定-encoding,那么文件中的中文“我爱Alibaba”就会变成“ÎÒ°®Alibaba” 。

 

2. 外部读入的中文,如:数据库读入、文件读入、用户数据的提交。

 

 

以用户输入为例:

 

1.输入,结论如下

 注:一般来说,URL只能使用英文字母、阿拉伯数字和某些标点符号。这是因为网络标准RFC 1738做了硬性规定:

只有字母和数字[0-9a-zA-Z]、一些特殊符号“$-_.+!*'(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL,这意味着,如果URL中有汉字,就必须编码后使用。但是麻烦的是,RFC 1738没有规定具体的编码方法,而是交给应用程序(浏览器)自己决定。这导致“URL编码”成为了一个混乱的领域。

1. 网址路径的编码,用的是utf-8编码. (firefor&IE相同)

http://www.taobao.com/春节 %E6%98%A5%E8%8A%82

2. 直接在浏览器输入查询字符的编码,不同的浏览器有不同的处理.

http://www.taobao.com/?q=春节

chrome : %E6%98%A5%E8%8A%82

firefor(中文版):  %B4%BA%BD%DA

3. GET和POST方法提交的编码,由网页的编码决定

4. Ajax调用的URL包含汉字, IE总是采用GB2312编码(操作系统的默认编码),而Firefox总是采用utf-8编码. (未验证)

 

2. 数据在内存中的编码

Java用unicode(实现unicode-16)编码,Java能够实现unicode与其它字符集之间的转换。比如: GBK的字节串d6 d0转换成utf8字节串e4 b8 ad的过程是:GBK bytes (解码) -> unicode (编码) -> UTF8 bytes(解码),而不是gbk -> utf-8。

 

3. 数据输出

3.1 Servlet输出:

字符流方式 response.getWriter(),用来输出文本类型的内容,如HTML和纯文本。 在调用response.getWriter()前,我们必须设置content type:response.setContentType("text/html; charset=GBK"); response.getWriter()通过content type中指定的字符编码来决定如何将字符流转换成字节流。

3.2 浏览器如何确定页面的字符编码

浏览器收到从WEB服务器返回的页面时:1.首先检查HTTP响应中指定的contentType,也就是servlet通过response.setContentType方法设置的值。如果content type中指定字符编码(例如text/html; charset=GBK),则使用这种方式解码这个页面;2.如果HTTP响应中没有指定字符集,那么浏览器会检查HTML页面中是否包含:<meta http-equiv="Content-Type" content="text/html; charset=GBK">如果找到,则使用这里指定的字符编码;3.如果既没有在HTTP响应中指定字符编码,也没有在HTML内容中指定字符编码,则浏览器根据一定的规则自动确定页面的字符编码(字符集探测)。例如,在英文环境中,浏览器会使用ISO-8859-1,简体中文环境中,则使用GBK。

 

三、总结:

分析Java中文乱码问题的根本原因,首先要了解这些中文字符的输入源,其次是了解这些字符被输出到用户浏览器经过了哪些转换和输出环节。 乱码有很多种情形,但是万变不离其宗,就是:解码所用的charset和编码所用的charset不兼容。

 

 

主要参考:
1.宝宝的文章<中文化和国际化问题浅析> 
2.阮一峰的网络日志http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html, http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

分享到:
评论

相关推荐

    深入理解字符编码(字符集 字符编码 字符显示 乱码问题)

    文档中主要介绍了各类字符集以及相关的字符编码,字符的显示原理,从输入到显现的整个过程,程序中出现的乱码问题以及解决方案

    深入理解字符编码

    介绍了字符编码的常识及常见字符编码的介绍,及字符编码的深层原理

    字符编码的奥秘详解 字符编码的奥秘详解

    对于字符编码的认识与理解、防止与解决乱码问题 ,编码问题是信息处理的基本问题,但是由于历史和政治的问题,事实上存在着大量不统一的编码方式,造成在信息处理过程中的信息丢失,转换错误等问题,UCS 为问题的...

    各种字符编码详解.doc

    字符编码的问题看似很小,经常被技术人员忽视,但是很容易导致一些莫名其妙的问题。这里总结了一下字符编码的一些普及性的知识,希望对大家有所帮助。 还是得从ASCII码说起 说到字符编码,不得不说ASCII码的简史。...

    java字符编码

    本文介绍了字符与编码的发展过程,相关概念的正确理解。举例说明了一些实际应用中,编码的实现方法。然后,本文讲述了通常对字符与编码的几种误解,由于这些误解而导致乱码产生的原因,以及消除乱码的办法。本文的...

    各种字符编码方式详解及由来

    一直对字符的各种编码方式懵懵懂懂,什么ANSI UNICODE UTF-8 GB2312 GBK DBCS UCS……是不是看的很晕,假如您细细的阅读本文你一定可以清晰的理解他们

    字符集编码和理解材料

    几篇文章,组合起来容易理解字符集及编码问题。

    字符编码的通俗讲解

    用通俗平实的语言对字符内码做了概述,适合刚接触相关知识的朋友阅读理解!

    字符,字节和编码.中文问题”,“乱码问题”。

    本文介绍了字符与编码的发展过程,相关概念的正确理解。举例说明了一些实际应用中,编码的实现方法。然后,本文讲述了通常对字符与编码的几种误解,由于这些误解而导致乱码产生的原因,以及消除乱码的办法。本文的...

    C语言基础之转义字符:从原理到应用的字符编码教程 .txt

    提供了多种字符编码和转换的功能,如输入和输出字符的实体、ASCII码、八进制转义字符、十六进制转义字符等,以及支持多个字符的批量转换和显示。本资源适合C语言编码和转换的学习者和工程师使用,帮助他们通过Web...

    JAVA里字符编码的探索与理解.txt

    众所周知,JAVA为了国际通用,用的是UNICODE来保存里面的字符。而UNICODE只是一个种字符集,字符的存储...

    字符编码详述

    字符编码的一些介绍,可以对程序中的字符编码有更好的理解

    字符集编码和转换(JS实现)

    1.示例是CSDN博文的附带产物 2.文章有大量参考网络资料,已经都在开头给出链接. 3.是个人对字符编码的理解和整理 @author dailc @csdn http://blog.csdn.net/u010979495/article/details/50601511

    基于C++文件的哈夫曼编码与解码.zip

    通过编写利用哈夫曼算法实现的文件编码解码小工具,可加深对哈夫曼算法的理解,以及编码的熟练度。本人的小工具仅针对英文大小字母及 ' '\n' ' ' 字符针对性的进行了哈夫曼编码,若想实现中文及各种支持语言的编码,...

    字符集和编码高度概括 (英文)

    深入地概括了常用的字符集和编码, 看后可对相关知识有深入的理解.

    基于字符集、字符编码与HTTP编码解码之万象详解

    2:字符编码:是一套法则,最常规的理解就是:让程序根据这个法则对应到相应的字符集中将byte[]存取为string。 现在,我们要来看看这些东西在 .NET 中对应的是什么。 一:字符集和字符编码 如果想得到全部的字符集...

    基于python 字符编码的理解

    下面小编就为大家带来一篇基于python 字符编码的理解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    编码类的书籍

    编码知识,可以更深层次理解计算机原理,让你理解程序的本质

Global site tag (gtag.js) - Google Analytics