如果你是JVM的设计者,让你来决定JVM中所有字符的表示形式,你会不会允许使用各种编码方式的字符并存?
我想你的答案是不会,如果在内存中的Java字符可以以GB2312,UTF-16,BIG5等各种编码形式存在,那么对开发者来说,连进行最基本的字符串打印、连接等操作都会寸步难行。例如一个GB2312的字符串后面连接一个UTF-8的字符串,那么连接后的最终结果应该是什么编码的呢?你选哪一个都没有道理。
因此牢记下面这句话,这也是Java开发者的共同意志:在Java中,字符只以一种形式存在,那就是Unicode(注意到我们没有选择特定的编码,直接使用它们在字符集中的编号,这是统一的唯一方法)。
但“在Java中”到底是指在哪里呢?就是指在JVM中,在内存中,在你的代码里声明的每一个char,String类型的变量中。例如你在程序中这样写
char han='汉';
在内存的相应区域,这个字符就表示为0x6C49.可以用下面的代码证明一下:
char han='汉';
System.out.format("%x",(short)han);
输出是:6c49反过来用Unicode编号来指定一个字符也可以,像这样:
char han=0x6c49;
System.out.println(han);
输出是:汉
这其实也是说,只要你正确的读入了“汉”这个字,那么它在内存中的表示形式一定是0x6C49,没有任何其他的值能代表这个字(当然,如果你读错了,那结果是什么就不知道了,范伟说:读,读错了呀,那还等于好几亿呢;本山大哥说:好几亿你也没答上,请听下一题)。
JVM的这种约定使得一个字符存在的世界分为了两部分:JVM内部和OS的文件系统。在JVM内部,统一使用Unicode表示,当这个字符被从JVM内部移到外部(即保存为文件系统中的一个文件的内容时),就进行了编码转换,使用了具体的编码方案(也有一种很特殊的情况,使得在JVM内部也需要转换,不过这个是后话)。
因此可以说,所有的编码转换就只发生在边界的地方,JVM和OS的交界处,也就是你的各种输入输出流(或者Reader,Writer类)起作用的地方。
话头扯到这里就必须接着说Java的IO系统。
尽管看上去混乱繁杂,但是所有的IO基本上可以分为两大阵营:面向字符的Reader啊Wrtier啊,以及面向字节的输入输出流。
下面我来逐一分解,其实一点也不难。
面向字符和面向字节中的所谓“面向”什么,是指这些类在处理输入输出的时候,在哪个意义上保持一致。如果是面向字节,那么这类工作要保证系统中的文件二进制内容和读入JVM内部的二进制内容要一致。不能变换任何0和1的顺序(也就是文件是怎么存的就怎么取,与文件保存的编码一致)。因此这是一种非常“忠实于原著”的做法(偶然间让我想起郭敬明抄袭庄羽的文章,那家伙,太忠实于原著了,笑)。
这种输入输出方式很适合读入视频文件或者音频文件,或者任何不需要做变换的文件内容。
而面向字符的IO是指希望系统中的文件的字符和读入内存的“字符”(注意和字节的区别)要一致。例如我们的中文版WindowsXP系统上有一个GBK的文本文件,其中有一个“汉”字,这个字的GBK编码是0xBABA(而Unicode编号是0x6C49),当我们使用面向字符的IO把它读入内存并保存在一个char型变量中时,我希望IO系统不要傻傻的直接把0xBABA放到这个char型变量中,我甚至都不关心这个char型变量具体的二进制内容到底是多少,我只希望这个字符读进来之后仍然是“汉”这个字。
从这个意义上也可以看出,面向字符的IO类,也就是Reader和Writer类,实际上隐式的为我们做了编码转换,在输出时,将内存中的Unicode字符使用系统默认的编码方式进行了编码,而在输入时,将文件系统中已经编码过的字符使用默认编码方案进行了还原。我两次提到“默认”,是说Reader和Writer的聪明也仅此而已了,它们只会使用这个默认的编码来做转换,你不能为一个Reader或者Writer指定转换时使用的编码。这也意味着,如果你使用中文版WindowsXP系统,而上面存放了一个UTF-8编码的文件,当你使用Reader类来读入的时候,它会傻傻的使用GBK来做转换,转换后的内容当然驴唇不对马嘴!
这种笨,有时候其实是一种傻瓜式的功能提供方式,对大多数初级用户(以及不需要跨平台的高级用户)来说反而是件好事。
但我们不一样啦,我们都是国家栋梁,肩负着赶英超美的责任,必须师夷长技以治夷,所以我们总还要和GBK编码以外的文件打交道。
说了上面这些内容,想必聪明的读者已经看出来,所谓编码转换就是一个字符与字节之间的转换,因此Java的IO系统中能够指定转换编码的地方,也就在字符与字节转换的地方,那就是(读者:InputStreamReader和OutputStreamWriter!作者:太强了,都会抢答了!)
PrintStream也可以对OutputStream进行包装并指定编码方式:PrintStream(OutputStream out,
boolean autoFlush, String encoding),但实质上也是调用OutputStreamWriter来实现的。
System.err在eclipse中输出时是红色的字体。
这两个类是字节流和字符流之间的适配器类,因此他们肩负着编码转换的任务简直太自然啦!要注意,实际上也只能在这两类实例化的时候指定编码,是不是很好记呢?
下面来写一段小程序,来把“汉”字用我们非常崇拜的UTF-8编码写到文件中!
try {
PrintWriter out = new PrintWriter(new OutputStreamWriter(
new FileOutputStream("c:/utf-8.txt"), "UTF-8"));
try {
out.write("汉");
} finally {
out.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
运行之后到c盘下去找utf-8.txt这个文件,用UltraEdit打开,使用16进制查看,看到了什么?它的值是0xE6B189!噢耶!(读者:这,这有什么好高兴的……)
【转自www.bitsCN.com】
分享到:
相关推荐
Java中的字符编码转换是编程实践中一个至关重要的概念,尤其是在处理多语言环境和跨平台交互时。Java通过统一采用UTF-16编码格式在JVM内部处理字符,简化了字符操作的复杂性。UTF-16是一种变长的Unicode编码,它可以...
### Java中的字符集编码入门(五):Java代码中的字符编码转换Part1 #### 核心知识点概述: 本文档深入探讨了Java编程语言中字符集编码的基础知识,并着重讲解了字符编码转换的基本原理及其在Java代码中的应用。...
Java中的字符编码转换是编程实践中不可或缺的一部分,尤其是在处理多语言和跨平台的场景下。Java选择了UTF-16作为其内部字符表示的标准,这确保了在JVM内存中,所有字符都以统一的方式存储,避免了编码不一致带来的...
一一击破Java入门可能会遇到的难点和疑惑 抽丝剥茧,层层推进,让知识环环相扣,降低了学习的难度 通过大量的比喻、类比、对比和图示等多种讲解方式,学习效果好 对Java语言的每个语法都提供了一个或多个例程讲解 ...
一一击破Java入门可能会遇到的难点和疑惑 抽丝剥茧,层层推进,让知识环环相扣,降低了学习的难度 通过大量的比喻、类比、对比和图示等多种讲解方式,学习效果好 对Java语言的每个语法都提供了一个或多个例程讲解 ...
CHR:字符集(字体文件) CHT:ChartViem文件;Harvard Graphics矢量文件 CIF:Adaptec CD 创建器 CD映像文件 CIL:Clip Gallery下载包 CIM:SimCity 2000文件 CIN:OS/2改变控制文件用于跟踪INI文件中的变化 ...
nclob 1~4GB 保存本地语言字符集数据 blob 1~4GB 以二进制信息保存数据 三、 DDL语言 1. Create table命令 用于创建表。在创建表时,经常会创建该表的主键、外键、唯一约束、Check约束等 语法结构 create table...
4.2.1 设置网页的编码 47 4.2.2 设定文档标题 48 4.2.3 添加关键字 48 4.2.4 设置网页的刷新 49 4.2.5 插入meta对象 50 4.3 插入表格 50 4.3.1 引例 50 4.3.2 制作表格 51 4.3.3 编辑表格 52 4.3.4 表格...
1. Introduction to Zend Framework 1.1. 概述 1.2. 安装 2. Zend_Acl 2.1. 简介 2.1.1. 关于资源(Resource) 2.1.2. 关于角色(Role) 2.1.3. 创建访问控制列表(ACL) 2.1.4. 注册角色(Role) 2.1.5. 定义访问...