Chinaunix首页 | 论坛 | 博客
  • 博客访问: 425502
  • 博文数量: 79
  • 博客积分: 2886
  • 博客等级: 少校
  • 技术积分: 968
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-16 10:33
文章分类

全部博文(79)

文章存档

2013年(7)

2012年(17)

2011年(28)

2010年(25)

2009年(1)

2008年(1)

我的朋友

分类: Java

2011-04-26 16:26:08

转载自 
最终编辑 
encoding编码问题之一Java文件

1.编辑*.java文件时发生了什么事情呢? 
答:当我们用记事本编辑时, 
   打开紫光输入法,紫光输入法软件控制了键盘,从键盘敲击的字母变成了拼音,拼音下方显示的是GBK中的简体还是繁体取决于指定的"简体""GBK",此时上方的"yanlei"就有了下方的汉字"眼泪"而不是"眼淚". 
   
   在空格录入"眼泪"以后,表征这两个字的键盘输入文件设备缓冲区内被存入了D1BBC0E1,这是由于紫光指定了"简体GBK"进而cpu告知系统IO设备管理器得知输入的字符是简体GBK,因此I操作接收了紫光送来的D1BBC0E1进入缓冲区.键盘文件录入设备有编码集"简体GBK" 
   
   由于并行处理的机制,完成这个输入动作后,cpu得到输入IO管理器的I输入完成中断指令,cpu利马把缓冲区的内容往内存处理区中送,这个送的过程伴随着一个码转换函数的调用,就是GBK码D1BBC0E1往Unicode内存处理码FFFE3C77EA6C的转换,当cpu把缓冲区D1BBC0E1送至处理区FFFE3C77EA6C完毕,发现O操作线程提出了需要显示的处理,紧接着又调用码转换函数Unicode内存处理码FFFE3C77EA6C往屏显缓冲区GBK码D1BBC0E1的转换动作,当转换完毕,开始调度IO设备让它进行O显示操作(这里的内存处理码的转换对用户来说是透明的,用户完全可以理解"入缓冲-处理区-出缓冲"是同一个码副本) 
   
   IO设备得到cpu通知说可以取出屏显缓冲区内容进行显示了,IO取出D1BBC0E1对照字体映射字符点阵符号表进行展示"眼泪",取出D1BBC0E1这些01代码时到底按照哪个编码进行展示呢?这时屏显文件输出设备利用了系统Locale代码页也就是ANSI936 GBK代码页就行展示,根据不同的字体显示同一个中文字符的不同字符点阵符号。所以这里屏显设备也参考了编码集才能进行正确显示 
   键盘入缓冲GBK->内存处理unicode->屏显出缓冲GBK的一个转换(文件保存缓冲采用GBK码的一个副本), 是透明的


2.编辑好的*.java文件保存时发生了哪些事情呢? 
答:编辑好的java源文件可以采用默认的GBK即ANSI936来保存文件,至于各种保存方案为什么存在以及各自的优缺点在上方的【源文件文件保存:】这里有介绍。所以在此处我们也就理解了eclipse工作空间workspace设定编码方案类似于另存为弹出窗口编码下拉框的指定 
   
   键盘入缓冲GBK->内存处理unicode->文件保存缓冲GBK的一个转换,是透明的 
       文件保存缓冲UTF-8的一个转换,是透明的 
       文件保存缓冲unicode的一个转换,是透明的 

3.编译java源文件的时候发生了什么呢? 
答:javac命令 javac -file.encoding xxx.java通过这个指令我们知道-file.encoding这个属性是有作用的 
     -file.encoding告诉JVM虚拟机是用什么编码方式保存文件的, 
     pageEncoding 
     setContentType() 
     setCharacterEncoding() 
   
   3.1 当我们保存java源程序文件为GBK即ANSI代码页时,取出编译时javac默认-file.encoding取的当前系统的默认文件编码方案,所以取出文件缓冲池内的"眼泪"D1BBC0E1会被正确的转换到内存处理区FFFE3C77EA6C,处理完了之后待生成*.class文件保存的时候又把内存处理区FFFE3C77EA6C转换成了UTF-8格式EFBBBFE79CBCE6B3AA保存在了*.class文件之中,因为*.class文件默认使用UTF-8保存。 
   
   动作:[GBK录入->unicode->GBK码文件缓冲(编辑时入)]====源文件怎么保存来的 
                           ||||||||||||||||||||||| 
                           GBK码文件缓冲(编译时出)->unicode->UTF-8码class文件保存 
     
     可以看出取出文件时,文件编码被对待成保存成的编码是正确的 
   3.2 当我们保存java源程序文件为UTF-8时(动作:GBK码D1BBC0E1->处理区FFFE3C77EA6C->UTF-8码EFBBBFE79CBCE6B3AA),取出编译时javac默认-file.encoding取的当前系统的默认文件编码GBK方案系统Locale语言指定,所以此处我们就需要进行源文件编码的指定. 
   3.2.1 如果我们没有重新指定的话:UTF-8格式EFBBBFE79CBCE6B3AA文件读出流的"眼泪"就会被当作是GBK编码E79C,BCE6,B3AA,造成错误,进行了GBK文件读出流->unicode处理->UTF-8码class文件保存的过程,在这个过程中GBK文件读出流发生了错误,导致以后的整个过程的错误. 
   
   动作:[GBK录入->unicode->utf-8码文件缓冲(编辑时入)]====源文件怎么保存来的 
                                   ||||||||||||||||||||||| 
                                   GBK码文件缓冲(编译时出)->unicode->UTF-8码class文件保存 
     
     可以看出取出文件时,文件编码被对待成GBK编码是错误的 
   3.2.2 如果我们指定了-file.encoding=utf-8,这时编译器就会知道源码是utf-8编码. 
   
   动作:[GBK录入->unicode->utf-8码文件缓冲(编辑时入)]====源文件怎么保存来的 
                               ||||||||||||||||||||||| 
                               utf-8码文件缓冲(编译时出)->unicode->UTF-8码class文件保存 
     
     可以看出取出文件时,文件编码被对待成保存时的utf-8编码是正确的 
4.执行.class文件的时候发生了什么呢? 
当JVM执行.class文件时,jvm直接将保存的utf-8格式的class文件拿出来执行,进行 
utf-8码class文件->内存处理unicode->处理后屏显GBK码,追加以前的从编辑开始的动作,如下: 
[GBK录入->unicode->utf-8码文件缓冲(编辑时入)]====java源文件怎么保存的 
             ||||||||||||||||||||||| 
             utf-8码文件缓冲(编译时出)->unicode->UTF-8码class类文件怎么编译保存的 
               |||||||||||||||| 
               UTF-8码class文件->unicode->GBK码屏显 class类文件怎么执行 
   
   好:通过以上三个环节之后,我们就知道jvm虚拟机处理编码工作的原理, 
       在这里需要强调一下, 
     unicode->UTF-8码class类文件怎么编译保存的 
       |||||||||||||||| 
       UTF-8码class文件读出->unicode 
       这个类文件的细节编码是我们不需要关心的,因为类文件的编码解码都是由JVM自动实现的,不需要本地操作系统file.encoding属性的参与的,对用户来说是透明的,所以我们可以不必考虑这层码的转换. 
       那么:上诉动作可以简化为: 
       GBK录入->unicode->utf-8码文件缓冲(编辑时入)]====java源文件怎么保存的 
           ||||||||||||||||||||||| 
                         utf-8码文件缓冲(编译时出)->unicode->->GBK屏显 class类文件怎么执行 
       其中GBK录入和GBK屏显都是根据系统Locale ANSI936来决定的 
       简化了之后我们可以看出文件的保存时编码和取出时编码是至关重要的,这里如果发生了错误,尤其是取出时编码的误认为-file.encoding=系统Locale是致命的。 
       
   针对普通Java类: 
   一句话总结:针对静态字符串文件以什么码保存就必需以什么码取出编译javac -file.encoding="文件保存码" 
             针对动态字符串,如从数据库数据文件存取内容时,数据文件什么码,rs.getString().getBytes("数据文件编码")   取出时new String(rs.getString().getBytes("数据文件编码"),"java文件保存码") 
     存入时new String(str.getBytes("java文件保存码"),"数据文件编码"); 
       
这里所设计都是普通的java类,那么jsp和servlet又是怎样的呢

encoding编码问题之二Jsp文件

5.jsp文件的编辑,上传,编译成servlet,网络传输发生了什么呢? 

【编辑】jsp文件windows操作开发保存时默认是按照GBK编码字符串的,可以指定保存成UTF-8等其它格式。假设以UTF-8格式保存 
编辑动作:GBK录入->unicode->UTF-8保存jsp文件 

【上传】此时UTF-8码保存的jsp文件被转换成了UNIX系统下的iso-8859-1编码         
上传动作:UTF-8保存jsp文件->unicode->iso-8859-1保存jsp文件==========(有待考究,也许没有文件编码的转换) 

【编译】[tomcat举例]jvm读出iso-8859-1保存jsp文件进入内存,不作任何格式的修改,交给jasper告诉它我读出好了而且文件编码格式iso-8859-1,jasper会根据指定的pageEncoding属性来决定响应内容的编码格式,当没有设置此属性时,翻译之后的_jsp.java文件为response.setContentType("text/html"),内容的编码jasper采取了默HTTP协议的传输编码格式iso-8859-1,所以我们在GBK屏显环境下看到的是乱码,(因为iso-8859-1直接高位补0进入内存处理区,结果导致两个汉字会看到四个iso-8859-1字符,gbk包含latin-1字符点阵,而进行输出缓冲待传输时又进行了简单的去高位0操作,准备传输的是默认的latin-1编码字节流)那为什么客户端反而可以看到正常的中文字符呢? 

编译动作:iso-8859-1保存jsp文件->unicode->网卡出缓冲码流(取决于pageEncoding)如果则没有默认latin1 
如:"眼泪"D1BBC0E1默认iso-8859-1字节四个->unicode补高位0x00D10,x00BB,0x00C0,0x00E1我们看到的乱码   ->iso-8859-1字节缓冲输出又还原到D1BBC0E1 

【网络传输】经过上面的示例之后,IE端得到iso-8859-1码因为没捕获到HTTP响应头"Content-Type"也没有捕获到属性,得到的D1BBC0E1字节流会按照IE自身支持的字符集(系统Locale)来加以显示,所以反而会两两字节重组后得到正确的"眼泪"二字 

IE端解释动作:iso-8859-1网卡出缓冲编码字节流->unicode->屏显GBK码 

============================================== 
==============================================       
总体发生动作: 
编辑动作:GBK录入->unicode->UTF-8保存jsp文件================================1 
上传动作:UTF-8保存jsp文件->unicode->iso-8859-1保存jsp文件==================2(上传换码否?) 
编译动作:iso-8859-1保存jsp文件->unicode->iso-8859-1默认/pageEncoding属性===3 
传输动作:iso-8859-1默认/pageEncoding属性->unicode->屏显GBK码===============4 
     
观察后不难发现: 
1,2环节不会有任何错误;3,4环节也不会有任何错误. 
2环节后半部分、3环节上半部分是否发生文件上传转码并无影响, 
     因为->unicode->iso-8859-1码保存jsp文件==2 
         iso-8859-1码保存jsp文件->unicode==3 构成闭环,在这里不会发生任何错误。 

关键点:在jasper处理2,3环节的地方,文件是以utf-8保存的,可如果没有pageEncoding='utf-8'的指定,就会导致输出码流不再是保存时的utf-8码流传给了IE端,IE端就会按照这个错误的pageEncoding属性指定的编码进行解码重组,而出现错误。 
         
点评: 
编译动作是个关键点:它在取出jsp源文件的时候,一定要和jsp源文件保存时的编码一致,否则就会出错误. 
这一点由pageEncoding="文件保存码"="Eclipse开发工具workspace工作区编码"来保证的。 

jsp静态汉字显示关键点就是: 
1。pageEncoding=""只要设置,一定要和保存jsp文件时编码一致。 
2。pageEncoding=""没有设置,注意属性,要和保存jsp文件时编码一致。 
3。如果既没有pageEncoding="",也没有属性, 那么是默认的iso-8859-1对于Tomcat进行jasper编译的时候来说。而对于webSphere来说,一定会默认的在jsp或者servlet传输时默认指定content-type头部为iso-8859-1,所以这里的页面不在有任何的效果 

以上分析了jsp页面中不读取动态字符串,静态字符串的展示原理,那么针对访问数据库数据文件的动态字符串的生成又是什么样子呢?这个可以参考上方的【数据库数据文件读取:】来理解。只是需要注意,从数据库取出的动态字符串必需和jsp文件保存时[既是pageEncoding=""]的编码一致, 
如:pageEncoding="utf-8",那么new String(rs.getString().getBytes("iso-8859-1"),"utf-8"); 

jsp总结: 
pageEncoding属性告之IE浏览器什么编码方式保存的静态字符串,IE好根据此来逆向解释往->unicode内存码转换 
pageEncoding = "jsp文件保存码" = "Eclipse开发环境workspace工作区编码" 
new String(rs.getString().getBytes("数据文件码"),"jsp文件保存码"); 
tomcat服务器在jasper解释开始解释jsp源文件的时候,是根据pageEncoding属性指定的码集进行网卡缓冲待输出字节流.如果未指定,则按照默认的iso-8859-1码流输出
encoding编码问题之三Servlet文件
6.servlet文件的编辑MyServlet.java,上传MyServlet.class,网络传输发生了什么呢? 
jsp本质上来说就是servlet 

【编辑】当我们在windows环境本地编辑MyServlet.java文件时,采用的内容编码方案默认是系统Locale ANSI936即GBK编码录入, 
此时屏显有动作:GBK录入->unicode->GBK屏显 
    保存有动作:GBK录入->unicode->指定编码方案保存.这里举例用UTF-8保存 

【编译】 
当我们本地编译servlet时,javac -file.encoding="文件保存码"|"系统Locale默认GBK码" MyServlet.java时,这里需要确保-file.encoding="文件保存码", 
[这一点是开发工具保证的,"Eclipse开发环境workspace工作区编码"="Myservlet.java源文件编码",当取出编译时Eclipse保证了-file.encoding="文件保存码"],编译好之后的*.class文件保存为UTF-8编码 

【上传】当/WEB-INF/classes/*.class类文件传至任何操作系统之上时仍然是UTF-8编码。 

【执行】jvm虚拟机解读classes文件,->unicode->setContentType("text/html;charset='文件保存码'")传输至IE端。如果无setContentType("uft-8")方法时,Tomcat默认准备iso-8859-1输出流传至IE端,此时发生了错误,因为原先的UTF-8码被认为成了iso-8859-1码流 

servlet总结: 
setContentType("文件保存码") = "Servlet文件保存码" = "Eclipse开发环境workspace工作区编码" 
new String(rs.getString().getBytes("数据文件码"),"Servlet文件保存码"); 

总结jsp,servlet: 
规则◎:pageEncoding = setContentType("文件保存码") = "文件保存码" = "Eclipse/workspace区编码" 
规则◎◎:new String(rs.getString().getBytes("数据文件码"),"文件保存码"); 

以上总结了服务器端jsp文件/servlet文件本身含有的静态字符串的编码处理---规则◎: 
         服务器端jsp文件/servlet文件本身获取数据库动态字符串的编码处理---规则◎◎: 

那么str = request.getParameter()从IE端获取的请求信息来的字符串到底是什么编码呢? 
答: 
IE浏览器读取系统Locale指定的ANSI代码页,通过HTTP请求头Accept-Language,并上传给Web服务器供参考。但服务器一般都忽略HTTP请求头Accept-Language,虽然有些应用程序通过读这个HTTP请求头(Accept-Language)来实现一定的国际化。 

=================
post方法时:(推荐) 
针对
请求而言 

1.IE端表单文本框中的字符串GBK录入->unicode->网卡请求字节码流(注意这里的码流是系统默认的,不同的浏览器可能传输在http协议的码流因己而异,不过不用担心), 

2.服务器端解读到网卡入流,进入内存处理unicode,然后会往指定的setCharacterEncoding()方法指定的码流进行转换,【【否则往默认的iso-8859-1码流转换,因为HTTP协议默认传输的码流就是latin-1码流,导致返回码中和原先静态字符串码不统一而被浏览器误认为是统一的,进行统一转换导致错误】】,有动作:网卡请求字节码流->unicode->目标码流其实本质上,把从网卡输入流获得而来的往setCharacterEncoding()指定的码流转换,可以看作是目标流里动态追加字符串信息,这一点是和pageEncoding=setContentType()正好是完全一致的 

3.最后目标码流夹带本身文件的静态字符串编码(文件保存码:GBK录入->unicode->文件保码[pageEncoding,setContentType]) 

访问数据文件码(new String(rs.getString().getBytes('数据文件码'),'文件保存码') 
获取请求信息码(未知网卡输入码->unicode->文件保存码[setCharacterEncoding]) 

所以最终目标里面只有一种编码,就是原始jsp/servlet文件保存码, 

送回响应至浏览器时,浏览器获致该编码,然后统一进行 
文件保存码->unicode->GBK码屏显,当然是能保证所有汉字都是正确显示的。

阅读(3127) | 评论(0) | 转发(0) |
0

上一篇:Android开发准备

下一篇:oracle sequences扫盲

给主人留下些什么吧!~~