java中的编码问题

一直在试图搞清楚java中的编码问题,也看了网上的一些文章,但还是云里雾里。直到最近看了方立勋老师的web课程,才略略明白一点。

在此记录一下自己的理解,看看自己能不能说清楚。

 

第一个问题:我在java代码中定义了一个字符串,它是什么编码?

字符串实质是一个char数组。那么char的编码,其实就是字符串的编码。那么char什么编码呢?为什么'中'字转int类型后的值是20013呢?

        char c = '中';
        System.out.println(c); //
        System.out.println((int) c); // 20013

 

引入第一个概念:Unicode编码

Unicode是一种“编码”,所谓编码就是一个编号(数字)到字符的一种映射关系,就仅仅是一种一对一的映射而已。

java的String使用的编码是Unicode。

 

来个简单的例子证明:

找个转码工具,将'中'字转为Unicode编码,结果是'\u4e2d'。用'\u4e2d'替换原来的'中'字。打印的结果和'中'字是一样的。

        char c = '\u4e2d';
        System.out.println(c); //
        System.out.println((int) c); // 20013

 

'\u'是什么意思?

'\u'的意思就是使用了Unicode编码。后面加上十六进制代码来表示Unicode字符。下面这段代码可以验证。

        Integer num = Integer.valueOf("4e2d", 16);
        System.out.println(num.intValue()); // 20013

 

第二个问题:编码和编码格式的区别是什么?

这部分内容来自文章 java中的编码和编码格式问题 作者:风未馨

1. Unicode是一种“编码”,所谓编码就是一个编号(数字)到字符的一种映射关系,就仅仅是一种一对一的映射而已。

2. GBK、UTF-8是一种“编码格式”,是用来序列化或存储1中提到的那个“编号(数字)”的一种“格式”。

 

编码和编码格式

  java的String使用的编码是Unicode,当String存在于内存中时(在代码中用string类型的引用对它进行操作时),是"只有编码而没有编码格式的",所以java程序中的任何String对象,说它是gbk还是utf-8都是错的,String在内存中不需要“编码格式”, 它只是一个Unicode的字符串而已。

  当字符串需要在网络中传输或要被写入文件时,就需要使用编码格式了。乱码问题也因此出现。

 

GBK和UTF-8

  GBK和UTF-8都是用来序列化或存储Unicode编码的数据的,但是分别是2种不同的格式,他们都是Unicode编码的实现方式;他们俩除了格式不一样之外,他们所关心的Unicode编码范围也不一样。

  UTF-8考虑了很多种不同国家的字符,涵盖整个unicode码表,所以其存储一个字符的编码的时候,使用的字节长度也从1字节到4字节不等;

  而GBK只考虑中文——在Unicode中的一小部分的字符的编码,所以它算好了只要2个字节就能涵盖到绝大多数常用中文(2个字节能表示6w多种字符),所以它存储一个字符的时候,所用的字节长度是固定的;

 

ASCII码和Unicode

  ASCII码,和Unicode编码一样,也是一种"编码"。

  ASCII码的范围比较小,一共规定了128个字符的编码。

  Unicode编码是一个很大的集合,现在的规模可以容纳100多万个符号。就像它的名字都表示的,这是一种包含所有符号的编码。

  当然也有其他的编码,没用过,我也不甚了解。

 

第三个问题:哪些地方会用到编码格式?

  前文中提到过一句话:当字符串需要在网络中传输或要被写入文件时,就需要使用编码格式了。

 

网络中传输:

对于java web开发人员,指的就是java web了。那在java web中,哪些地方需要设置编码格式呢?

先上个图(网上随便拷贝来的)。假定浏览器就是ie,WEB服务器是tomcat,页面是jsp,应用服务器用的是servlet。

 

1.  浏览器中打开了一个页面,这个页面映射应用服务的某个jsp。此时浏览器中的解码格式是什么呢?

 浏览器的解码格式是在jsp中指定的,比如在jsp文件中经常可以看到这样两行代码。


<!-- 
  这一句是和Tomcat说的:保存在硬盘上的jsp文件在被Tomcat翻译成servlet的时候,使用utf-8解码jsp文件的内容。
  如果不指定,会默认使用iso-8859-1来解码。
-->
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 

<!-- 这一行是和浏览器说的:浏览器解码的时候请使用utf-8解码哦 -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

这样浏览在解析这个jsp的时候,就是使用utf-8编码来解码。

 

浏览器的解码格式也是可以在浏览器上设置的:

如果网页中出现了乱码,你换一种编码格式来解码,可能就不乱了。

 

2. response中也可以设置内容的编码格式和指定浏览器的解码格式。

开发人员应该都知道,jsp就是一个特殊一点的servlet。在servlet中, response有两个设置编码的方法,和jsp中的那两行编码配置有着相似的功能。

它们就是:

        // 表示response的内容会以utf-8的编码方式编码后发送给浏览器。
     response.setCharacterEncoding("UTF-8");

     // 告诉浏览器,解码的时候也要使用utf-8解码哦。 response.setContentType(
"text/html;charset=UTF-8");

 

3. 通过request对象可以指定应用服务用哪种编码格式来解码接收到的数据.

上面两种情况,都是讲servlet自己如何编码,然后告诉浏览器如何解码的。servlet也可以指定对接收到的对象使用哪种编码格式来解码。   

    // 通过这句话,可以指定用utf-8编码格式来解码浏览器传来的数据。不过只对post方式传来的数据有效。如果是get方法传来的数据,还是会以默认的iso-8859-1来解码。
    request.setCharacterEncoding("UTF-8");

 

4. 设置tomcat服务器配置文件server.xml,指定以何种编码解码浏览器传来的参数。

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF-8" />

'URIEncoding="UTF-8"', 这个属性的配置, 和"request.setCharacterEncoding("UTF-8");"这句代码的功能大致相同。都是指定tomcat服务器接受到收据后如何解码。

如果不指定,tomcat服务器会默认使用iso-8859-1来解码。

此处建议不要使用URIEncoding指定编码,因为一个tomcat服务器里可能会有多个应用,因为某一个应用修改tomcat配置可能会导致其他应用出错,编码转换在每个应用中单独处理即可。

 

被写入文件:

1. 文件内容在被写入文件时是被编码了的,默认的编码格式是gb2312(可能和系统有关,简体中文系统测试,就是gb2312)。

可以在代码中指定文件流在写入文件时用什么编码格式。比如指定用"utf-8".

Writer write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");

 

2. 文件本身不同软件是有选择性的支持可以对哪些编码进行解码。

比如:

Excel支持gb2312,不支持UTF-8。

Txt记事本支持UTF-8编码。

 

所以有时候,我下载一个csv文件(指定内容使用utf-8编码)。会遇到这样的情况:

这个csv文件在使用excel打开的时候,中文是乱码的,如果换成txt打开,中文就正常显示了。

 

这个时候如果想要在excel中不乱码,就需要在代码中指定内容使用gb2312编码。

也可以通过txt的'另存为',改变文本的编码格式。

 

3. 如何确定文件的编码呢?

我在win7(简体中文)系统中,右键->新建一个文本文档,nodepad++打开后,查看编码,可以看到编码是ANSI。

使用java代码生成一个txt文件,未明确指定编码格式,nodepad++打开后,查看编码,可以看到编码是utf-8。

 

Eclipse中做了一个小测试:

 test1. 指定utf-8编码格式

        String fullPath = "D:\\test2.txt";
        File file = new File(fullPath);
        if (!file.exists()) {
            file.createNewFile();
        }
        Writer write = new OutputStreamWriter(new FileOutputStream(file),
                "utf-8");
        write.write("你好");
        write.flush();
        write.close();

 生成的文件编码格式是:

 

 test2. 指定gb2312编码格式

        String fullPath = "D:\\test2.txt";
        File file = new File(fullPath);
        if (!file.exists()) {
            file.createNewFile();
        }
        Writer write = new OutputStreamWriter(new FileOutputStream(file),
                "gb2312");
        write.write("你好");
        write.flush();
        write.close();

  生成的文件编码格式是:

 

test3. 先使用utf-8生成文件,输出"你好",再使用gb2312,追加内容,输出"你好",结果是这样子的。

 

总结:

第一次使用utf-8编码格式输出你好,此时文件内容 第一个"你好" 是utf-8编码的,文件的解码方式也是utf-8.

第二次使用gb2312编码格式输出你好,此时的文件追加的内容 第二个"你好" 是gb2312编码的,文件的解码方式也变成了gb2312。

也就是说,Eclipse操作文本文件TXT时,txt文件会一直使用最后一次操作文件使用的编码格式来解码文件的内容。

所以第二次输出"你好"后,第一次输出的"你好"变成了乱码。

 

 

posted @ 2017-06-16 18:24  迷失之路  阅读(15033)  评论(1编辑  收藏  举报