深入分析Java Web技术内幕-3.深入分析Java Web中的中文编码问题

1 几种常见的编码格式

为什么要编码

在计算机中存储信息的最小单元是1个字节(8bit),所以能表示的字符范围是0-255个。人类要表达的字符太多,无法用1个字节完全表示。要解决这个问题需要使用新的数据结构char,从char到byte必须编码。

编码格式

ASCII码:共128个,用一个字节的低7位表示,0-31控制字符,32-126打印字符。

ISO-8859-1:拓展自ASCII码,覆盖大多数西欧语言字符,单字节编码,共能表示256个字节。

GB2312:双字节编码,包含6763个汉字。

GBK:拓展自GB2312,和GB2312兼容,能表示21003个汉字。

GB18030:可能单字节、双字节或四字节,应用不广泛。

UTF-16:具体定义了Unicode[统一码]字符在计算机中的存取方法。定长,使用两个字节表示任何字符。Java以UTF-16作为内存的字符存储格式。

UTF-8:采用变长技术,每个编码区域有不同的字码长度。中文一般占三个字节。

Java中的编码操作

I/O操作:

上一篇文章介绍I/O字符和字节转化的时候有提到两个类:StreamDecoderStreamEncoder,InputStreamReader在I/O过程中处理读取字节到字符的转换,其委托StreamDecoder实现字节到字符的节码实现,解码过程中必须由用户指定Charset编码格式,默认使用本地环境中的默认字符集(中文环境即为GBK)。
OutputStreamWriter负责转换字符到字节,编码格式和默认编码规则与解码一致。
示例如下:

@Test
    public void test(){
        String file = "C:/stream.txt";
        String charset = "utf-8";
        try {
            FileOutputStream outputStream = new FileOutputStream(file);
            OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset);
            try {
                writer.write("这是要保存的中文字符");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
               writer.close();
            }
            //读取字节转换成字符
            FileInputStream inputStream = new FileInputStream(file);
            InputStreamReader reader = new InputStreamReader(inputStream);
            StringBuffer buffer = new StringBuffer();
            char[] chars = new char[64];
            int count = 0;
            try {
                while((count = reader.read(chars)) != -1){
                    buffer.append(chars,0,count);
                }
                System.out.println(buffer.toString());
            }finally {
                reader.close();
            }

        } catch (IOException e) {
            System.out.println("文件不存在");
        }
    }

内存操作

String s = "这是一段中文字符串";
byte[] b = s.getBytes("uft-8");
String n = new String(b,"uft-8");

另外Charset提供了encode和decode方法,代码如下:

String string = "中文字符串";
Charset charset = Charset.forName("UTF-8");
ByteBuffer byteBuffer = charset.encode(string);   
CharBuffer charBuffer = charset.decode(byteBuffer); 

在Java中如何编解码

String → ByteBuffer:Charset.encode()
ByteBuffer → String:Charset.decode().toString()
CharBuffer→ String :toString()
ByteBuffer → byte[]:array()
byte[] → ByteBuffer :ByteBuffer.wrap()
CharBuffer → char[]:array()
char[] → CharBuffer:CharBuffer.wrap()

Java Web中涉及的编解码

URL编解码

在这里插入图片描述
浏览器编码URL是将非ASCII字符按照某种编码格式编码成16进制数字后将每个16进制表示的字符前加上%。在这里插入图片描述
URL的URI部分进行解码的字符集是在<Connector URIEncodeing="UTF-8">里设置的。
QueryString的解析过程:GET方式HTTP请求的querystring和POST方式HTTP请求的表单参数都是作为parameters保存的,都通过request.getParameter();
queryString的解码字符集是在哪定义的呢?
是在HTTP的header的contentType或者是默认的ISO-8859-1,如果要用HTTP的header中中定义的编码需要设置<Connector URIEncoding="UTF-8" useBodyEncodingForURI='true'>

HTTP Hander的编解码

客户端发起的HTTP请求除了URL外,还可能会在Header中传递其他参数(如Cookie)。对Header中的项进行解码默认使用ISO-8859-1,且不能设置Header其他的解码格式,若设置的Header中有非ASCII字符,解码中肯定会出现乱码。若一定要传递,则调用Tomcat中的URLEncoder编码,再添加到Header中。

POST表单的编解码

POST表单的参数传递方式是通过HTTP的BODY传递到服务器的,当提交时先根据ContentType中的字符集进行解码,字符集编码可以由request.setCharacterEncoding(charset)来设置。
此外务必注意:对POST表单提交参数的解码是发生在getParameter时,所以在第一次调用request.getParameter方法之前就要先设置request.setCharacterEncoding(charset)方法。
关于上传的文件编码:也是使用ContentType定义的字符集编码,不过上传文件是以字节流的方式传输到服务器的本地临时目录,此过程尚不涉及字符编码,只有当文件内容添加到parameters时才进行编码。

HTTP BODY的编解码

编解码字符集通过response.setCharacterEncoding来设置,通过Header的Content-Type返回客户端。若Header中没有Content-Type,浏览器会根据中的charset来解码,若依然没有该属性,浏览器则使用默认编码。

常见问题分析

中文变成了看不懂的字符

解码和编码用的字符集不一样,就会出现乱码。

一个汉字变成一个问号

ISO-8859-1遇到不认识的用3F表示,是问号。

一个汉字变成两个问号

说明中文经过了多次编码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值