字符从浏览器到数据库过程的转换
尽管开发些web程序,但也没有系统地理解字符到底如何在浏览器、web服务器、数据库服务器三者之间如何来回转换,也经常看到初学者或者有些“高手”亦有类似困惑,偶然间,看到SUN网站上这篇文章,系统通俗地讲述了该问题,看后受益匪浅。翻译过来,与大家分享。
翻译自:http://java.sun.com/developer/technicalArticles/Intl/HTTPCharset/
字符在3W中的最终存储目的地路线中,穿过了不同层次的编程接口,并且可以跨越软件和硬件的边界。本文提供了准确的从浏览器到数据库传输字符数据...,然后再返回过程中的一些有用提示和最佳做法。
内容:
- 浏览器显示和表单提交
- Web设置
- 数据库设置
- 总结
为国际化,许多操作系统,应用开发语言,以及平台都走过了漫长的道路。有些事情很容易,比如在Swing 文本框中输入你的姓名。通过键盘,输入方法,和主机软件协作来创建正确的字符,无论你的名字是John,José, or 。不幸的是,虽然在浏览器中输入非ASCII文本与你在Swing组件中一样容易,但通过web精确传送它却是复杂的。因为没有工业标准来控制应用程序如何在GET或者POST方法中对数据进行编码,在多个编程接口传输过程中数据可能被转换成无意义的乱码。而且,web服务器和数据库管理者通常对字符编码都了解甚少,这样就影响了文本高保真的从浏览器到数据库的传输。
浏览器显示和表单提交(Browser Display and Form Submission) 只要HTML页面为浏览器解析字符提供了足够的选择和使用合适的字体及编码的提示,现代浏览器就能够正确显示大多数文本。下面的图显示了当你没有为浏览器提供任何字符编码信息时,浏览器的一个可能的显示。这种情况下,通过HTML页面接受的字符虽然没有数据丢失但导致了错误的解析。
图1 没有为浏览器提供字符编码信息
在下图中,浏览器(火狐1.07)错误地按照ISO 8859-1编码解析文件内容。尽管大多数浏览器都允许用户针对某个特定文件修改或者覆盖对应的编码设置,但是这种期望对普通用户来讲是不合理的。
图2 不正确的字符编码信息
这个文本实际上是UTF-8(一种Unicode编码)编码的文本。当在HTML中利用<mata>标签设置该信息后,浏览器就能正确显示日语中的“Hello World!”。
图3 给浏览器正确的字符编码信息
能正确显示的HTML文件如下所示。注意其中的<meta>标签包含一个content属性,它告诉浏览器该文件有个使用charset=UTF-8的text/html类型。content属性中Charset关键字表示HTML文件的字符编码。另外,要尽可能使用language标签,这样浏览器就能够发现和利用合适的具体语言字形。比如,中文和日语都使用许多许多相同的字符。因为这些语言中和字形和其它语言相比有很大不同,所以,language标签可以帮助浏览器发现最合适的字体。
Japanese Language Tag
如果你用JSP技术生成页面,仍然必须指示生成的HTML页面的字符编码。你可以使用JSP的page指令或HMTL的meta标签。page指令应是你的JSP文件的第一个元素且应该包含一个带charset设置的contenType属性。JSP编译器使用pageEncoding属性来编译JSP页面本身的。
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
现在你的页面能够正确编码了,并且它们可以与浏览器交流信息了。非常棒,但是仍然没有从浏览器传过来任何东西。正如已经展示的那样,浏览器从保存在HTTP header中charset信息或者HTML中<meta>标签知道了页面的编码信息。当通过GET或者POST向服务器返回信息时,它们使用相同的信息对form表单中的数据进行编码。如果在HTML的form表单标签中有一个accpet-charset属性,浏览器将使用该设置。也就是说,浏览器使用页面或者form表单本身编码方式对form表单中的数据进行编码。
下面的JSP展示了一个页面,它指示了页面内容使用ISO-8859-1编码,一种在西欧和美洲国家用来处理语言和脚本的常用字符编码。这个页面生成一个询问用户name的HTML表单。在提交时,同一个页面处理GET命令并且使用NAME属性打印出欢迎用户的信息。
当输入John后点击提交按钮,浏览器创建并且使用下面的URL向服务器发送信息。
http://localhost/sayhello.jsp?NAME=John
到目前为止一切正常,现在,输入一个José后点击提交按钮。由此产生的URL如下所示:
http://localhost/charconv/sayhello.jsp?NAME=Jos%E9
尽管产生的NAME参数看见有些不同,但是它是正确的。%E9字符串是一个URL-Encoding字符:即字符在ISO-8859-1字符集中代码点(codepoint)所对应的十六进制整数值。服务器期望GET和POST数据使用8859-1编码,因此它能够正确对该URL编码实体进行解码。
简单讲,URL编码是一个URL方面的web标准。它要求把所有非ASCII字符和特殊的ASCII字符按照%HH形式编码为十六进制字符串。不幸的是,标准中并没有规定对数据编码时使用何种字符集。
当表单中输入的字符没有包含在页面中的字符集时将会发生什么呢?想象一个日本,韩国或者中国的用户在相同的页面中输入他们的NAME。这些字符很可能没有出现在ISO-8859-1编码中。浏览器使用8859-1对它们编码将会出现一个重大问题,而且每个浏览器使用不同的方式来处理这个问题。
比如日本名字田中,FF浏览器产生下面的GET URL:
http://localhost/charconv/sayhello.jsp?NAME=%26%2330000%3B%26%2320013%3B
这个URL第一眼看上去有些奇怪,但是很快你能解密它的意思。首先,FF浏览器知道它不能用8859-1字符集来表示田中....它甚至都没有尝试。相反,它使用数字字符引用(NCR)来对字符编码。其它浏览器可能使用不同的方式,选择性来生成问号或者甚至完全阻止他们的输入。然而在这个情况下,每个字符都被编码成&#D;形式,这里D表示了字符在Unicode字符集中的十进制代码点值。其中田字符的值为30000,中的值为20013。根据创建的数字字符引用(NCR),我们可以产生出下面的参数字符串:
NAME=田中
现在URL-Encoding出现了,把特殊的&,#和; 字符转换成为了它们%HH相等的值。
NAME=%26%2330000%3B%26%2320013%3B
<