周末宅在家里睡完觉就吃饭,吃完饭接着睡觉,这日子过的实在是没劲啊。明明还有计划中的事情没有做, 为什么就是不想去做呢,这样的生活持续下去,必然会成为一个彻头彻尾的loser。上一篇写的 初识编码 ,这一篇把web编码写出来和菜鸟们分享一下。图片比较多,手机用户不要看,流量没了俺不负责。

一.html页面编码

  当浏览器请求一个静态的html页面时,服务器会将html页面的字节流通过网络传输给浏览器。浏览器再将字节流解码成相应的html文本字符,然后将html元素渲染出来。在这个流程中浏览器有一个解码html字节流的过程。浏览器如何判断用什么编码格式去解码呢?在html页面的head标签中通常会包含一个指定页面编码的<meta/>标签,如果指定的是utf-8编码,浏览器就用utf-8解码html页面。问题是在浏览器解析<meta/>标签获得页面编码之前,浏览器是用什么编码来解析<meta/>标签的呢?因为html开头都是字母,基本不会存在ASCII码之外的字符,所以识别起来还是没有难度的。

  如下是我的html代码,第5行和第6行,会根据不同的测试条件分别打开注释,具体打开哪个后边会有提示。

 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -->
<!-- <meta http-equiv="Content-Type" content="text/html; charset=GBK" /> -->
<title>utf-8 html</title>
</head>
<body>
<p>Get 请求</p>
<form action="TestServlet" method="get">
<input type="text" value="你好" name="txtKey"/>
<input type="submit" value="提交"/>
</form>
<p>Post 请求</p>
<form action="TestServlet" method="post">
<input type="text" value="你好" name="txtKey"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>

  演示过程用的环境是tomcat+servlet,浏览器是chrome。项目结构如下,其中有两个html页面,index.htm页面用utf-8编码存储,lindexGBK.html页面用GBK编码存储。两个页面的html代码都是上边给出的html代码。

     

实验一: 在utf-8编码的html页面中指定不同的编码格式

  启动项目,用浏览器打开index.html的url,如左下图所示,可以看出浏览器解析到<meta/>标签中的utf-8编码,并用utf-8解码html页面,页面正确显示。如果在<meta/>标签中指定页面编码为GBK,那么浏览器就用GBK解码,肯定就不能正确显示页面了,如右下图所示,页面中文出现乱码。如果想避免乱码的话,还是必须保证编解码格式统一。

     

实验二: 不指定页面编码的情况下测试不同的编码页面

  这次index.html和indexGBK.html都没有指定页面编码。用浏览器打开index.html对应的url,如左下图所示,可以看出浏览器用utf-8解码html页面,页面正确显示。用浏览器打开indexGBK.html对应的url,浏览器也是用utf-8解码,肯定就不能正确显示页面了,如右下图所示,页面中文出现乱码。因为在没有指定页面编码的情况下,浏览器采用操作系统默认编码,我的系统就是utf-8编码,所以index.html页面就碰巧解析正确了。

     

实验三: 在一个html页面中指定两个不同的编码

  用浏览器打开index.html对应的url,如左下图所示,有两个<meta/>标签指定了不同的页面编码。当utf-8编码在GBK编码上面时,可以看出浏览器用utf-8解码html页面,页面正确显示。如右下图所示,如果GBK编码在utf-8编码上面,那么浏览器就用GBK解码,页面中文出现乱码。所以如果出现多个编码以第一个出现的为主,当然这种情况是不可能出现的(出现了也是代码问题),为什么会出现这种情况我觉得需要解释一下,因为浏览器在解析<meta/>标签时一旦发现页面编码,立马就用这个编码解析页面。

     

  html的form在没有指定accept-charset属性的情况下,html的表单提交采用的编码也是指定的页面编码,如果没有指定页面编码,也是采用操作系统的默认编码。总之,html页面的加载与表单提交采用的编码格式是一样的。因为浏览器认为你指定的页面编码就是html文件的实际编码,所以不管是否乱码,它都会照做。

  与html页面编码相关的还有js文件编码,默认情况下,浏览器采用html的编码格式去解码js文件。如果js文件与html页面的编码不同怎么办?script标签有一个charset属性,这个属性就决定了浏览器去加载完js,解析字节流时用的编码。

<script type="text/javascript" src="myscripts.js" charset="UTF-8"/>

二.Web后台编码

  示例代码是Java写的,其他语言也是一个道理,不耽误理解。请求后台无非是GET或POST方法,原理是一样的,我只介绍POST。

html页面是utf-8格式的, 页面编码也指定为utf-8。

实验一:后台解码

  后台响应代码如下所示,页面以post方式请求时会触发doPost方法。

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("START...");
String param=request.getParameter(PARAM);
System.out.println("原始参数");
System.out.println(param);
System.out.println("utf-8解码");
System.out.println(new String(param.getBytes("ISO-8859-1"),"utf-8"));
System.out.println("GBK解码");
System.out.println(new String(param.getBytes("ISO-8859-1"),"GBK"));
System.out.println("END...");
System.out.println("");
}

  先是直接获取参数打印出来,然后又将获取的参数用ISO-8859-1编码,再用utf-8和GBK分别解码并打印出来。点击页面的Post表单提交,控制台结果如下:

  html提交普通表单时,参数以字节流的方式传输到web服务器,web服务器解码字节流得到参数。tomcat默认采用ISO-8859-1编解码,所以直接获取参数是乱码,以ISO-8859-1编码回原本的字节流再用utf-8重新解码就可以得到正确的参数。

  为什么tomcat采用ISO-8859-1为默认的编码格式,导致解码流程这么复杂?在servlet规范中明确规定:如果客户端请求没有指定字符编码,web容器用来创建请求读取器和解析POST 数据的编码必须是“ISO-8859-1”。为什么会有这么奇葩的规定呢?我猜可能是适应早期的浏览器吧,早期的浏览器大多采用ISO-8859-1为默认编码,这个编码的应用是非常广泛的,觉得陌生或不可思议只是因为我们在中国。稍微捋一下,ASCII码是1967年的,ISO-8859-1是1987年的,unicode是1994定稿,浏览器是在94年底95年初发布,所以估计早期的浏览器来不及用unicode作为默认编码。我是用这么一套理论来说服自己的,对不对就不重要了,总之ISO-8859-1在很多国家是普遍采用的,在人家的圈子里做东西采用人家的编码也是正常的。

  可以在获取任何参数前调用 request.setCharacterEncoding("utf-8") ,即可用utf-8解码参数,就不用那么麻烦了。其他语言在原理上也都是相同的。

实验二:响应编码

  响应代码更改为如下代码。

 protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// response.setCharacterEncoding("utf-8"); ①
// response.setContentType("text/html; charset=UTF-8"); ②
// response.setHeader("Content-Type", "text/html; charset=UTF-8"); ③
PrintWriter writer = response.getWriter();
writer.print("<html><head><meta charset=\"utf-8\"/></head><body><font color=\"red\">你好</font></body></html>");
writer.close();
}

  只在响应的html页面中指定了页面编码,显示是乱码,如左下图所示。因为在输出时并没有指定编码格式,默认采用ISO-8859-1编码,浏览器用utf-8解码自然就是乱码了。

  代码中注释掉的三行,是java常用的设置响应编码方式。②跟③的效果是一样一样的,只讨论①和②。将注释①打开,响应如右下图所示,指定了响应编码为utf-8,浏览器用utf-8解码正确显示。

      
实验三:setCharacterEncoding 与 setContentType 区别

  setCharacterEncoding只是把响应字符按指定格式编码,setContentType除此之外,还在header里添加了contentType属性。只把注释②打开也可以正常显示,在下图右侧ResponseHeaders中有Content-Type属性 “text/html; charset=utf-8” 。当header中出现Content-Type属性并出现指定编码,浏览器将忽略html里<meta/>标签中指定的编码,以header中的编码解码html页面。

  当setCharacterEncoding和setContentType同时出现时,后边的编码会覆盖前边的编码。响应代码更改为如下所示。

 protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=utf-8");
response.setCharacterEncoding("GBK");
PrintWriter writer = response.getWriter();
writer.print("<html><head><meta charset=\"utf-8\"/></head><body><font color=\"red\">你好</font></body></html>");
writer.close();
} 

  响应编码设为GBK,contentType属性指定的编码却是utf-8,浏览器按照contentType指定的编码来解码岂不是乱码?如下图所示,答案是否定的,真实的contentType是“text/html; charset=GBK”,这是为什么,明明设置的“text/html; charset=utf-8”。因为contentType包含的其实是两部分,一部分是text/html,第二部分是charset=utf-8。设置了contentType之后,这两部分是分别存储的,setCharacterEncoding会把第二部分编码的值覆盖掉。这时contentType就变成了“text/html; charset=GBK”。

实验四:浏览器对动态页面与静态页面在默认编码上的区别

  前边说过了,html静态页面在没有指定页面编码的时候是按照系统默认编码解码的。响应代码更改如下所示。

 protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.print("<html><head></head><body><font color=\"red\">你好</font></body></html>");
writer.close();
}  

  没有任何地方指定页面编码。按照静态html的规律,浏览器采用我的系统默认编码utf-8解码,不会出现乱码。但是实际情况并不是这样,如左下图所示,乱码还是出现了,因为浏览器使用的GBK去解码。这是为什么呢?打开chrome的设置-》显示高级设置-》自定义字体-》出现右下图所示的窗口,可以看到chrome的默认编码是GBK,也就说如果动态页面没有指定页面编码将会按照浏览器的默认编码来解码。为了验证我们的猜测,将chrome编码设置为utf-8,重新请求一遍,果然页面正确显示。这只是我的猜测,具体对错未知,但是未指定页面编码的情况是不多见的,大家在编码过程中一定要指定页面编码为页面实际的存储编码。

     

下篇文章介绍一下应用程序交互时的字节流编码。

从原理上搞定编码(二)-- Web编码的更多相关文章

  1. 001_从原理上搞定编码-- Base64编码

    开发者对 Base64编码肯定很熟悉,是否对它有很清晰的认识就不一定了.实际 上Base64已经简单到不能再简单了,如果对它的理解还是模棱两可实在不应该.大概介绍一下Base64的相关内容,花几分钟时 ...

  2. 从原理上搞定编码-- Base64编码

    BASE64是一种编码方式,通常用于把二进制数据编码为可写的字符形式的数据.这是一种可逆的编码方式.编码后的数据是一个字符串,其中包含的字符为:A-Z.a-z.0-9.+./共64个字符:26 + 2 ...

  3. 从原理上搞定编码(四)-- Base64编码

    开发者对Base64编码肯定很熟悉,是否对它有很清晰的认识就不一定了.实际上Base64已经简单到不能再简单了,如果对它的理解还是模棱两可实在不应该.大概介绍一下Base64的相关内容,花几分钟时间就 ...

  4. git从安装到多账户操作一套搞定(二)多账户使用

    作者:良知犹存 转载授权以及围观:欢迎添加微信:Allen-Iverson-me-LYN 总述     GIT是当今热门代码管理技术,但是如此火的系统,竟然是大神林纳斯花了两周用C写出来的一个分布式版 ...

  5. 表格搞定 Asp.net Web 状态管理

    最近在网上搜罗了 ASP.NET WEB 状态管理方面的一些内容,终于把这些内容整合总结了一下. 1. 希望自己通过整理,能够掌握一些,为自己投资. 2. 以便自己忘记,又要浪费时间搜罗. 3. 希望 ...

  6. ASP.NET Core 编码、web编码、网页编码 System.Text.Encodings.Web

    System.Text.Encodings.Web 空间包含表示 Web 编码器的基类.表示 HTML.JavaScript 和 Url 字符编码的子类,以及表示仅允许编码特定字符.字符范围或码位的筛 ...

  7. [转]用GSON 五招之内搞定任何JSON数组

    关于GSON的入门级使用,这里就不提了,如有需要可以看这篇博文 <Google Gson的使用方法,实现Json结构的相互转换> ,写的很好,通俗易懂. 我为什么写这篇文章呢?因为前几晚跟 ...

  8. [转] Android:用GSON 五招之内搞定任何JSON数组

    [From] http://www.open-open.com/lib/view/open1472632967912.html 写在前面 关于GSON的入门级使用,这里就不提了,如有需要可以看这篇博文 ...

  9. WEB安全第二篇--用文件搞定服务器:任意文件上传、文件包含与任意目录文件遍历

    零.前言 最近做专心web安全有一段时间了,但是目测后面的活会有些复杂,涉及到更多的中间件.底层安全.漏洞研究与安全建设等越来越复杂的东东,所以在这里想写一个系列关于web安全基础以及一些讨巧的pay ...

随机推荐

  1. 20145226夏艺华 网络对抗技术 EXP7 网络欺诈技术防范

    20145226夏艺华 网络对抗技术 EXP7 网络欺诈技术防范 实践内容 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法. · 简单应用SET工具建立冒名网站 · ett ...

  2. XAMPP设置tomcat自启动时,报无效的Win32程序

    最近给一个客户开发了一套系统,需要在内网中部署.系统是Java + Tomcat7 + mysql开发的. 考虑到客户内网不能上网的情况下,想使用XAMPP的便捷性,给客户进行部署.因为只需要Tomc ...

  3. TCP三次握手和四次挥手以及11种状态

    1.三次握手 置位概念:根据TCP的包头字段,存在3个重要的标识ACK.SYN.FIN ACK:表示验证字段 SYN:位数置1,表示建立TCP连接 FIN:位数置1,表示断开TCP连接 三次握手过程说 ...

  4. loj2538 「PKUWC 2018」Slay the Spire

    pkusc 快到了--做点题涨涨 rp. ref我好菜啊QAQ. 可以发现期望只是一个幌子.我们的目的是:对于所有随机的选择方法(一共 \(\binom{2n}{m}\)种),这些选择方法都最优地打出 ...

  5. async+await 让界面飞,让双手爽

    .net 4.5已经发布很久了,但是一直也没有静下心来好好的研究微软给开发者带来的喜悦. 今天我将简单的介绍下 async + await 这对搭档的出现,如何让频繁假死的界面飞起来(其实只是不再阻塞 ...

  6. 「Leetcode」14. Longest Common Prefix(Java)

    分析 与其说是算法题,不如说是语言特性题. 这题要是对Java的String相关函数掌握的比较熟练,写起来的速度(各种意义上)就会很快. 大致的思路都是一致的,差不到哪里去,无非是枚举长度.值得一提的 ...

  7. selenium自动化之定位多个元素

    前面我们讲的都是如何定位单个元素,下面讲下怎么去定位多个元素,并且输出文本. 以百度为例:获取标红的这一组元素的文本 这里我用到的是xpath来定位的://div[@id="u1" ...

  8. TPO-17 C2 Reschedule part-time job in campus dining hall

    TPO-17 C2 Reschedule part-time job in campus dining hall 第 1 段 1.Listen to a conversation between a ...

  9. 从零开始的Python学习Episode 8——深浅拷贝

    深浅拷贝 一.浅拷贝 列表中存储的是数据的内存地址,当我们要查询或修改列表中的数据时,我们是通过列表中的地址找到要访问的内存.当我们修改列表中的数据时,如果修改的是一个不可变类型(整型,长整型,浮点数 ...

  10. 从零开始的Python学习Episode 7——文件基本操作

    文件基本操作 一.打开文件 f = open('11','r')#open('file path','mode') 创建一个文件对象 文件有多种打开模式: 1. 'r':新建一个文件对象以只读方式打开 ...