一、姓名存储和展现
1. 按学生总表编码存储
李洋
薛鹏磊
按照学生总表进行编码,并将编码进行存储或者发送
- 左边是客户端(或编辑器-编写文件)
- 右边是服务端(或编辑器-打开文件)
- 双方都有一整套编码-字符对照表
- 只有双方都用同一套编码表,才能看到相同的字符
- 客户端必须告诉服务端,编码时用的是哪一套
- 如果客户端没有告诉编码表,那么服务端会选一个默认表
2. 按学生总表解码展现
总表(UTF-8:1~4字节=1个字符)
编码 | 字符 |
---|---|
1 2 7 | 李洋 |
1 2 9 | 薛鹏磊 |
3. 按1711提高班解码展现
1711提高班(GBK:2字节=1个字符)
编码 | 字符 |
---|---|
1 2 | 姚柯雪 |
7 1 | 不识别 |
2 9 | 不识别 |
4. 按1709基础班解码展现
1709基础班(ISO8859-1:1字节=1个字符)
编码 | 字符 |
---|---|
1 | 吴昊 |
2 | 吴令 |
7 | 赵彦博 |
1 | 吴昊 |
2 | 吴令 |
9 | 袁峰 |
基本概念
名称 | 含义 |
---|---|
字符 | 展示给人看 |
编码 | 给计算机看,文件存储或网络传输 |
字符编码表 | 字符和编码一一对应的关系 |
字符转编码 | 写文件、或者发送报文时,字符自动转成编码 |
编码转字符 | 打开文件、或者接收报文时,编码自动转成字符 |
默认编码 | 如果未指定具体编码表,则按照一个默认表编码和解码,一般是utf-8、iso8859-1、gbk中的一种 |
常见编码和编码表
编码 | 在线编码表 | 字节数 |
---|---|---|
ASSIC | 美国 | 1字节=1字符 |
iso8859-1 | 欧洲 | 1字节=1字符 |
gbk | 中国 | 2字节=1字符 |
utf-8 | 统一各国 | 1~4字节=1字符 |
软件 | 设置方式 |
---|---|
notepad | 编辑时设置编码格式,打开是也指定同样格式 |
jmeter | 报文设置encoding编码,加入http请求头,服务端解析 |
html界面 | htmlhead里设置界面编码,浏览器自动读取并解析 |
1. ASCII码****:美国
我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000到11111111。
上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。
ASCII码一共规定了128个字符的编码,比如空格“SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。
2、ISO8859-1:欧洲
英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。
但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0—127表示的符号是一样的,不一样的只是128—255的这一段。
ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。此字符集主要支持欧洲使用的语言。
3. GBK****:中国
计算机普及了****,****于是就中国要使用计算机了 但是机器不认得中文,于是就有了国际码。 gbk gb2312都是这类。两个其实一个,一个是标准(发布的代号),一个是简称。
GBK: 汉字国标扩展码,基本上采用了原来GB2312-80所有的汉字及码位,并涵盖了原Unicode中所有的汉字20902,总共收录了883个符号, 21003个汉字及提供了1894个造字码位
4. UniCode****:全球
后来多了个阿拉伯语、日语、韩语......所以就出来统一编码UniCode****。正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。
可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。
Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字“严”。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表。
4. Unicode的问题
需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
比如,汉字“严”的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
这里就有两个严重的问题,第一个问题是,如何才能区别unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?
第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
它们造成的结果是:1)出现了unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示unicode。2)unicode在很长一段时间内无法推广,直到互联网的出现。
5.UTF-8****:unicode字符集存储方式之一
互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。
UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
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 0000-0080 07FF | 110xxxxx10xxxxxx 0000 0800-0000 FFFF | 1110xxxx10xxxxxx10xxxxxx 0001 0000-0010 FFFF | 11110xxx10xxxxxx10xxxxxx10xxxxxx
0xxxxxxx110xxxxx
10xxxxxx1110xxxx
10xxxxxx10xxxxxx
11110xxx10xxxxxx
10xxxxxx10xxxxxx
下面,还是以汉字“严”为例,演示如何实现UTF-8编码。
已知“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。
6. Unicode与UTF-8之间的转换
通过上一节的例子,可以看到“严”的Unicode码是4E25,UTF-8编码是E4B8A5,两者是不一样的。它们之间的转换可以通过程序实现。
在Windows平台下,有一个最简单的转化方法,就是使用内置的记事本小程序Notepad.exe。打开文件后,点击“文件”菜单中的“另存为”命令,会跳出一个对话框,在最底部有一个“编码”的下拉条。
[图片上传失败...(image-4634d8-1516711584296)]
7. Little endian和Big endian
上一节已经提到,Unicode码可以采用UCS-2格式直接存储。以汉字”严“为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。
这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。
因此,第一个字节在前,就是”大头方式“(Big endian),第二个字节在前就是”小头方式“(Little endian)。
那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。
如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
8. 实例
下面,举一个实例。
打开”记事本“程序Notepad.exe,新建一个文本文件,内容就是一个”严“字,依次采用ANSI,Unicode,Unicode big endian 和 UTF-8编码方式保存。
然后,用文本编辑软件UltraEdit中的”十六进制功能“,观察该文件的内部编码方式。
1)ANSI:文件的编码就是两个字节“D1 CF”,这正是“严”的GB2312编码,这也暗示GB2312是采用大头方式存储的。
2)Unicode:编码是四个字节“FF FE 25 4E”,其中“FF FE”表明是小头方式存储,真正的编码是4E25。
3)Unicode big endian:编码是四个字节“FE FF 4E 25”,其中“FE FF”表明是大头方式存储。
4)UTF-8:编码是六个字节“EF BB BF E4 B8 A5”,前三个字节“EF BB BF”表示这是UTF-8编码,后三个“E4B8A5”就是“严”的具体编码,它的存储顺序与编码顺序是一致的。
请问URI和URL有什么区别?****URL是全球资源定位符的英文所写,您平时上网时在IE浏览器中输入的那个地址就是URL。比如:网易 http://www.163.com就是一个URL。****URI是Web上可用的每种资源 - HTML文档、图像、视频片段、程序,由一个通过通用资源标志符(Universal Resource Identifier, 简称"URI")进行定位。****URL的格式由下列三部分组成: ****第一部分是协议(或称为服务方式); ****第二部分是存有该资源的主机IP地址(有时也包括端口号); ****第三部分是主机资源的具体地址。****URI一般由三部分组成: ****访问资源的命名机制。 ****存放资源的主机名。 ****资源自身的名称,由路径表示。****URI 是从虚拟根路径开始的****URL是整个链接****如****URL http://zhidao.baidu.com/question/68016373.html ****URI 是/question/68016373.html
常用URL编码表
%0A: linefeed(换行),很多手机url编码后会自动在句末添加%0A,后端会无法识别导致报错,因此需要把它去掉。
%20:space(空格)
只有
字母:a -> z、A -> Z
数字:0 -> 9
*特殊符号:****$-_.+!'(), **
以及某些保留字,
才可以不经过编码直接用于 URL。
一、问题的由来
URL就是网址,只要上网,就一定会用到。
[图片上传失败...(image-fa867d-1516711584295)]
一般来说,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/春节**** ”。 注意,“春节”这两个字此时是网址路径的一部分。
[图片上传失败...(image-b412da-1516711584295)]
查看HTTP请求的头信息,会发现IE实际查询的网址是“****http://zh.wikipedia.org/wiki/%E6%98%A5%E8%8A%82**** ”。 也就是说,IE自动将“春节”编码成了“%E6%98%A5%E8%8A%82”。
[图片上传失败...(image-881318-1516711584295)]
我们知道,“春”和“节”的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混淆。
[图片上传失败...(image-b9cf73-1516711584295)]
查看HTTP请求的头信息,会发现IE将“春节”转化成了一个乱码。
[图片上传失败...(image-9f420d-1516711584295)]
切换到十六进制方式,才能清楚地看到,“春节”被转成了“B4 BA BD DA”。
[图片上传失败...(image-5f3b63-1516711584295)]
我们知道,“春”和“节”的GB2312编码(我的操作系统“Windows XP”中文版的默认编码)分别是“B4 BA”和“BD DA”。因此,IE实际上就是将查询字符串,以GB2312编码的格式发送出去。
Firefox的处理方法,略有不同。它发送的HTTP Head是“wd=%B4%BA%BD%DA”。也就是说,同样采用GB2312编码,但是在每个字节前加上了%。
[图片上传失败...(image-bc34f7-1516711584295)]
所以,****结论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编码。
[图片上传失败...(image-97f140-1516711584295)]
Google生成的是%E6%98%A5%E8%8A%82,这是UTF-8编码。
[图片上传失败...(image-4110df-1516711584295)]
所以,****结论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个(十六进制)字符。
[图片上传失败...(image-4272fa-1516711584295)]
它的具体规则是,除了ASCII字母、数字、标点符号“@ * _ + - . /”以外,对其他所有字符进行编码。在/u0000到/u00ff之间的符号被转成%xx的形式,其余符号被转成%uxxxx的形式。对应的解码函数是 unescape()。
所以,“Hello World”的escape()编码就是“Hello%20World”。因为空格的Unicode值是20(十六进制)。
[图片上传失败...(image-67591e-1516711584295)]
还有两个地方需要注意。
首先,无论网页的原始编码是什么,一旦被Javascript编码,就都变为unicode字符。也就是说,Javascipt函数的输入和输出, 默认都是Unicode字符。这一点对下面两个函数也适用。
[图片上传失败...(image-61b1c-1516711584295)]
其次,escape()不对“+”编码。但是我们知道,网页在提交表单的时候,如果有空格,则会被转化为+字符。服务器处理数据的时候,会把+号处 理成空格。所以,使用的时候要小心。
七、Javascript函数:encodeURI()
encodeURI()是Javascript中真正用来对URL编码的函数。
它着眼于对整个URL进行编码,因此除了常见的符号以外,对其他一些在网址中有特殊含义的符号“; / ? : @ & = + $ , #”,也不进行编码。编码后,它输出符号的utf-8形式,并且在每个字节前加上%。
[图片上传失败...(image-5d1851-1516711584295)]
它对应的解码函数是decodeURI()。
[图片上传失败...(image-a56c88-1516711584294)]
需要注意的是,它不对单引号'编码。
八、Javascript函数:encodeURIComponent()
最后一个Javascript编码函数是encodeURIComponent()。与encodeURI()的区别是,它用于对URL的组成部分 进行个别编码,而不用于对整个URL进行编码。
因此,“; / ? : @ & = + $ , #”,这些在encodeURI()中不被编码的符号,在encodeURIComponent()中统统会被编码。至于具体的编码方法,两者是一样。
[图片上传失败...(image-3c2021-1516711584294)]
它对应的解码函数是decodeURIComponent()。
PS1**** :
网页里的form编码其实不完全取决于网页编码,form标记中有一个accept-charset属性,在非ie浏览器种,如果将其赋值(比如 accept-charset="UTF-8"),则表单会按照这个值表示的编码方式进行提交。****在ie下,我的兼容解决办法是:****form1.onsubmit=function(){****document.charset=this.getAttribute('accept-charset');****}