`
xiebh
  • 浏览: 612666 次
  • 性别: Icon_minigender_1
  • 来自: 太原
社区版块
存档分类
最新评论

字符从浏览器到数据库过程的转换

阅读更多
尽管开发些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=&#30000;&#20013;


现在URL-Encoding出现了,把特殊的&,#和; 字符转换成为了它们%HH相等的值。
NAME=%26%2330000%3B%26%2320013%3B


如果你的JSP页面检索这个参数并简单地给浏览器返回该值,将不会丢失数据。尽管页面的字符编码仍然是8859-1,浏览器也理解字符引用的数字,并且它将尽自己最大努力显示日文。然而,这里仍然有个潜在的问题。尽管处理GET和POST命令的服务器端的代码理解URL-Encoding字符串,它也很难能理解数字字符引用NCR。大多数读取NAME参数的servlet代码根据&#30000;&#20013;来解码字符串,除了字面意思并不知道这代表任何东西。换句话说,真实的田中字符将不会被发现除非你已经有意识的计划处理这种情况。因此发送数据时,不用实际的代码点或编码值而用数字字符引用(NCR)编码将会造成潜在的数据丢失。

为了解决这个问题,始终选择一个HTML网页编码,它可以处理你打算处理的所有字符。如果你期望有多语言用户并希望处理多种脚本,使用一种能表示所有脚本的字符编码。UTF-8是一种统一字符编码,并且它能正确表示世界上所有字符。若在页面中使用UTF-8而不是8859-1,浏览器将会产生一个不同的URL:
http://localhost/charconv/sayhello.jsp?NAME=%E7%94%B0%E4%B8%AD


这是一个正确的NAME参数的编码,它使用了UTF-8字符集。UTF-8使用3个字节对田中编码。因为每个字节有个超出ASCII范围的值,浏览器对其进行URL编码,这样每个字节就产生了%HH形式。期望UTF-8形式数据的服务器将能够正确处理该参数。

Web服务器处理过程

尽管当前的浏览器能根据页面或者form表单中提示把form中数据使用相同的编码返回给服务器,但大多数服务器仍认为浏览器不知道如何选择字符。它们典型假设浏览器编码是ISO 8859-1。即使一个应用经历了用UTF-8 来对GET参数进行URL编码的麻烦,服务器仍将假设使用8859-1。这就导致了字符通过多个web应用层时出现了乱码。

当你使用UTF-8字符集来修改以前的JSP代码时,新的代码如下所示:



现在输入“José”并点击提交按钮,GET的URL如下:
http://localhost/sayhello.jsp?NAME=Jos%C3%A9
%C3%A9 是José 使用UTF-8对其进行URL编码的结果。没问题,浏览器根据contenType中UTF-8提示对字符进行编码。浏览器显示下面的文本。



Figure 4. UTF-8 Character Encoding


注意其中的展现的文本Hello, José!,这是不对的。发生了什么?尽管浏览器正确地发送了GET数据,但web服务器从URL中读取NAME参数Jos%C3%A9时,不正确地假设字符编码是8859-1。从这个角度讲,%C3代表了 ISO-8859-1中0xC3 (Ã) 字符的编码,%A9 代表了ISO-8859-1中 0xA9 (©)  。虽然contentType已经明确写在了JSP标签中了!

现在还没有公布的标准规定(浏览器和服务器)如何沟通关于GET和POST数据的字符集选择。如何解决呢?一些服务器尝试着解决这些问题。Tomcat中通过在server.xml文件里面的connector(连接器)中设置URIEncoding="UTF-8"来告诉web服务器如何选择字符编码,这样Tomcat就能正确读取URL中的GET参数了。它然后就发出了期望的"Hello, José!" 或者田中。在Sun Java System Application Server ,你可以在sun-web.xml文件中包含<parameter-encoding default-charset="UTF-8"/>。URL正确解释如下图所示:




Figure 5. URL Interpretation


假如你想POST非ASCII数据将会如何呢?一切都正常,因为你设置了URIEncoding标签,对吗?错了,Tomcat不使用URIEncoding标签来处理POST过来的form表单数据。那么,它将使用什么编码?ISO-8859-1.

所以现在,你又回到了你的起点,应用仅简单地返回Mr. ç°ä,而不是Mr. 田中。而SUN应用服务器在设置了parameter-encoding标签后,能够正确处理GET和POST数据,所以这些代码在该系统上能工作很好。

不幸的是,这些解决方法都完全依赖于服务器,并且你不能总控制你的应用在哪里部署。幸运的是,存在着与服务器独立的方法。

可能,最基本的服务器独立的方案是设置一个context参数来指示应用中的所有表单如何选择字符编码。然后你的应用就能在读取任何请求参数之前,通过读取context参数并能设置请求字符编码。你能够在servlet或者JSP中设置请求编码。
在WEB-INF/web.xml中设置context参数:
<context-param>
  <param-name>PARAMETER_ENCODING</param-name>
  <param-value>UTF-8</param-value>
</context-param>

在读取前面的JSP文件之前加入下面的代码:
String paramEncoding = application.getInitParameter("PARAMETER_ENCODING");
request.setCharacterEncoding(paramEncoding);
在一个servlet中,你可以在servlet初始化时读取参数并在处理参数之前设置编码。
public class MsgHandler extends HttpServlet {   
    private String encoding;    
    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        encoding = config.getInitParameter("PARAMETER_ENCODING");
    }
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        if (encoding != null) {
            request.setCharacterEncoding(encoding);
        }
        ...
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }
}

现在,无论你GET或POST数据,你的JSP或servlet都能正确读取参数,因为你已经明确告诉web服务器处理请求时使用哪种字符编码。

数据库设置Database Settings
假如你的数据存活时间很长,经过了浏览器编码和web服务器处理,那么数据库就是字符数据转换的最后一个障碍了。为了安全地把数据存放到数据库并不受影响地检索它,你必须配置数据库系统使用一种被浏览器和业务逻辑层都支持的且能表示所有字符集来存储数据。因为UTF-8已使用至今,所以在数据库中选择它是有意义的。否则,当你存储了数据库的编码不支持文本数据时,数据将会造成不可逆的丢失。
下面的图展示了这样的数据丢失(当数据库字符编码不是浏览器和中间层字符集的超集)。在这种情况下,浏览器和中间层使用UTF-8,但是数据库配置为ASCII。



Figure 6. Database Configured for ASCII

一旦你把数据库字符集修改为UTF-8,不管怎么样,数据从浏览器迁移到数据库,并再次回到都完整的保真度。



Figure 7. Database Configured for UTF-8


现代多数数据库系统都支持Unicode。两个流行的关系数据库产品,Oracle和MySQL,在他们最新的版本中都完全支持UTF-8和UTF-16编码。如果你有机会创建你应用的数据库shemale,认真考虑下把UTF-8作为你数据库的字符编码。如果你的数据使用了一个不同、很有限的字符编码,且你已经经历了前面所描述的数据丢失,你应该考虑迁到UTF-8编码了。

总结 Summary
从浏览器到数据库的旅程有几个潜在的数据丢失点。文本数据通常经过了三次甚至更多的字符编码转换,每次转换都能引起不可逆的数据丢失。为了避免这个问题,遵循这些规则:
  • 总是在你的HTML或JSP页面中通过使用HTML<mata>标签或JSP@page提供字符编码信息。这个信息告诉浏览器如何解析页面的文本。
  • 使用一种能够处理所有你的多语言和写脚本需要的HTML字符编码。UTF-8一个适应多种情景的好选择,因为它能表示一个范围很广的脚本。
  • 告诉web服务器当处理请求参数时选择什么字符编码。可通过使用servlet初始化参数获知context参数来实现它。
  • 为你的数据库选择一种你应用中任何编码的超集,比如UTF-8或者UTF-16。
  • 大小: 24.2 KB
  • 大小: 47.5 KB
  • 大小: 23.1 KB
  • 大小: 22 KB
  • 大小: 31.7 KB
  • 大小: 28 KB
  • 大小: 26.9 KB
  • 大小: 53.8 KB
  • 大小: 20.2 KB
  • 大小: 21.2 KB
分享到:
评论
1 楼 wangflood 2011-05-04  
牛B。太牛B了。

相关推荐

    图片转base64保存到数据库 , 并回显到浏览器

    `imgdemo`可能是一个示例程序或者一个包含处理图片的测试脚本,用于演示如何将图片转换为Base64,存储到数据库,然后从数据库中取出并回显到浏览器。 7. **安全性与性能考虑**: 尽管Base64编码方便了图片的存储...

    工具-字符编码转换

    在IT领域,字符编码转换是一项基础且重要的任务,特别是在处理多语言或跨平台的数据时。本文将基于“工具-字符编码转换”的主题,深入探讨字符编码的概念、常见编码格式、字符编码转换工具以及如何进行实际操作。 ...

    java字符串编码转换

    编码转换的过程通常是将一种编码格式的字符串转换为另一种编码格式的字符串。在Java中,可以通过 `String` 类的构造函数或者 `getBytes` 方法实现。 例如,将一个GBK编码的字符串转换为UTF-8编码: ```java String...

    多功能数据库浏览器

    至于压缩包中的"Usp10.dll"文件,这是Unicode支持的相关动态链接库,常常用于处理Unicode字符集,确保“多功能数据库浏览器”在处理多语言数据时的正确性和兼容性。而"多功能数据库浏览器.exe"则是软件的可执行文件...

    字符编码转换iconv

    字符编码转换是计算机科学中的一个重要概念,涉及到数据的存储、传输和显示。在处理不同语言或平台时,正确地进行编码转换是确保信息准确无误的关键。`iconv` 是一个广泛使用的命令行工具和库,它允许在不同的字符...

    UTF8字符串转换工具绿色版

    UTF8字符串转换工具是一款便捷实用的软件,专为处理各种编码格式之间的字符串转换问题而设计。在信息技术领域,字符编码是至关重要的,因为它决定了计算机如何存储、处理和显示文本。UTF8作为最广泛使用的Unicode...

    VC++源码—数据库浏览器

    2. **数据库连接**:要实现数据库浏览器,首先需要连接到数据库。VC++通常使用ODBC(Open Database Connectivity)或ADO(ActiveX Data Objects)来建立数据库连接。ODBC是标准的数据库访问接口,而ADO是Microsoft的...

    有关连接SQL数据库和转换网页提交汉字的类

    标题中的“有关连接SQL数据库和转换网页提交汉字的类”涉及到两个主要的IT知识点:数据库连接和字符编码处理。在Java编程中,这两个主题是非常关键的,特别是在开发Web应用程序时。 1. 数据库连接: 在Java中,...

    ASP实现excel文件到数据库文件的转换

    在ASP(Active Server Pages)环境下,将Excel文件转换为数据库文件是常见的数据迁移或整合任务。...通过ASP进行Excel到数据库的转换,能够方便地实现数据迁移和管理,但需谨慎处理数据的完整性和一致性问题。

    JSP存到数据库乱码解决办法

    当我们将中文等非ASCII字符的数据存储到数据库时,经常会遇到乱码的情况。这主要是因为不同系统、组件之间默认使用的字符集不一致所导致的。本文将详细介绍如何通过配置`CharacterEncodingFilter`来解决JSP存取数据...

    字符转换类

    在JSP(Java Server Pages)中,数据从页面传递到数据库的过程中,可能会遇到乱码问题。这通常是因为在不同的环节中,字符编码不一致导致的。以下是一些相关的知识点: 1. **字符编码**: 字符编码是用来将字符转换...

    niceware在Node和浏览器中生成或转换随机字节成密码

    2. **字节到单词的转换**:利用预先定义的8位字节到单词的映射表,Niceware将每个字节转换为一个对应的单词。映射表确保了每个字节都有一个独一无二的单词对应,这样可以保证生成密码的唯一性。 3. **单词到字节的...

    分享php代码将360浏览器导出的favdb的sqlite数据库文件转换为html

    这个过程不仅适用于360浏览器,只要数据结构相同,也可以应用于其他浏览器的书签转换。不过,需要注意的是,不同浏览器的书签数据结构可能会有所不同,因此在实际应用时,可能需要根据具体的数据格式进行调整。

    字符转换工具

    3. 转换过程:一旦编码确定,工具会将每个字符转换成相应的目标编码表示。这可能涉及到对原始字节序列的重新解释。 4. 输出结果:转换后的字符被写入新文件或显示在界面上,供用户查看或进一步处理。 在实际应用中...

    论文研究-异构数据库迁移的设计和实现.pdf

    数据库转换技巧涉及到数据类型转换、数据完整性保持以及数据迁移过程中的各种约束处理。数据转换规则指的是在不同数据库系统之间进行数据迁移时,针对不同的数据类型和数据表达方式所制定的映射规则。这些规则决定了...

    aspnet(C#)实现从sqlserver数据库中下载以二进制存储的图片

    当图片被上传到应用时,会被转换为二进制流并存储在对应的字段内。 接下来,让我们探讨C#代码如何与数据库交互来获取这些二进制数据: 1. **连接数据库**:使用ADO.NET或者Entity Framework等ORM工具,建立与SQL ...

    操作带格式的文本到数据库

    在IT领域,尤其是在软件开发中,经常需要处理各种格式的文本数据,并将其存储到数据库中。这个场景描述的“操作带格式的文本到数据库”是一个常见的需求,特别是在内容管理系统、文档存储系统或富文本编辑器的开发中...

    C#将图像添加到数据库

    在从数据库检索图像时,如果是二进制数据,我们需要读取该字段,然后写入到响应流中,以供浏览器显示。如果是URL引用,直接返回URL即可。 ```csharp // 二进制数据检索并显示 byte[] imageData = (byte[])reader[...

    网页编码转换工具GB2312转换UTF-8

    “GB2312转换UTF-8”工具提供了一种便捷的解决方案,能够将使用GB2312编码的文件转换为UTF-8编码,确保在各种设备和浏览器上都能正常显示。同时,工具也支持反向操作,即UTF-8转GB2312,以满足特定场景的需求。 ...

    c#中上传下载文件到数据库的源代码

    在C#编程中,将文件上传到数据库以及从数据库下载文件是常见的操作,尤其是在构建Web应用程序时。这个源代码示例可能包含一个完整的Web站点项目(WebSite4),用于演示如何处理这种数据存储。以下是对这些关键知识点...

Global site tag (gtag.js) - Google Analytics