平时工作对资源的操作都是面向字节流的,然而数据资源根据不同的字节编码转为字节时,它们的内容是不一样,容易造成乱码问题 两种出现乱码场景 encode和decode使用的字符编码不一致:资源使用UTF-8编码,而在代码里却使用GBK解码打开使用字节流读取字节数不符合字符规定字长:字符是由字节组成的,比如“程”的utf-8格式是三个字节;如果在InputStream里以每两个字节读取流,再转为String(java默认编码是utf-8),此时会出现乱码(半个中文,你猜是什么)ByteArrayInputStream in = new ByteArrayInputStream("程序大法好".getBytes());byte[] buf = new byte[2]; //读取流的两个字节in.read(buf); //读取数据System.out.println(new String(buf)); //乱码---result---- ?//乱码 - 乱码场景1,知道资源的字符编码,就可以使用对应的字符编码来解码解决
- 乱码场景2,可以一次性读取所有字节,再一次性编码处理 。但是对于大文件流,这是不现实的,因此有了字符流的出现
- 字节流使用InputStreamReader、OutputStreamReader转化为字符流,其中可以指定字符编码,再以字符为单位来处理,可解决乱码
InputStreamReader reader =new InputStreamReader(inputStream, StandardCharsets.UTF_8);4 字符集和字符编码的概念区分
- 字符集和字符编码的关系,字符集是规范,字符编码是规范的具体实现;字符集规定了符号和二进制代码值的唯一对应关系,但是没有指定具体的存储方式;
- unicode、ASCII、GB2312、GBK都是字符集;其中ASCII、GB2312、GBK既是字符集也是字符编码;注意不混淆这两者区别;而unicode的具体实现有UTF-8,UTF-16,UTF-32
- 最早出现的ASCII码是使用一个字节(8bit)来规定字符和二进制映射关系,标准ASCII编码规定了128个字符,在英文的世界,是够用的 。但是中文,日文等其他文字符号怎么映射呢?因此其他更大的字符集出现了
- unicode(统一字符集),早期时它使用2个byte表示1个字符,整个字符集可以容纳65536个字符 。然而仍然不够用,于是扩展到4个byte表示一个字符,现支持范围是U+010000~U+10FFFF
- unicode是两个字节的说法是错误的;UTF-8是变字长的,需要用1~4个字节存储;UTF-16一般是两个字节(U+0000~U+FFFF范围),如果遇到两个字节存不下,则用4个字节;而UTF-32是固定四个字节
- unicode表示的字符,会用“U+”开头,后面跟着十六进制的数字,如“字”的编码就是U+5B57
- UTF-8 编码和unicode字符集
范围 Unicode(Binary) UTF-8编码(Binary) UTF-8编码byte长度 U+0000~U+007F 00000000 00000000 00000000 0XXXXXXX 0XXXXXX 1 U+0080~U+07FF 00000000 00000000 00000YYY YYXXXXXX 110YYYYY 10XXXXXX 2 U+0800~U+FFFF 00000000 00000000 ZZZZYYYY YYXXXXXX 1110ZZZZ 10YYYYYY 10XXXXXX 3 U+010000~U+10FFFF 00000000 000AAAZZ ZZZZYYYY YYXXXXXX 11110AAA 10ZZZZZZ 10YYYYYY 10XXXXXX 4
- 程序是分内码和外码,java的默认编码是UTF-8,其实指的是外码;内码倾向于使用定长码,和内存对齐一个原理,便于处理 。外码倾向于使用变长码,变长码将常用字符编为短编码,罕见字符编为长编码,节省存储空间与传输带宽
- JDK8的字符串,是使用char[]来存储字符的,char是两个字节大小,其中使用的是UTF-16编码(内码) 。而unicode规定的中文字符在U+0000~U+FFFF内,因此使用char(UTF-16编码)存储中文是不会出现乱码的
- JDK9后,字符串则使用byte[]数组来存储,因为有一些字符一个char已经存不了,如emoji表情字符,使用字节存储字符串更容易拓展
- JDK9,如果字符串的内容都是ISO-8859-1/Latin-1字符(1个字符1字节),则使用ISO-8859-1/Latin-1编码存储字符串,否则使用UTF-16编码存储数组(2或4个字节)
System.out.println(Charset.defaultCharset()); //输出java默认编码for (byte item : "程序".getBytes(StandardCharsets.UTF_16)) {System.out.print("[" + item + "]");}System.out.println("");for (byte item : "程序".getBytes(StandardCharsets.UTF_8)) {System.out.print("[" + item + "]");}----result----UTF-8//java默认编码UTF-8[-2][-1][122][11][94][-113] //UTF_16:6个字节?[-25][-88][-117][-27][-70][-113] //UTF_8:6个字节 正常 - “程序”的UTF-16编码竟是输出6个字节,多出了两个字节,这是什么情况?再试试一个字符的输出
for (byte item : "程".getBytes(StandardCharsets.UTF_16)) {System.out.print("[" + item + "]");}---result--[-2][-1][122][11] - 可以看出UTF-16编码的字节是多了[-2][-1]两个字节,十六进制是0xFEFF 。而它用来标识编码顺序是Big endian还是Little endian 。以字符'中'为例,它的unicode十六进制是4E2D,存储时4E在前,2D在后,就是Big endian;2D在前,4E在后,就是Little endian 。FEFF表示存储采用Big endian,FFFE表示使用Little endian
推荐阅读
-
1818黄金眼|杭州女子报警!凌晨收到弟弟转帐,之后人就失联了…
-
浪子归家 结果蒙了,快递小哥被投诉后怒怼女顾客:1元就想送货上门
-
「国家卫健委」国家卫健委:2月26日新增确诊病例433例 累计死亡病例2744例
-
获得北京小车指标后,能否将父母赠与的已上外地牌照的车上北京牌照
-
勒布朗·詹姆斯|至少7500万?掘金锋线大将跳出合同,防守詹姆斯让其身价暴涨
-
-
台积电|画风突变!台积电不愿意妥协,接连做出3个动作
-
护肤品|一直回购的8种国货护肤品,便宜又好用,连美白精华都不超过百元
-
肝病医生田飞@小三阳患者竟成了肝癌的爆发人群?想要避免你需要这样做
-
林心如■林心如晒精致早餐,运动帽配墨镜低调大方,皮肤超好惹人羡
-
-
甜乐影视|便宜不输大牌,51岁王菲成冻龄女神?分享自用的“抗衰老”面霜
-
保险|业绩承压财报又遭询问,华凯保险深陷发展“阴霾”
-
-
雪梨|1986年, 徐少强欺骗雪梨未婚生子后转身抛弃, 今雪梨“报复”真高级
-
王者荣耀|王者荣耀:在当前赛季排位赛中双打野最吃香,而坦边打野最【稳】
-
「游戏电竞小狐美」绝版光环外观爆料,还不如去年好看,DNF体验服:520表白盒子实测
-
-
叮当快药TB|这13种快速解压法值得一试,压力大、焦虑迷茫…面对心理问题
-
马鲁兰迪■野火肆虐又面临干旱,澳大利亚这小镇酒比水还要多