关于编码格式的小纪

写在前面

编解码的问题是个蛮大的概念,网上也搜过一些资料,讲的也都蛮详细的,写此一篇也是给自己做个纪要,以免日后再遇见编解码问题无法解决

遇见问题

在项目中使用的dubbo来做服务间通信的方式,有一个需求是将一个图片流显示给前端,由于项目依赖层级的原因,这个图片流只能在服务的最底层生成,然后向上传递,最后交给调用者。一开始就简单的将输出流inputStream作为返回值传出去了,结果报错,大意是不识别某个字符,刚开始没想到是编码格式的问题,以为就是不能传数据流,就想着要不把流转成string传出去,结果试了之后dubbo倒是没报错,生成的图片无法打开,debug发现,底层生成的inputStream的数据长度跟最后string转成的inputStream的长度不相同,才想到可能是编码格式的问题。

寻求解答

其实想到是编码格式的问题,第一反应是设置一个编码格式好了,于是给转码和解码都加了UTF-8格式的要求,结果一看仍然是不对的,这就有点懵逼了,因为本身对这块不是很熟,就想着那要不就稍微研究一下是为什么好了。

首先是原理

从原理上来说,计算机都是以二进制来储存所有的数据的,那要表达全世界各种各样的字符,就需要有相应的编码格式,告诉计算机要以什么样的规则来解析一段指定的二进制数据。而计算机在读取二进制数据的时候又规定了以八个二进制位为一个字节,即1byte,由于每位二进制可以有0和1两种形式,所以1byte可以表示256种不同含义。那用不同的编码格式来读取同样的二进制,可以得到不同的结果,比如同样的1000100011110001,这是十六位二进制数据,以ISO-8859-1解析和以UTF-8来解析,结果就是完全不一样的意思(当然我不知道这具体是啥意思,随手写的,保证结果不一样而已)。这也就是为什么,同样的一份txt文件,可以是一堆乱码,也可以是正常显示的文字的原因。

然后是原因

上面讲了一些计算机读取文件时显示具体内容的原理,提到了编码格式,现在为人熟知且常用的就那几种,ASCII,ISO-8859-1,UTF-8,UTF-16,GBK。这里面有两组是很相似的,一个是ASCII和ISO-8859-1,一个是UTF-8和UTF-16。首先是ASCII和ISO-8859-1,同样都是采用单字节读取,即用一个字节表示一个字母,不同的是ASCII表只能表示128个字符,因为最高位是0不变的;而ISO-8859-1会用到最高位的值,所以能够表示256种不同的字符。而UTF-8和UTF-16则是Unicode编码的实现,这里就不得不讲一下Unicode编码和UTF-8,UTF-16的区别。后两者的区别比较简单,UTF-8采用的是1~4个字节不定长的存储格式,而UTF-16则是两个字节或四个字节来存储字符。Unicode是我们熟知的编码字符集,而其实也只是字符集,只规定了符号的二级制代码,并没有规定如何存储。什么意思,比如一个“汉”字,Unicode编码为6C49,转化成二进制是1101100 01001001,这并不意味着计算机存储这个“汉”字就是这些二进制,还取决于具体的编码格式。所以通俗点说可以将Unicode理解为接口,而UTF-8和UTF-16理解为是接口的具体实现。
而上面这个“汉”字是如何存储的呢,比如我们采用UTF-8的方式来存储,UTF-8定义了两个简单的编码规则:

  • 对于单字节的符号,字节第一位设0,后面的是这个符号的Unicode编码。所以对于英文字母,UTF-8编码和ASCII码是一样的。
  • 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode码。

Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
----------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

上面是具体的Unicode转UTF-8的编码规则,根据这张表,可以很轻松的定位不同的Unicode编码对应的UTF-8的编码格式,由于“汉”的Unicode编码是6C49,落在了上面的第三个区间,所以UTF-8编码格式则是111001101011000110001001,即用三个字节来储存。

接着是实践

了解了上面的理论,就随手实践一下,看看结果。首先是想一下情景,上面问题中提到的是从流转成字符,然后从字符再转成流,所以模拟一下这个过程

    byte[] bytes = new byte[2];
    bytes[0] = (byte) 130;
    bytes[1] = 88;
    String test = new String(bytes);
    System.out.print(test);

上面这段代码模式的是字节流转成字符流的过程,注意byte[0]需要强转而1不需要,是因为Java默认一个byte的大小是0~127,最高位是符号位,而130由于大于127,则按照规定,是取互补的那个负数,即-126(如果说设置的是大于256,则一个字节无法表示,需要多个字节,这里面就需要更多的运算了,Java则是取最低的那8位)。然后new一个string出来,这里没有设置编码格式,Java默认使用的是UTF-8(可以通过System.out.println(Charset.defaultCharset());来打印默认的编码格式),打印出来的结果是�X,可以看见有两个字符,第一个是乱码,第二个识别出来是X,乱码是因为这个二进制流按照UTF-8的对照表找不到对应的字符,所以就无法显示。

这个时候再将这个字符流转成字节流试试呢

        String test = new String(bytes);
        byte[] bytes1 = test.getBytes();

debug一下看看结果,会发现byte1的数组长度不再是2了,而是4位


image.png

可以发现最后一位仍然是88,但是前三位是由130代表的字符流转变而来的,原因则也是因为130代表的Unicode字符大小落在了上面总结的编码表的第三区间,所以需要三个字节来存储,于是乎变成了三位,感兴趣的可以将这三个字节转成二进制看看是什么。

最后是结论

有了上面的一个小实践,这里明白了两个问题

  • 我在最开始那个流转成字符串,再由字符串转成流的问题中遇见了一种情况,我使用ASCII码编码的时候,图片无法显示出来,但是字节数转化前后是一样的;但是当我使用ISO_8859_1编码格式的时候,就可以显示出图片了。于是可以得到的结论是这个图片流里面的字符肯定都是那0-256范围中的字符,由于ASCII码表无法表示128-256,所以才会有字节数一样,但是结果不一样的情况。
  • 当我用UTF-8转变的时候,发现转换后的字节数组会比一开始的多很多,这可能是因为图片流中存在UTF-8无法识别的字符,比如,举个极端的例子,所有的8位二进制全部是10xxxxxx形式的,对应上面的UTF-8与Unicode转码表,就会发现无法对应,自然是得不到想要的结果的

再次象征性总结

这次码的也挺乱的,其实对Java中字节流与字符流的了解确实不够透彻,也源于对计算机原理的基础了解还不足,这次也算是个引子,后面还是要再多研究强化一下这方面的知识。

还有,这里参考了很多阮一峰大神的一篇关于编解码的文章,大神就是大神,讲的就是通俗易懂,虽然是十几年前的文章,但是仍然有很强的可读性

长路漫漫~~

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

推荐阅读更多精彩内容

  • 字符是用户可以读写的最小单位。计算机所能支持的字符组成的集合,就叫做字符集。字符集通常以二维表的形式存在。二维表的...
    刘惜有阅读 7,917评论 2 14
  • UTF-8 编码提供了一种简便而向后兼容的方法, 使得那种完全围绕 ASCII 设计的操作系统, 比如 Unix,...
    谢大见阅读 4,276评论 0 3
  • 编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换...
    x360阅读 2,393评论 1 20
  • 2014-03-17 18:04 3月16日,有机会在市工人文化宫聆听了64岁的中国籍犹太后裔、上海市侨联副主席沙...
    湘江隐士阅读 709评论 0 0
  • 回顾了一下《正念的奇迹》共读内容,正念强调当你看到一棵树、一朵花时,不要一眼万年、思绪纷飞,要能看到树是树,花是花...
    caucxiaom阅读 357评论 0 0