字节流和字符流探究

前言

以前在学习流概念的时候,一直会被提到字节流和字符流,也经常涉及到各种编码方式,如ASCII和UNICODE,但老师告知文本传输最好使用字符流,而其他的最好使用字节流,虽然知道用法,但是还是心中有所疑问,今天就探究之后做如下记录。

编码

ANSCII编码

计算机是美国人过出来的,所以一开始他们只需要考虑现实英文字母,而英文字母只有26个,外加上计算机上的其他32个不可显示字符和符号等,1个字节(可以表示256个不同的状态)的编码方式显然对他们足够了。

所以,ASCII一共规定了128个字符的编码,包括了英文字符和所有其他字符,这128个字符只占用了字节的后7位,开头一位统一规定为0。

非ANSII编码的产生

ANCII只支持英文字符的现实,而地球上那么多语言,比如中文,德文,日文,每种语言的字母个数都不同而繁多,所有anscii编码对他们远远不够,所以在这阶段许多国家都有自己的编码标准,比如中国的GB2312,使用了两个字节表示 一个汉字,最多表示了 65536 个汉字。

Unicode

正如前面所言,多种编码方式给不同语种之间的通信带来了很大的麻烦,经常会遇到文件打开乱码的情况,所以提出了一种包含了所有符号的编码方式——Unicode编码,现在Unicode编码已经包含了百万多的符号,具体的符号索引,可以查询 unicode.org

注意,unicode虽然规定了符号的二进制编码形式,但一个字符存储到电脑上的二进制代码又是不同的,因为计算机并不能根据unicode编码知道字符占几个字节,简单来说,它需要一次编码解码过程,即将unicode进行编码成新的二进制代码存储到电脑。为什么需要这样做呢?我们知道,英文只需要一个字节就可以表示,汉字则最少需要两个字节,可能还有其他的需要3-4字节,如果unicode统一规定所有符号都用4字节编码表示,那么每个英文字母前面都有2-3字节是0,这将造成极大的浪费,所以需要一种具体落实unicode编码的技术——即UTF8、UTF16和UTF32都是实现unicode字符存储的实现方式

UTF-8

utf-8是当前应用最广的unicode实现方式,正如我们前面所说,为了解决浪费问题,它是一种变长编码方案,它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

他的编码规则很简单:

  1. 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

  2. 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码

下表总结了编码规则,字母x表示可用编码的位。

Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
--------------------±--------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx (11表明了占用两个字节,第二字节开头10占位)
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx (111表明符号长3字节)
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx(1111表明符号长4字节)

例子,的 Unicode 是4E25100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5

过渡

之前说了编码方式的由来,之后将基于java来探讨下编码和字节流、字符流之间的关系。

字节流

计算机的最小存储单位是字节,所有文件,如视频、音频等等,亦包括文本,都将转换成二进制的字节流存储,所以其实从根本上来讲,后面的字符流只是在字节流上做了一层字符编码解码的工作。inputstream和outputstream是专门为字节流服务的两个类,字节流的应用偏向于非文本文件,应用比较清晰明了,不做赘述。

字符流

我们说,字符流是字节流的包装,只是在字节流之外添加了一层字符集,只要我们指定字符集就可以实现对应的编解码,从java的继承层次上我们可以看到这层关系。

InputStreamReader是读取字符流的类,该类里面有成员StreamDecoder,而StreamDecoder

又包含了InputStream和Charset, 所以我们可以很明显知道,字符流其实是对字节流的包装。

对应的,outputStreamWriter是写入字符流的类。

在我们的应用程序中涉及到 I/O 操作时只要注意指定统一的编解码 Charset 字符集,这样一般不会出现乱码问题,有些应用程序如果不注意指定字符编码,中文环境中取操作系统默认编码,如果编解码都在中文环境中,通常也没问题,但是还是强烈的不建议使用操作系统的默认编码,因为这样,你的应用程序的编码格式就和运行环境绑定起来了,在跨环境下很可能出现乱码问题。

​ ——选自参[2]

扩展

场景的不同对编码方式的要求不一样,不一样的编码会导致效率等的不同,常见需要编码的场景是磁盘io和网络io,网络io需要经常用到编码,包括url、cookie和http header等,这里面的应用可以参考深入分析 Java 中的中文编码问题

参考

1.字符编码笔记:ASCII,Unicode和UTF-8

2.深入分析 Java 中的中文编码问题

3.Java 中字节流与字符流的区别?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值