中文编码问题详解

来源:互联网 发布:交通大数据 编辑:程序博客网 时间:2024/05/17 22:41
一.常见的编码
ASCII,ISO-8859-1,GB2312,GNBK,UTF-8,UTF-16等
编码格式表示个数所需字节数说明ASCII128单字节的低七位表示0~31为控制字符如回车换行等;32~126为打印字符,可键盘输入能够显示出来ISO-8859-1表示256个字符单字节扩展ASCII码,ISO8859-1到ISO8859-15,ISO8859-涵盖大多数西欧语言字符,应用最广泛。GB23127445双字节A1~A9是符号区,682个;B0~F7是汉字区,共6763个汉字。GBK23940双字节扩展自GB2312,支持更多汉字,范围从8140~FEFE(去掉XX7F),能表示21003汉字,兼容GB2312。GBK18030兼容GB2312应用不广泛应用不广泛UTF-16处理Unicode编码双字节用2字节表示Unicode的转化格式,任何字符都通过2个字节表示,定长表示,效率快,java以UTF-16内存的字符存储格式。适合在本地磁盘和内存之间使用,可以达到字符和字节快速切换。UTF-8处理unicode编码变长每个编码区域不同字码长度,不同类型字符可以由1~6个字节组成,节省空间,效率不如utf-16,介于gbk和uft-16之间,适合网络传输,对ASCII码单字节存储,单字符损坏不影响后面字符。其通过首字节的前2位确定需要几个字节表示。
说明:unicode是统一码,ISO创建的全新的超语言字典,所有语言都可以通过这个字典相互翻译。
二.java中需要编码的场景
1.磁盘I/O操作中存在的编码
Reader类是java中读取字符的父类
InputStream是读取字节的父类
InputStreamReader类是关联字节到字符的桥梁,它负责在I/O中处理字节到字符的转换
具体字节到字符的解码实现它委托StreamDecoder类去做,在解码过程中必须由用户指定通过Charset指定编码格式,如果不指定会使用系统自带的编码格式.
Writer,OutputStream,OutputStreamWriter类的情况类似.
例如
String file = "D:/a.txt";String charset = "UTF-8";//写字符转化为字节流FileOutputStream outputStream = new FileOutputStream(file);//创建OutputStreamWriter对象OutputStreamWriter writer = new OutputStreamWriter(outputStream,charset);writer.write("这是内容");writer.close();//读取字节转化为字符FileInputStream inputStream = new FileInputStream(file);//创建InputStreamReaderInputStreamReader reader = new InputStreamReader(inputStream,charset);//创建拼接字符串StringBuffer stringBuffer = new StringBuffer();char[] buf = new char[64];int count = 0;while ((count = reader.read(buf)) != -1){stringBuffer.append(stringBuffer,0,count);}reader.close();


2.内存操作中编码
2.1 String提供了转换字节的方法
String s = "内容";//字符串转换为字符数组buye[] b = s.getBytes("UTF-8);//字符数组转换为字符串String n = new String(b,"UTF-8");
2.2 Charset类
//规定编码Charset charset = Charset.forName("UTF-8");//字符转换为字节ByteBuffer buf = charset.encode("内容");//字节转换为字符CharBuffer buf2 = charset.decode(buf);
2.3 ByteBuffer类
ByteBuffer的用法:
//字符转换为字节//创建一个容量为256字节的ByteBufferByteBuffer buf = ByteBuffer.allocate(1024);ByteBuffer buf1 = buf.putChar(c);

三.几种编码格式比较
1.GB2312与GBK
GBK是GB2312的升级版,GBK可以处理所有的汉字字符,而GB2312包含的中文字符不全,所以GB2312和GBK去比较,应该选择GBK
2.UTF-16和UTF-8
UTF-16编码效率最高,但不适合网络之间传输,占用空间也比较大,因为全部编码为双字节
UTF-8编码效率处在UTF-16和GBK之间,适合网络传输数据,是理想的中文编码方式
四.java web中需要编码的场景
1.网络I/O操作中存在的编码
1.1 数据经过网络传输都是以字节为单位的,则所有的数据都必须能够被序列化字节.
1.2 用户的一个HTTP请求,所需要编码的地方:URL,Cookie,Paramiter.
1.3 URL编解码:引用大佬的博客,写的非常详细
http://www.cnblogs.com/liuhongfeng/p/5006341.html
1.4 HTTP Header编码
在Header中传递的参数包含Cookie,redirectPath等.
如果在Header中传递非ASCII字符时,需要将这些字符用org.apache.catalina.util.URLEncoder编码才可.
1.5 POST表单的编解码
POST表单提交的参数的解码在第一次调用request.getParameter时发生,POST表单参数通过http的body传递到服务端。
整个流程是点提交时,浏览器根据contenttype的charset对表单参数编码,提交到服务端,服务端同样用contenttype中的字符集进行解码,所以post表单的参数一般不会乱码。
通过request.setCharacterEncoding(charset)可以设置。
注意:要在第一次调用request.getParameter方法之前设置request.setCharacterEncoding(charset),否则POST表单提交的数据可能出现乱码。
1.6 HTTP BODY编解码
服务端返回的结果经过编码返回浏览器,浏览器解码,然后显示。
编码可以通过response.setCharacterEncoding设置,会覆盖request.setCharacterEncoding的值,通过Header的content-type返回给客户端。
浏览器首先根据Content-type解码,无则根据HTML的来解码,无则使用浏览器默认编码解码。
2.JS中的编码问题
2.1 js文件编解码
<html>
<head>
<script src="xxx/a.js" charset="gbk"/>
引入的js文件若有中文,和本html页面的编码若不一致则会乱码,可以手动指定编码格式。
2.2 js的url编解码
js中发起ajax请求的url默认编码受浏览器不同影响,可使用encodeURI()、encodeURIComponent()几个函数。
encodeURL():可以将整个URL中的字符进行UTF-8编码,在背个码值之前添加"%"
注意:java中的URLEncoder、URLDecoder和js的encodeURIComponent对应。
2.3 xml文件的编解码
xml文件开始设定encoding
2.4 velocity模板设置编码
services.VelocityService.input.encoding=UTF-8
2.5 jsp设置编码
jsp页面<%@page >里面设置charset
五.编码的常见问题
1.中文变成了看不懂的字符
因为字符串解码时使用的字符集和编码字符集使用不一致所导致的.将字符集使用一致即可
2.中文变成了问号,一个中文变为一个问号
因为该字符串经过了不支持中文的ISO-8859-1编码后所出现的问题.换为GBK或者UTF-8即可
3.中文变成了问号,一个中文变为两个问号
这种情况比较复杂,中文经过了多次编码才会出现,需要检查中间的编码环节才可.
4.使用request.getParameter(name);出现乱码
因为配置文件中将useBodyEncodingForURL配置项没有设置为true,从而造成第一次解析用ISO-8859-1来解析.设置为true即可.



--本博文为博主在学习《深入分析java web 技术内幕》一书时所写。这本书不错,推荐给大家。
--本博文书写借鉴了博友的博客,在此表示感谢.