一、前言

  继上一篇写完字节编码内容后,现在分析在Java中各字符编码的问题,并且由这个问题,也引出了一个更有意思的问题,笔者也还没有找到这个问题的答案。也希望各位园友指点指点。

二、Java字符编码

  直接上代码进行分析似乎更有感觉。 

public class Test {
public static String stringInfo(String str, String code) throws Exception {
byte[] bytes = null;
if (code.equals("")) // 使用缺省编码格式
bytes = str.getBytes();
else // 使用指定编码格式
bytes = str.getBytes(code);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) { // 转化为十六进制
sb.append(Integer.toHexString(bytes[i] & 0xff).toUpperCase() + " ");
}
// 对最后一个空格做处理(为了显示美观)
String info = sb.toString().substring(0, sb.toString().length() - 1);
// 组合返回
StringBuffer result = new StringBuffer();
result.append(bytes.length);
result.append("[");
result.append(info);
result.append("]");
return result.toString();
} public static void main(String[] args) throws Exception {
String left = "(";
String right = ") : ";
String[] strs = {"L", "LD", "李", "李邓"};
String[] codes = {"ASCII", "ISO-8859-1", "GB2312", "GBK", "Unicode", "UTF-8", "UTF-16", "UTF-16BE", "UTF-16LE", ""};
for (String code : codes) {
for (String str : strs) {
System.out.println(str + left + (!code.equals("") ? code : "default") + right + stringInfo(str, code));
}
System.out.println("---------------------------------------");
}
}
}

  运行结果:   

L(ASCII) : 1[4C]
LD(ASCII) : 2[4C 44]
李(ASCII) : 1[3F]
李邓(ASCII) : 2[3F 3F]
---------------------------------------
L(ISO-8859-1) : 1[4C]
LD(ISO-8859-1) : 2[4C 44]
李(ISO-8859-1) : 1[3F]
李邓(ISO-8859-1) : 2[3F 3F]
---------------------------------------
L(GB2312) : 1[4C]
LD(GB2312) : 2[4C 44]
李(GB2312) : 2[C0 EE]
李邓(GB2312) : 4[C0 EE B5 CB]
---------------------------------------
L(GBK) : 1[4C]
LD(GBK) : 2[4C 44]
李(GBK) : 2[C0 EE]
李邓(GBK) : 4[C0 EE B5 CB]
---------------------------------------
L(Unicode) : 4[FE FF 0 4C]
LD(Unicode) : 6[FE FF 0 4C 0 44]
李(Unicode) : 4[FE FF 67 4E]
李邓(Unicode) : 6[FE FF 67 4E 90 93]
---------------------------------------
L(UTF-8) : 1[4C]
LD(UTF-8) : 2[4C 44]
李(UTF-8) : 3[E6 9D 8E]
李邓(UTF-8) : 6[E6 9D 8E E9 82 93]
---------------------------------------
L(UTF-16) : 4[FE FF 0 4C]
LD(UTF-16) : 6[FE FF 0 4C 0 44]
李(UTF-16) : 4[FE FF 67 4E]
李邓(UTF-16) : 6[FE FF 67 4E 90 93]
---------------------------------------
L(UTF-16BE) : 2[0 4C]
LD(UTF-16BE) : 4[0 4C 0 44]
李(UTF-16BE) : 2[67 4E]
李邓(UTF-16BE) : 4[67 4E 90 93]
---------------------------------------
L(UTF-16LE) : 2[4C 0]
LD(UTF-16LE) : 4[4C 0 44 0]
李(UTF-16LE) : 2[4E 67]
李邓(UTF-16LE) : 4[4E 67 93 90]
---------------------------------------
L(default) : 1[4C]
LD(default) : 2[4C 44]
李(default) : 3[E6 9D 8E]
李邓(default) : 6[E6 9D 8E E9 82 93]
---------------------------------------

  说明:通过结果我们知道如下信息。

  1. 在Java中,中文在用ASCII码表示为3F,实际对应符号'?',用ISO-8859-1表示为3F,实际对应符号也是为'?',这意味着中文已经超出了ASCII和ISO-8859-1的表示范围。

  2. UTF-16采用大端存储,即在字节数组前添加了FE FF,并且FE FF也算在了字符数组长度中。

  3. 指定UTF-16的大端(UTF-16BE)或者小端(UTF-16LE)模式后,则不会有FE FF 或 FF FE控制符,相应的字节数组大小也不会包含控制符所占的大小。

  4. Unicode表示与UTF-16相同。

  5. getBytes()方法默认是采用UTF-8。

三、char表示问题

  我们知道,在Java中char类型为两个字节长度,我们来看下一个示例。 

public class Test {
public static void main(String[] args) throws Exception {
char ch1 = 'a'; //
char ch2 = '李'; //
char ch3 = '\uFFFF'; //
char ch4 = '\u10000'; // }
}

  问题:读者觉得这样的代码能够编译通过吗?如不能编码通过是为什么,又具体是那一行代码出现了错误?

  分析:把这个示例拷贝到Eclipse中,定位到错误,发现是第四行代码出现了错误,有这样的提示,Invalid character constant。

  解答:问题的关键就在于char类型为两个字节长度,Java字符采用UTF-16编码。而'\u10000'显然已经超过了两个字节所能表示的范围了,一个char无法表示。说得更具体点,就是char表示的范围为Unicode表中第零平面(BMP),从0000 - FFFF(十六进制),而在辅助平面上的码位,即010000 - 10FFFF(十六进制),必须使用四个字节进行表示。

  有了这个理解后,我们看下面的代码  

public class Test {
public static void main(String[] args) throws Exception {
char ch1 = 'a';
char ch2 = '李';
char ch3 = '\uFFFF';
String str = "\u10000";
System.out.println(String.valueOf(ch1).length());
System.out.println(String.valueOf(ch2).length());
System.out.println(String.valueOf(ch3).length());
System.out.println(str.length());
}
}

  运行结果:

1
1
1
2

  说明:从结果我们可以知道,所有在BMP上的码点(包括'a'、'李'、'\uFFFF')的长度都是1,所有在辅助平面上的码点的长度都是2。注意区分字符串的length函数与字节数组的length字段的差别。

四、问题的发现

  在写Java小程序时,笔者一般不会打开Eclispe,而是直接在NodePad++中编写,然后通过javac、java命令运行程序,查看结果。也正是由于这个习惯,发现了如下的问题,请听笔者慢慢道来,来请园友们指点指点。

  有如下简单程序,请忽略字符串的含义。

public class Test {
public static void main(String[] args) throws Exception {
String str = "我我我我我我我\uD843\uDC30";
System.out.println(str.length());
}
}

  说明:程序功能很简单,就是打印字符串长度。

  4.1 两种编译方法

  1. 笔者通过javac Test.java进行编译,编译通过。然后通过java Test运行程序,运行结果如下:

  

  说明:根据结果我们可以推测,字符'我'为长度1,\uD843\uDC30为长度10,其中\u为长度1。

  2. 笔者通过javac -encoding utf-8 Test.java进行编译,编译通过。然后通过java Test运行程序,运行结果如下:

  

  说明:这个结果很好理解,字符'我'、\uD843、\uDC30都在BMP,都为长度1,故总共为9。

  通过两种编译方法,得到的结果不相同,经过查阅资料知道javac Test.java默认的是采用GBK编码,就像指定javac -encoding gbk Test.java进行编译。

  4.2. 查看class文件

  1. 查看java Test.java的class文件,使用winhex打开,结果如下:

  说明:图中红色标记给出了字符串"我我我我我我我\uD843\uDC30"大致所在位置。因为前面我们分析过,class文件的存储使用UTF-8编码,于是,先算E9 8E B4,得到Unicode码点为94B4(十六进制),查阅Unicode表,发现表示字符为'鎴',这完全和'我'没有关系。并且E9 8E B4 后面的E6 88 9E,和E9 8E B4也不相等,照理说,相同的字符编码应该相同。后来发现,红色标记地方好像有点规则,就是E9 8E B4 E6 88 9E E5 9E 9C(九个字节)表示'我我',重复循环了3次,表示字符'我我我我我我',之后的E9 8E B4 E6 85(五个字节)表示'我',总共7个'我',很明显又出现疑问了。

  猜测是因为使用javac Test.java进行编译,采用的是GBK编码,而class文件存储的格式为UTF-8编码。这两种操作中肯定含有某种转化关系,并且最后的class文件中也加入相应的信息。

  2. 查看java -encoding -utf-8 Test.java的class文件,使用winhex打开,结果如下:

  说明:红色标记给出了字符串的大体位置,E6 88 91,经过计算,确实对应字符'我'。这是没有疑问的。

  4.3 针对疑问的探索

  1. 又改变了字符串的值,使用如下代码:

public class Test {
public static void main(String[] args) throws Exception {
String str = "我我coder";
System.out.println(str.length());
}
}

  同样,使用javac Test.java、java Test命令。得到结果为:

  

  这就更加疑惑了。为什么会得到8。

  2. 查阅资料结果

   在Javac时,若没有指定-encoding参数指定Java源程序的编码格式,则javac.exe首先获得我们操作系统默认采用的编码格式,也即在编译java程序时,若我们不指定源程序文件的编码格式,JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式,如WIN2k,它的值为GBK),然后JDK就把我们的java源程序从file.encoding编码格式转化为Java内部默认的UTF-16格式放入内存中。之后会输出class文件,我们知道class是以UTF-8方式编码的,它内部包含我们源程序中的中文字符串,只不过此时它己经由file.encoding格式转化为UTF-8格式了。

五、问题提出

  1. 使用javac Test.java编译后,为何会得到上述class文件的格式(即GBK -> UTF16 -> UTF8具体是如何实现的)。

  2. 使用javac Test.java编译后,为何得到的结果一个是17,而另外一个是8。

六、总结

  探索的过程有很意思,这个问题暂时还没有解决,以后遇到该问题的答案会贴出来,也欢迎有想法的读者进行交流探讨。谢谢各位园友的观看~

参考链接:

http://blog.csdn.net/xiunai78/article/details/8349129

  

  

【字符编码】Java字符编码详细解答及问题探讨的更多相关文章

  1. 关于JAVA字符编码:Unicode,ISO-8859-1,GBK,UTF-8编码及相互转换

    我们最初学习计算机的时候,都学过ASCII编码. 但是为了表示各种各样的语言,在计算机技术的发展过程中,逐渐出现了很多不同标准的编码格式, 重要的有Unicode.UTF.ISO-8859-1和中国人 ...

  2. Java 字符编码归纳总结

    String newStr = new String(oldStr.getBytes(), "UTF-8");       java中的String类是按照unicode进行编码的 ...

  3. 【JAVA编码专题】JAVA字符编码系列一:Unicode,GBK,GB2312,UTF-8概念基础

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

  4. java字符编码(转)

    转载:http://blog.csdn.net/peach99999/article/details/7231247 深入讨论java乱码问题 几种常见的编码格式 为什么要编码 不知道大家有没有想过一 ...

  5. 小伙子又乱码了吧-Java字符编码原理总结

    前提 配合前面阅读的I/O和NIO的资料,现在总结一下关于字符集和乱码问题的原理和解决方案.参考资料: 码表ASCII Unicode GBK UTF-8 字符编码笔记ASCII,Unicode和UT ...

  6. JAVA字符编码一:Unicode,GBK,GB2312,UTF-8概念基础

    第一篇:JAVA字符编码系列一:Unicode,GBK,GB2312,UTF-8概念基础 来源:holen'blog   对字符编码与Unicode,ISO 10646,UCS,UTF8,UTF16, ...

  7. java字符编码-Unicode编码问题刨根究底

    博客搬家: java字符编码问题 前段时间在读<java核心技术卷一>,遇到一些名词:码点.代码单元等,其实字面意思不难理解,解释如下 码点(code point):Unicode编码表中 ...

  8. java字符编码和oracle乱码

    编码问题我仍旧没搞懂,最根本的从哪里来就没搞懂.当页面发送请求,编码到后台是什么编码呢?好吧,我默认的都是utf-8.后台接收参数后,可以在控制台打印出来,我也不清楚是什么编码.然后,就是数据库问题. ...

  9. 【JAVA编码专题】 JAVA字符编码系列三:Java应用中的编码问题

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

随机推荐

  1. 参数的元数据信息&数据库的元数据信息

    package it.cast.jdbc; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql. ...

  2. ADO.NET Entity Framework CodeFirst 如何输出日志(EF 5.0)

    ADO.NET Entity Framework CodeFirst 如何输出日志(EF4.3) 用的EFProviderWrappers ,这个组件好久没有更新了,对于SQL执行日志的解决方案的需求 ...

  3. Mono 3.2 测试NPinyin 中文转换拼音代码

    C#中文转换为拼音NPinyin代码  在Mono 3.2下运行正常,Spacebuilder 有使用到NPinyin组件,代码兼容性没有问题. using System; using System. ...

  4. 对改善ABP的一些建议

    园子里有不少同学对ABP框架很感兴趣,而且也已经将ABP用在了商用项目中,有些可能还在操练阶段.一般来说,我们使用ABP默认的一些功能已经足够了,但还是有很多人想要自己拓展一些功能而自己实现不了或者说 ...

  5. MySQL 体系结构

    标签:MYSQL/数据库/查询原理/体系结构 概述 学习一门数据库系统首先得了解它的架构,明白它的架构原理对于后期的分析问题和性能调优都有很大的帮助,接下来就通过分析架构图来认识它. 目录 概述 架构 ...

  6. JS获取剪贴板图片之后的格式选择与压缩问题

    前言 某年某月的某一天,突然发现博客服务器上上传的图片都比较大,一些很小的截图都有几百kb,本来服务器带宽就慢,不优化一下说不过去. 问题细述 特别说明:本文代码因为只是用于我自己后台写markdow ...

  7. android内部培训视频_第一节

    声明:本视频为公司内部做android培训时录制的,无任何商业目的.同时鉴于水平有限,可能不符合您的需求,放在这里的目的是提供给公司同事下载,作为培训的一个记录,也作为一个系列教程的自我督促完成的理由 ...

  8. 可扩容分布式session方案

    分布式session有以下几种方案: 1. 基于nfs(net filesystem)的session共享 将共享服务器目录mount各服务器的本地session目录,session读写受共享服务器i ...

  9. 在互联网公司参与拍卖是一种怎样的感觉?part 1

    拍卖在中国是不太流行的一件事,为什么呢?说不太出.当初在外国火的不得了的ebay在交易时采用的就是拍卖出价的模式,但进入中国后这种方式就是玩不转,不得以后来也变成了跟淘宝一样的一口价方式. 话说现在每 ...

  10. Python3 的json 和 PHP的json

    Python3操作json的标准api库参考:https://docs.python.org/3/library/json.html#module-json >>> aa = ['/ ...