Java 字节流与字符流

字符流使用缺省编码

通过前面的例子,已经得出了一个结论:字符流=字节流+编码。
可以在构建字符流时显示传入编码参数,那么所得到的字符流就会以该编码来 编码(encode)解码(decode) 字节流,这会给文本数据处理带来极大方便。
但有时,构建字符流时也可以不传入编码参数,比如如下直接构建一个 InputStreamReader :


对比注释掉的代码,可以看出此时只用了字节流,没有指定编码。
那么这时它到底是以什么编码来解码(decode)它要读取的字节流呢?比如上面的 utf-8 编码的文本文件它能否正常读取呢?我们想知道,编码参数是必要的吗?如果没有指定,它是否会自动猜测出正确的编码呢?
这里先给出结论,那就是编码参数是必要的,是 decode(或 encode)所必不可少的,之所以可以省略那是因为系统会为我们提供缺省值。
然后,它也不会去自动猜测。(至少这里的 BufferedReader 之类的不会去猜测,如果你使用其它的第三方增强的工具类,那就未可知)没有指定它就用缺省去 decode(或 encode)。
那么,这就可能导致一个问题。比如现在要读取的文件是 utf-8 编码的,而假如系统的缺省编码值是 gbk 的话,显然,它将不能正确地解码!

缺省编码来自哪里?

但是,系统的缺省编码到底是什么呢?这个缺省值可以通过这样来得到:


也就是用 Charset.defaultCharset() 或 System.getProperty(“file.encoding”) 的方式可以得到它。
当我在本地的 Windows 系统执行这段程序时,结果如下:

缺省编码是 gbk。
而当我把这个 class 类上传到云主机时,那是个 linux 系统(具体为 centos 7),再执行时发现结果又不同了:

此时的结果是 ASCII(ANSI_X3.4-1968 是它的一个别名)。不同系统,甚至不同版本这个值可能有很大差异,跟具体的环境配置也有关,在你的 linux 系统里可能输出是 utf-8 等值。
如果你用的是 Mac,你也可以试一试看看结果是什么。(我没有 Mac,所以没有测试这个~)
不过可以通过增加运行参数调整这个值,比如以这样的方式
java -Dfile.encoding=utf-8 DefaultEncoding
来运行,缺省编码就变成 utf-8 了:

在 linux 系统上也是同样的:

而我在 Windows 系统下的 Eclipse 工程里用它的“运行”来做这个测试时,结果是 UTF-8 而不是 GBK,因为工程我设置了缺省编码是 UTF-8,而 Eclipse 运行时会根据你工程设置传入这个参数,具体如下,在 Debug 视图 中,选中运行的实例–右键–选择“properties”,在弹出的窗口中的 Command Line(命令行)部分可以看到指定了编码:

如果你工程指定的编码是 GBK 或没有指定,在 Windows 下输出结果就会跟在 CMD 命令行窗口中那样,结果应该为 GBK。
在 Idea IDE 中也是类似的,比如工程编码是 GBK,那么运行出来的结果也是 GBK:

在上图中,勾选“use soft wraps”(使用软换行),
这里因为是这个生成的命令行特别长,主要是那一堆的 classpath,跟 eclipse 类似,所以让其换行显示。
然后单击所运行的命令展开它的细节,可以发现也传入了相关 file.encoding 参数:

这个参数决定了缺省值是什么。如果你没有指定,JVM 将询问它所在的运行环境(一般也就是操作系统了)来得到一个缺省值。
如果你运行 web server 的程序,比如 tomcat,也可以照此查看它的参数,或通过它改变缺省值。通过显式指定一个缺省值,可以在各种运行环境下保持一致。
比如这是我本机 Eclipse 上运行 tomcat 插件时的命令行:

可以看到也是传入了这个参数。不过有一点要注意的是,至少就 server 中的这个缺省值而言,它影响的是上述所说的 new InputStreamReader 以及 getBytes 之类的方法。而在 servlet 的响应流中,如果使用 response.getWriter 得到的 PrintWriter 这个字符流,它的缺省又是另外一个值,具体为 iso-8859-1.
这个缺省值来自于 servlet 的规范,进一步的原因则是来源于 http 规范。关于这一话题将在另一篇文章中再去分析。
所以,总体而言,如果你构建字符流时使用缺省编码,那么情况会比较混乱,也很容易出错。

使用缺省编码去读取

现在来看一些具体的例子。假如要读取的还是之前那个 utf-8 编码的文本文件,内容还是“hi你好”,总计 8 个字节,具体十六进制如下:


写一段读取它的程序如下:

然后在 CMD 中编译并运行,那么结果是这样的:

可以看到出来的并不是想要的“hi你好”,而是很奇怪的“hi 浣犲ソ ”。那么其中的原因也不难理解,因为没有指定编码,所以程序就用缺省编码去解码这段字节流,此时是 gbk,所以就把那六字节按每两字节去解析,结果得到三个汉字:

对于我们中国人来说,会意识到出了问题,因为这三字很不常见,但对于歪果仁来说,他可能没有什么感觉,反正就是一些方块字而已,反正他都不认识。
假如现在显式指定 –Dfile.encoding=utf-8,那么结果就 OK 了:

又假如指定为 –Dfile.encoding=iso-8859-1 呢?那么结果就按 iso-8859-1 解析了:

这里它显示为六个问号,其实正常来说,应该是有对应字符的。如果在 eclipse 本身执行,console 中的输出是这样的:

所以显示为问号可能是 CMD 程序本身的一些问题。
毕竟,前面也一再提到,文本文件内容本身是不包含有编码信息的(BOM 的情况除外),就这么 8 个字节,你有很多的解析可能,按 gbk,按 utf-8,按 iso-8859-1,都能解析出一些字符来。

总结

由此也不难明白,缺省绝对不是什么好主意。假如你不知道别人启动 web server 时传入了什么参数,或者没传参数时你不知道程序到底运行在什么操作系统上,你就完全无法预料你的程序会运行出什么结果。
通常,我们把这种情况称为对环境形成了依赖,是种不稳定因素。程序员在开发活动中常遇到的一种情况是:“在我这里运行明明是正常的,怎么放到其它地方去就不正常了呢?”就有可能是这种环境差异导致的。
假如你想把这段字节流稳定地解析成“hi你好”,那么最好的方式就是在你的代码里显式构建 utf-8 的字符流,而不是依赖缺省。
当然,有人可能会说,每次都要显式指定,我觉得太麻烦了,我还是希望用缺省,那么这种情况你就要保证所有环节都使用统一的编码,比如所有你要读入的文件都是用这一编码编码的等等,然后用 –Dfile.encoding 参数指定这一编码。假如你无法保证在所有环节统一,恐怕你就不能如此依赖缺省了。

欢迎加入学习交流群569772982,大家一起学习交流。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,066评论 0 62
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,567评论 18 399
  • 在经过一次没有准备的面试后,发现自己虽然写了两年的android代码,基础知识却忘的差不多了。这是程序员的大忌,没...
    猿来如痴阅读 2,827评论 3 10
  • 医务工作经广泛的调查和统计分析,根据身高与体重因素给出了以一按“体指数”进行体型判断的方法: 体指...
    逍遥_9353阅读 3,187评论 0 0
  • 舌尖上的中国以及另一个干脆叫做小面的纪录片,炒火了重庆的一个著名的街边小吃小面,上次去重庆是五一的时候吧,在街边真...
    Lujuannnn阅读 244评论 0 2