蛋疼的中文编码及其计算机编码历史

更好的博客:http://my.oschina.net/goldenshaw/blog?catalog=536953

一:编码发展:

1 ANSI编码:计算机在美国出现,使用单字节8位编码,共可以表示255个状态。(0-32(0x00-0x20)表示控制码),其中0-127可以表示所有的英文字符。称为ANSI(Ascii American Standard Code for Information Interchange)编码。

   不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、Big5、Shift_JIS 等各自的编码标准。这些使用 1 至 4 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。 不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。 当然对于ANSI编码而言,0x00~0x7F之间的字符,依旧是1个字节代表1个字符。这一点是ANSI编码与Unicode编码之间最大也最明显的区别。

 

2 GB2312->GBK->GB18030:ANSI无法满足非英语国家的使用,故中国制定了一系列的标准,127单字节与ANSI兼容,127以上连续两个字节,表示一个中文。即程序员口中的:"一个汉字算两个英文字符!一个汉字算两个英文字符……"。从GB2312逐渐扩展,字符集逐渐扩展。类似的台湾繁体标准BIG-5。等等。

 

3  UNICODE(全称Universal Multiple-Octet Coded Character Set,简称UCS,俗称Unicode):字符编码之间的转换相当头疼,ISO给出解决方案unicode,所有字符必须用双字节,未来可以扩展UCS-4编码(4字节)。unicode与GBK等编码之间不兼容,故需要转码表等等。(顺便说一下,windows nt底层已经重构使用unicode存储)

  unicode只规定符号的二进制代码,没有规定如何存储和传输编码。会导致计算机无法区分编码究竟是unicode还是ascii编码。utf-8是unicode的实现方式之一。

 

4 UTF(UCS Transfer Format ):计算机网络的兴起,字符如何传输是个问题。故 utf-8,utf-16分别代表,一次传输8位或者16位。网络传输中,计算机架构有的采用低位优先,有些采用高位优先。故传输前先发送标识符(FEFF代表高位优先,FFFE代表低位优先)。

  UTF-8的编码规则很简单,只有二条:

  1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

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

下表总结了编码规则,字母x表示可用编码的位。

Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

下面,还是以汉字“严”为例,演示如何实现UTF-8编码。

已知“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。

 

5  Little endian和Big endian

 Unicode码可以采用UCS-2格式直接存储。以汉字”严“为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。

那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。

 6 BOM

Byte order Mark:UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?

在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来标记文本文件的编码方式的。

 

7 escape加密/unescape解密,%u编码:

有时候会遇到%u编码:js通过escape函数,将字符使用utf-16be编码后,前面加入%u。已经被逐渐废弃。具体参考这里:http://blog.csdn.net/lvxiangan/article/details/8151670,内容如下:

 

一般来说,URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号。比如,世界上有英文字母的网址 “http://www.abc.com”,但是没有希腊字母的网址“http://www.aβγ.com”(读作阿尔法-贝塔-伽玛.com)。这是 因为网络标准RFC 1738 做了硬性规定:

"...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL."

“只有字母和数字[0-9a-zA-Z]、一些特殊符号“$-_.+!*'(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于 URL。”

这意味着,如果URL中有汉字,就必须编码后使用。但是麻烦的是,RFC 1738没有规定具体的编码方法,而是交给应用程序(浏览器)自己决定。这导致“URL编码”成为了一个混乱的领域。

下面就让我们看看,“URL编码”到底有多混乱。我会依次分析四种不同的情况,在每一种情况中,浏览器的URL编码方法都不一样。把它们的差异解释 清楚之后,我再说如何用Javascript找到一个统一的编码方法。

二、情况1:网址路径中包含汉字

打开IE(我用的是8.0版),输入网址“http://zh.wikipedia.org/wiki/春节 ”。 注意,“春节”这两个字此时是网址路径的一部分。

bg2010021102.jpg

查看HTTP请求的头信息,会发现IE实际查询的网址是“http://zh.wikipedia.org/wiki/%E6%98%A5%E8%8A%82 ”。 也就是说,IE自动将“春节”编码成了“%E6%98%A5%E8%8A%82”。

bg2010021103.png

我们知道,“春”和“节”的utf-8编码分别是“E6 98 A5”和“E8 8A 82”,因此,“%E6%98%A5%E8%8A%82”就是按照顺序,在每个字节前加上%而得到的。(具体的转码方法,请参考我写的《字符编码笔记》 。)

在Firefox中测试,也得到了同样的结果。所以,结论1就是,网址路径的编码,用的是utf-8编码。

三、情况2:查询字符串包含汉字

在IE中输入网址“http://www.baidu.com/s?wd=春节 ”。注意,“春节”这两个字此时 属于查询字符串,不属于网址路径,不要与情况1混淆。

bg2010021104.jpg

查看HTTP请求的头信息,会发现IE将“春节”转化成了一个乱码。

bg2010021105.png

切换到十六进制方式,才能清楚地看到,“春节”被转成了“B4 BA BD DA”。

bg2010021106.png

我们知道,“春”和“节”的GB2312编码(我的操作系统“Windows XP”中文版的默认编码)分别是“B4 BA”和“BD DA”。因此,IE实际上就是将查询字符串,以GB2312编码的格式发送出去。

Firefox的处理方法,略有不同。它发送的HTTP Head是“wd=%B4%BA%BD%DA”。也就是说,同样采用GB2312编码,但是在每个字节前加上了%。

bg2010021107.png

所以,结论2就是,查询字符串的编码,用的是操作系统的默认编码。

四、情况3:Get方法生成的URL包含汉字

前面说的是直接输入网址的情况,但是更常见的情况是,在已打开的网页上,直接用Get或Post方法发出HTTP请求。

根据台湾中兴大学吕瑞麟老师的试验 ,这时的编码方法由网页的编码决定,也就是由HTML源码中字符集的设定决定。

  <meta http-equiv="Content-Type" content="text/html;charset=xxxx">

如果上面这一行最后的charset是UTF-8,则URL就以UTF-8编码;如果是GB2312,URL就以GB2312编码。

举例来说,百度是GB2312编码,Google是UTF-8编码。因此,从它们的搜索框中搜索同一个词“春节”,生成的查询字符串是不一样的。

百度生成的是%B4%BA%BD%DA,这是GB2312编码。

bg2010021109.jpg

Google生成的是%E6%98%A5%E8%8A%82,这是UTF-8编码。

bg2010021108.jpg

所以,结论3就是,GET和POST方法的编码,用的是网页的编码。

五、情况4:Ajax调用的URL包含汉字

前面三种情况都是由浏览器发出HTTP请求,最后一种情况则是由Javascript生成HTTP请求,也就是Ajax调用。还是根据吕瑞麟老师的 文章,在这种情况下,IE和Firefox的处理方式完全不一样。

举例来说,有这样两行代码:

  url = url + "?q=" +document.myform.elements[0].value; // 假定用户在表单中提交的值是“春节”这两个字

  http_request.open('GET', url, true);

那么,无论网页使用什么字符集,IE传送给服务器的总是“q=%B4%BA%BD%DA”,而Firefox传送给服务器的总是“q=%E6%98 %A5%E8%8A%82”。也就是说,在Ajax调用中,IE总是采用GB2312编码(操作系统的默认编码),而Firefox总是 采用utf-8编码。这就是我们的结论4。

六、Javascript函数:escape()

好了,到此为止,四种情况都说完了。

假定前面你都看懂了,那么此时你应该会感到很头痛。因为,实在太混乱了。不同的操作系统、不同的浏览器、不同的网页字符集,将导致完全不同的编码结 果。如果程序员要把每一种结果都考虑进去,是不是太恐怖了?有没有办法,能够保证客户端只用一种编码方法向服务器发出请求?

回答是有的,就是使用Javascript先对URL编码,然后再向服务器提交,不要给浏览器插手的机会。因为Javascript的输出总是一致 的,所以就保证了服务器得到的数据是格式统一的。

Javascript语言用于编码的函数,一共有三个,最古老的一个就是escape()。虽然这个函数现在已经不提倡使用了,但是由于历史原因, 很多地方还在使用它,所以有必要先从它讲起。

实际上,escape()不能直接用于URL编码,它的真正作用是返回一个字符的Unicode编码值。比如“春节”的返回结果 是%u6625%u8282,也就是说在Unicode字符集中,“春”是第6625个(十六进制)字符,“节”是第8282个(十六进制)字符。

bg2010021110.png

它的具体规则是,除了ASCII字母、数字、标点符号“@ * _ + - . /”以外,对其他所有字符进行编码。在/u0000到/u00ff之间的符号被转成%xx的形式,其余符号被转成%uxxxx的形式。对应的解码函数是 unescape()。

所以,“Hello World”的escape()编码就是“Hello%20World”。因为空格的Unicode值是20(十六进制)。

bg2010021111.png

还有两个地方需要注意。

首先,无论网页的原始编码是什么,一旦被Javascript编码,就都变为unicode字符。也就是说,Javascipt函数的输入和输出, 默认都是Unicode字符。这一点对下面两个函数也适用。

bg2010021112.png

其次,escape()不对“+”编码。但是我们知道,网页在提交表单的时候,如果有空格,则会被转化为+字符。服务器处理数据的时候,会把+号处 理成空格。所以,使用的时候要小心。

七、Javascript函数:encodeURI()

encodeURI()是Javascript中真正用来对URL编码的函数。

它着眼于对整个URL进行编码,因此除了常见的符号以外,对其他一些在网址中有特殊含义的符号“; / ? : @ & = + $ , #”,也不进行编码。编码后,它输出符号的utf-8形式,并且在每个字节前加上%。

bg2010021113.png

它对应的解码函数是decodeURI()。

bg2010021114.png

需要注意的是,它不对单引号'编码。

八、Javascript函数:encodeURIComponent()

最后一个Javascript编码函数是encodeURIComponent()。与encodeURI()的区别是,它用于对URL的组成部分 进行个别编码,而不用于对整个URL进行编码。

因此,“; / ? : @ & = + $ , #”,这些在encodeURI()中不被编码的符号,在encodeURIComponent()中统统会被编码。至于具体的编码方法,两者是一样。

bg2010021115.png

它对应的解码函数是decodeURIComponent()。

PS1 :

网页里的form编码其实不完全取决于网页编码,form标记中有一个accept-charset属性,在非ie浏览器种,如果将其赋值(比如 accept-charset="UTF-8"),则表单会按照这个值表示的编码方式进行提交。
在ie下,我的兼容解决办法是:
form1.οnsubmit=function(){
document.charset=this.getAttribute('accept-charset');
}

 

转载于:https://www.cnblogs.com/songxinya/p/4711367.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/419683.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java异常处理机简答题,【简答题】JAVA 语言如何进行异常处理,关键字: throws,throw,try,catch,finally 分别代表什么意义?...

【简答题】JAVA 语言如何进行异常处理&#xff0c;关键字&#xff1a; throws,throw,try,catch,finally 分别代表什么意义&#xff1f;更多相关问题用蒸汽进行设备管线吹扫时,应将被吹扫的设备管线,相应部位仪表引线阀关闭,防止损坏仪表。()铝壶内装有2.5kg的水&#xff0c;其温…

阐述linux IPC(五岁以下儿童):system V共享内存

【版权声明&#xff1a;尊重原创。转载请保留源&#xff1a;blog.csdn.net/shallnet 要么 .../gentleliu&#xff0c;文章学习交流&#xff0c;不用于商业用途】system V共享内存和posix共享内存类似&#xff0c;system V共享内存是调用shmget函数和shamat函数。 shmget函数创…

php io select,Python IO多路复用之——select方案服务端和客户端代码【python源码详解】...

准备文件&#xff1a;IO.py 服务端代码tcp_c.py 客户端代码IO.py 代码&#xff1a;from select import * #引入 select 模块from socket import * #引入 socket 模块s socket() #实例化一个socket 对象s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #设置端口可重用s.bind((0.0.0…

NDK和项目依赖

NDK使用 JDK:Java Development KitSDK:Software Development KitNDK:Native Development KitJNI: Java Nattive InterfaceNDK开发流程&#xff1a; 下载NDK并配置NDK路径新建项目 修改build.gradle文件 替换classpath:com.android.tools.build:gradle-experimental:0.4.0 替换a…

java创建树形子目录,Java之File类及递归树形展示目录_20150804

Java之File类及递归树形展示目录20150804下面的构造函数可以用来生成File 对象&#xff1a;File(String directoryPath)File(String directoryPath, String filename)File(File dirObj, String filename)这里&#xff0c;directoryPath是文件的路径名&#xff0c;filename 是文…

使用PDFBox解析PDF文件

今天在Nutch源码中准备增加一个PDF处理方面的功能&#xff0c;其中要做的一步是提取出PDF文档中的文本信息。考虑了一下&#xff0c;还是准备使用PDFBox。看了一下&#xff0c;Nutch源码中的parse-tika插件下有一个PDFBox&#xff0c;不过是1.1.0版本&#xff0c;很多PDF文档都…

matlab 数字图像滤波,数字图像处理 (基于Matlab) 滤波

《数字图像处理》实验报告一、实验目的(不少于200字)一、第一个实验用的是各种空间域的方式来滤波&#xff0c;也就是直接把图像和空间滤波器的模板做卷积&#xff0c;当然图像处理很重要的一个部分还有频域的处理。这就涉及到图像的傅里叶变换&#xff0c;通过将空域内的图像傅…

myelicpes怎么导入PHP项目,利用PHP执行SQL文件,将SQL文件导入到数据库

引用如何利用php自动执行 sql文件。其实很简单&#xff0c;就是获取sql文件中的内容&#xff0c;然后将每一句sql语句一次执行就行啦。//读取文件内容$_sql file_get_contents(test.sql);$_arr explode(;, $_sql);$_mysqli new mysqli(DB_HOST,DB_USER,DB_PASS);if (mysqli_…

atitit. 分销系统规划p8k

atitit. 分销系统规划p8k 1. 商户平台管理 overview2 1.1. 分销业务管理2 1.2. 文案管理2 1.3. 订单管理3 1.4. 统计报表3 1.5. 财务结算3 1.6. 自身信息管理4 2. 商户后台详细5 3. 推广人后台6 3.1. 产品及文案6 3.2. 订单与结算6 3.3. 下线邀请与奖励6 3.4. 订单统计6 3.5. 资…

php数据访问层设计,php - Zend框架数据访问层(DAL) - 堆栈内存溢出

好吧&#xff0c;在处理Data Access Layer &#xff0c;您必须考虑的第一件事是该层还具有子层 &#xff0c;在现代框架中很难找到名为“ dal”的文件夹(我以Zend为基础框架和Symfony)。其次&#xff0c;关于ActiveRecord &#xff0c;您必须知道默认情况下Zend Frameworks 不会…

matlab 8.4,《DSP using MATLAB》Problem 8.42

代码&#xff1a;%% ------------------------------------------------------------------------%% Output Info about this m-filefprintf(‘\n***********************************************************\n‘);fprintf(‘ Problem 8.42 \n\n‘);banner();%% ------------…

lua的string.gsub初使用

今天在学习lua&#xff0c;熟悉项目代码的过程中&#xff0c;发现string.gsub好高级&#xff0c;所以在此mark下。 以下是lua5.1的官方文档介绍。 string.gsub (s, pattern, repl [, n])Returns a copy of s in which all occurrences of the pattern have been replaced by a …

php对象比较大小,PHP 面向对象:对象的比较

简明现代魔法 -> PHP服务器脚本 -> PHP 面向对象&#xff1a;对象的比较PHP 面向对象&#xff1a;对象的比较2010-04-07在PHP中有 赋值符号、 等于符号 和 全等于符号, 这些符号代表什么意思&#xff1f;当使用比较操作符()时&#xff0c;对象以一种很简单的规则比较&a…

mysql 备份

方法一&#xff1a; 1&#xff0c;暂停Mysql进程或服务 2&#xff0c;复制mysql\data文件夹 3&#xff0c;重新安装程序和mysql, 把 ibdata1及数据库文件夹copy到新目录 4&#xff0c;重启服务 方法二 数据导出&#xff1a;mysqldump -u数据库用户名 -p 要导出的数据库> otc…

php程序变量,PHP 变量

PHP 变量变量是用于存储信息的"容器"&#xff1a;实例$x5;$y6;$z$x$y;echo $z;?>运行实例 与代数类似x5y6zxy在代数中&#xff0c;我们使用字母(如 x)&#xff0c;并给它赋值(如 5)。从上面的表达式 zxy &#xff0c;我们可以计算出 z 的值为 11。在 PHP 中&…

为什么构造函数不能是虚函数

为什么构造函数不能是虚函数 从存储空间角度看&#xff0c;使用虚函数时&#xff0c;系统要有一定的空间开销&#xff0c;当一个类带有虚函数时&#xff0c;编译系统会为该类构造一个虚函数表&#xff08;virtual function table&#xff09;&#xff0c;他是一个指针数组&…