关于Java中的编码

以下为我自己的学习总结(部分是网络上资源),并不能确定百分之百正确,请读者自己多思考验证。我会及时更新笔记。如您发现错误,请帮忙指出,谢谢。

------------------------------------------------------------------------------------------------------------------------

解码:是指解码指定的 byte 数组,构造一个String。
比如:

对于字节数组bytes,按平台默认字符集解码:比如按gbk解码,则就是将bytes数组中的的字节值,按gbk编码表翻译成文字。

编码:就是将字符串按指定字符集,转换成字节形式。【注意编码这个过程一般我们是不直接操作的,我们选择这个文件的编码格式,点“保存”后就以这种编码存储了(有个encode类好像进行编码的。。)。getBytes(charset)方法只是按指定字符集获取字符串对应编码的字节数组
总结:解码就是将字节数组按指定字符集转换成字符串,而编码就是将字符串按指定字符集转换成字节数组。
解码:字节数组----->字符串
编码:字符串-------->字符数组

我们在文本编辑器中输入汉字、英文,OK,没问题,但是你保存的时候,如果你输入的是中文,你选择ISO-8859-1编码保存就会报错,因为ISO-8859-1编码不支持中文,无法根据ISO-8859-1表将中文编码成字节数组,出错的现象就是提示你无法保存;如果你用utf-8保存了,OK,utf-8支持中文,如果你以gbk编码保存,也OK,因为gbk也支持中文。
这个文件原先是UTF-8编码(即每个字符对应的字节值是按UTF-8编码表生成的),里面的中文显示OK,但是你又右键选择文件编码格式为ISO-8859-1(即以ISO-8859-1解码字节数组显示),则中文就变成乱码了(因为ISO-8859-1不支持中文编码),你再把文件改成UTF-8编码,又会显示OK。如果你选择编码格式是ISO-8859-1,又输入了中文,则会提示不能保存的,因为ISO-8859-1无法对中文进行编码。

所有的文本编辑器都有一个最基本的功能就是对你输入的字符串进行编码与解码!比如你输入“中华人民共和国”以UTF-8编码存放,然后又以gbk字符集解码,则显示就会出现乱码!在notepad中你new一个文本,输入一行中文,然后以UTF-8保存,OK,然后你又选择编码字符集是gbk(即以gbk字符集解码字节数组并显示),则肯定会乱码。在eclipse中也一样,一个类文件,你选择UTF-8保存,里面中文显示OK,你在该文件上右键选择属性,把字符集选为gbk,则显示肯定会出问题,你再选择会UTF-8就OK了。一个文本文件,你输入内容比如“中华人民共和国”点保存后,这个文本在硬盘上就是以某种字符集生成的字节数组保存,这个字节数组的值是不会变的,你再用另一种字符集编码打开,肯定会出现乱码,你又输入了中文比如还是“中华人民共和国并以新的字符集编码保存,原先的“中华人民共和国”对应的字节数组是没变的,只不过以新的字符集编码显示时出错了!(比如一个文本文件,你输入“中华人民共和国”以UTF-8保存,然后选择编码格式为gbk,则原先的“中华人民共和国”会显示出现乱码,你再输入“中华人民共和国”,以gbk保存,则第二次输入的“中华人民共和国”显示OK,第一次输入的显示乱码,你再选择编码格式为big-5什么的,上两次输入的“中华人民共和国”都会显示乱码,这次输入的显示OK。你一共输入了三次“中华人民共和国”,在硬盘上分别是以三种编码方式存储成字节数组的!!)--------->感觉自己好啰嗦,其实我想说的就一点: 在eclipse中点击文件右键属性,修改编码格式,或notepad中,选择新编码格式,这个操作的意思是,将原来硬盘上的文件以修改后的编码格式解码显示,并没有改变原来文字在硬盘上的字节值。选择新的编码方式后,你新输入的内容,会以新的编码格式存放。----->还是啰嗦,其实我想说的就一句话:对于一个已经存在的文件,你选择什么编码格式打开(即显示),并不会改变该文件原先在硬盘上的字节值,但是你新输入的内容。就会按新的编码格式存储。


UTF-8编码下的“中华人民共和国”改成gbk存放后就成了:涓崕浜烘皯鍏卞拰鍥?

我的源文件是以UTF-8编码存放的,有下面一句:

当我把这个UTF-8编码的源文件用jdk自带的javac命令编译时,出现以下 经典错误
出现这个错误的原因应该是,这个文件是UTF-8编码的(这个文件的字节值也是UTF-8编码表对应的值),而jdk使用系统默认的字符集(Windows下默认字符集是gb2312)解码编码,所以读取该源文件时对于UTF-8对应的字节值无法识别,无法转换成正常的文字,像上面的错误提示,就是将UTF-8编码的字节值按gb2312解码后出现了乱码,且字符串最后没有已引号结束,导致的错误。
看下面的程序:

好像UTF-8对中文的编码并不都是一个汉字3个字节,也有4个字节的。。。
---------------------------------------------------------------------------

JAVA源文件-->JAVAC-->Class-->Java-->getBytes()-->new String()-->显示的过程中,每一步都有编码的转换过程,这个过程总是存在的,只是有的时候用默认的参数进行。

      1.  JAVAC是以系统默认编码读入源文件,然后按UNICODE进行编码的。可以通过指定编码方式改变Javac读入源文件的编码方式。

 

  1. javac -encoding GBK Test.java  

 

      2. 在JAVA运行的时候,JAVA也是采用UNICODE编码的,并且默认输入和输出的都是操作系统的默认编码。

      也就是说在new String(bytes[,encode])中,系统认为输入的是编码为encode的字节流,换句话说,如果按encode来翻译bytes才能得到正确的结果,这个结果最后要在JAVA中保存,它还是要从这个encode转换成Unicode,也就是说有bytes-->encode字符-->Unicode字符的转换;而在String.getBytes([encode])中,系统要做一个Unicode字符-->encode字符-->bytes的转换。【巨重要】(我们在Java程序中new一个String,参数是字节数组,则过程是先将字节数组按系统默认编码字符集GBK转换成GBK格式的字节数组,再转成Unicode编码的字节数组,JVM中都是Unicode编码的字符串;而我们对一个String字符串用getBytes()方法获取其字节数组,是先把该字符串的Unicode编码的字节数组转换成系统默认的GBK编码的字节数组)【巨重要】

即:我们用String类的构造函数生成一个字符串,参数是字节数组,过程是:字节数组--->GBK字节数组--->Unicode字节数组

       我们把一个String变量用getBytes( )方法得到字节数组,过程是:该字符串的Unicode字节数组--->GBK字节数组

--------------------------------------------------------------------------------------------------

上面这一段文字巨重要,以下是我的笔记:

我们在文本编辑器(如eclipse中)写的Java代码,我们可以指定这个源文件在硬盘上存储时的编码格式,比如GBK、UTF-8,然后当我们用javac命令编译该源文件的时候,默认用操作系统默认字符集编码读取,中文Windows下就是GBK(GB2312),如果你保存源文件选择UTF-8保存,而读取时没有指定编码格式,系统会用GBK编码读取,就会出现乱码,报错。如果我们保存时用GBK保存、javac编译时又用GBK读取,就不会报错,然后该源文件被编译成class文件(当我打开这个class文件可以看到,class文件中较多的字符是都可以看懂的,另:比如我在源文件中定义了一个字符串”中文“,以GBK存储,当我将此源文件编译之后,生成的class文件中,”中文“二字竟然是按照UTF-8编码存放的,值是E4B8ADE69687,而”你“的UTF-8编码是E4BD)。

看上面的程序,我在源程序中定义了”中文“这个字符串,然后我在程序中输出每个字符的值,得到的是”中“这个字符的UNICODE编码是20013,”文”的UNICODE编码是25991,即:“在Java程序运行中,字符都是以Unicode存储的”这句话意思就是,我定义了“中文”这个字符串,在程序在JVM中运行的时候,这个字符串是以Unicode编码格式存储的。

虽然在源程序中我以UTF-8、GBK存储源文件了,虽然生成的class文件中“中文”是以UTF-8存储的,但在程序在JVM中运行的时候“中文”是以Unicode编码存储的。

还有一点:

如果我在上面的程序中对字符串变量s这样:s.getBytes("UTF-8");其实是把“中文”二字从Unicode编码转换成UTF-8编码的字节数组(说到这里是不是对这个函数理解的更透彻了,欧耶!),然后可以对字节数组再进行编码生成字符串!

还有一点:如下面这个十分简单的程序:

我们定义了一个字符串,然后用System.out.println输出这个字符串,这个过程中发生了什么?其实是这样的:“ab中文”字符串在JVM中是Unicode编码存储的,当我们输出的时候,System.out.println会将“ab中文”的Unicode编码转换成系统默认的GBK编码的字节数组送到输出流里,终端(即Java控制台吧)里会对输出的流里的字节按照终端的编码进行decode得到字符即我们能看懂的“ab中文”。【我说的对不】

---------------------------------------------------------------------------------------------------------

3. Java中的编码支持

Java是支持多国编码的,在Java中,字符都是以Unicode进行存储的,比如,“你”字的Unicode编码是“4f60”,我们可以通过下面的实验代码来验证: 

  1. class TestCharset          {  
  2.             public static void main(String[] args)  
  3.             {  
  4.             char c = '你';  
  5.             int i = c;  
  6.             System.out.println(c);  
  7.             System.out.println(i);  
  8.             }  
  9.     }   

 不管你在任何平台上执行,都会有相同的输出:20320,20320就是Unicode “4f60”的整数值。其实,你可以反编译上面的类,可以发现在生成的.class文件中字符“你”(或者其它任何中文字串)本身就是以Unicode编码进行存储的:char c = '\u4F60';【左边说的不正确,生成的class文件“你”是以UTF-8存储的,值为E4BD】
     4. 为了避免这种问题,建议大家都在编码中使用String.getBytes(String charset)方法。

  1. class TestCharset {  
  2.     public static void main(String[] args) {  
  3.         new TestCharset().execute();  
  4.     }  
  5.   
  6.     private void execute() {  
  7.             String s = "Hello!你好!";  
  8.             byte[] bytesISO8859 =null;  
  9.             byte[] bytesGBK = null;  
  10.             try  
  11.             {  
  12.             bytesISO8859 = s.getBytes("iso-8859-1"); 【"Hello!你好!"在内存中是Unicode编码的,即便用iso-8859-1编码,不也应该全部进行编码吗,为什么下面的输出的编码少了那么多?】 解释:ISO-8859-1对每个字符使用一个字节编码,"Hello!你好!"共9个字符,在JVM中英文部分“Hello!"的Unicode编码可转换成ISO-8859-1编码,但汉字“你好!”无法转换成ISO-8859-1,用问号代替了!有一点:Unicode编码用两个字节表示任意字符,“中文!”的Unicode编码是4个字节,但是转换成ISO-8859-1的时候成了2个字节,唉,其实Unicode转ISO-8859-1或转UTF-8都是以两个字节为单位的,转ISO-8859-1两个字节转换成一个,转UTF-8两个字节转成3个字节
  13.             bytesGBK = s.getBytes("GBK");  
  14.             }  
  15.             catch  
  16.             (java.io.UnsupportedEncodingException e)  
  17.             {  
  18.             e.printStackTrace();  
  19.             }  
  20.             System.out.println  
  21.             ("--------------\n 8859 bytes:");  
  22.             System.out.println("bytes is:     " + arrayToString(bytesISO8859));  
  23.             System.out.println("hex format is:" + encodeHex(bytesISO8859));  
  24.             System.out.println();  
  25.             System.out.println  
  26.             ("--------------\n GBK bytes:");  
  27.             System.out.println("bytes is:" + arrayToString(bytesGBK));  
  28.             System.out.println("hex format is:" + encodeHex(bytesGBK));  
  29.             }  
  30.   
  31.     public static final String  
  32.             encodeHex (byte[] bytes)  
  33.             {  
  34.             StringBuffer buff =  
  35.             new StringBuffer(bytes.length * 2);  
  36.             String b;  
  37.             for (int i=0; i<bytes.length ; i++)  
  38.             {  
  39.             b = Integer.toHexString(bytes[i]);  
  40.             // byte是两个字节的,而上面的Integer.toHexString会把字节扩展为4个字节  
  41.             buff.append(b.length() > 2 ? b.substring(6,8) : b);  
  42.             buff.append(" ");  
  43.             }  
  44.             return buff.toString();  
  45.             }  
  46.   
  47.     public static final String arrayToString(byte[] bytes) {  
  48.         StringBuffer buff = new StringBuffer();  
  49.         for (int i = 0; i < bytes.length; i++) {  
  50.             buff.append(bytes[i] + " ");    【byte数组中的默认都是ASCII码值吗?】 
  51.         }  
  52.         return buff.toString();  
  53.     }  
  54. }  

 

执行上面程序将打印出:

--------------
            8859 bytes:
            bytes is:     72 101 108 108 111 33 63 63 63【8859编码对最后三个中文无法识别,所以用三个问号表示,共用9个字节】
            hex format is:48 65 6c 6c 6f 21 3f 3f 3f
            --------------
            GBK bytes:
            bytes is:     72 101 108 108 111 33 -60 -29 -70 -61 -93 -95  即:"Hello!你好!"(GBK编码后的字节值,共12个字节,GBK对6个英文只用一个字节编码,对3个中文用两个字节编码,所以共用12个字节)
            hex format is:48 65 6c 6c 6f 21 c4 e3 ba c3 a3 a1

可见,在s中提取的8859-1格式的字节数组长度为9,中文字符都变成了“63”,ASCII码为63的是“?”,一些国外的程序在国内中文环境下运行时,经常出现乱码,上面布满了“?”,就是因为编码没有进行正确处理的结果。

-------------------------------------------------------------------------------
以下是我以前的总结,值得一看:
我的总结:
目前我可以确定的:
编码:使用指定字符集(如GBK,UTF-8等)编码,是指将字符编码成数字存储在硬盘上, 所有的东西都是以数字形式存储在硬盘上的
比如“中国”采用GBK编码后,以-42 -48 -50 -60四个字节存储在硬盘上,当显示(如在记事本中显示)的时候,才采用GBK解码,就可以看到正常的“中国”两个字了!
如果解码时使用任何其它编码(如UTF-8)就看不到正常的汉字了(除非UTF-8中的 -42 -48 -50 -60也代表“中国”两个字,另GBK是GB18030的子集 )。
同样“中国”两个字如果编码时采用了某些英文编码或其它国家字符的编码,则在进行编码时会出错或提示,因为这些编码不认识“中国”两个字无法进行编码,即便编码了,再解码时也会出现乱码。

不论Java源文件是何种编码,最终到class文件中都是Unicode编码。【 左边这句话是错的2014-12-27】--> class文件中不是Unicode编码吧???

两个函数:【重要】
byte[] getBytes(Charset charset)       【这个byte数组中的是按指定编码转换后的编码值】
          把JVM内存中unicode形式的String按encoding制定的编码,转成字节流 【将Unicode编码的数值转换成charset指定的编码的编码值】
          使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
String(byte[] bytes, Charset charset)      【仅有上面byte数组中的编码值还不可以,还得对编码进行解码才能正常显示】
          通过使用指定的 charset解码指定的 byte数组,构造一个新的 String。
案例:
1.JVM内部的String,Char都是用unicode存储(没有任何编码),比如:
"分"的unicode=20998(十进制)String=[20998],String中有1个char
"分享"的unicode=20998,20139 (十进制)String=[20998,20139],String中有2个char
无论系统编码是什么,"分"这个字在JVM中都是20998
-------------------------------------------------------------------------------------------------------------------------------
下面Java文件的编码是UTF-8的,其中Java文件最终会以Unicode编码存在class文件中【错,class文件并不是Unicode编码的】,下面Java程序在Myeclipse中和我用javac命令和Java命令运行结果并不相同(切记,最好用原生的Java命令做此类测试, Myeclipse中的输出结果不可信 ):
eclipse对于读取Java源文件等会增加一些操作,如你的Java源文件是以UTF-8保存的,则eclipse编译时会自动在javac前面加上 encoding参数指定以UTF-8编码读取,而且好似在 System.out.println(new String("中华人民共和国".getBytes("GBK"))); 中,如果不指定解码的字符集编码,则会以该源文件保存时的编码格式读取,所以这行代码等同于 System.out.println(new String("中华人民共和国".getBytes("GBK"),"UTF-8"));
-------------------------------------------------------------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值