博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于UTF-8所占用的字节以及UTF-8与GB2312之间的关系
阅读量:6581 次
发布时间:2019-06-24

本文共 5639 字,大约阅读时间需要 18 分钟。

一、UTF-8规则下的汉字

clipboard.png

  使用UTF-8编码方式的文件,一个汉字所占用的是三个字节(byte),而其他字母控制字符之类还是按照ASCII的编码方式,即占一个字节。为了在解码的时候区分,经对的测试发现,在汉字所占用的三个字节当中:

  • 一个字节转换为10进制的范围为:[-28 ~ -23]
  • 第二个字节和第三个字节的10进制范围均为:[-128 ~ -65]

       这样在比如new String(byte[] b)类的函数在解码的时候,就能够通过字节为正来判断是ASCII字符,如是在[-28 ~ -23]范围内则是一个汉字的开始,并且后面还有两个[-128 ~ -65]范围的字节时,就会把这三个字节转换为一个汉字。

  测试代码如下:

public class FileInputStreamTest{    public static void main(String[] args) throws IOException    {        FileInputStream fis = new FileInputStream("src\\IO系统\\汉字");        List
> listTreeSet =new ArrayList<>(); for(int i=0;i<3;i++) listTreeSet.add(new TreeSet
()); byte[] bbuf = new byte[3]; int hasRead = 0; while ((hasRead = fis.read(bbuf)) > 0 ) { //System.out.println(new String(bbuf , 0 , hasRead ));//可用来查看当前bbuf字节数组表示的一个汉字 for(int i=0;i<3;i++) if(bbuf[i]<0) { //这里用了这一句是因为eclispe里面一行有长度限制,所以将汉字分成了三行,因为每行末换行符和回车符各占一个字节,再在下行初加上一个字母比如a就可以使得三个字符被一起读走而不影响汉字字节的读取顺序,其他的每三个字符仍然是一个汉字 listTreeSet.get(i).add((int) bbuf[i]); } } for(int i=0;i<3;i++) System.out.println("第"+(i+1)+"个字节:"+listTreeSet.get(i)); fis.close(); }}/**第1个字节:[-28, -27, -26, -25, -24, -23]第2个字节:[-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65]第3个字节:[-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65]**/

clipboard.png
  Unicode是一种字符集(charset),即字符的集合。UTF-8与UTF-16都是是一种建立在Unicode字符集上面的编码方式(encoding),是将Unicode字符集里的字符转换成具体的二进制流。所不同的是在UTF-8和UTF-16当中,将Unicode中一个汉字编码成二进制后,分别是三个字节大小和两个字节大小。
  在一个Java文件(例如该文件为UTF-8编码)里面写上这样一句话char a = '猿';如图所示,编译后生成的class文件是UTF-8的,不过是modified的(可能与通常的utf-8的机制有些许区别),一个汉字仍然是占三个字节的,但关键在于运行的时候会将其转换为UTF-16编码方式下的,这样在运行的时候char类型当中仍然只放有两个字节,所以java编译器也是允许用char来存放中文字符的。
详细参考回答

二、UTF-8与GB2312之间的关系

  关于字符集与编码方式的关系:字符集就是字符的集合,如ASCII,GBK,BIG5,Unicode等,编码方式是即可理解为定义在字符集上的映射规则。

  对于unicode字符集,有utf8,utf16,utf32等多种编码方式,但对于其他字符集,只有一种默认的编码方式:比如,ASCII,GBK,GB2312等,不仅仅代表字符集,同时也代表了(默认的)的编码方式。

复制文件时的乱码

  在win10中默认的字符集是gb2312,。用notepad++随便打开一个txt文件,在右下角就能看到字符集

clipboard.png
  而我自己在eclipse里面设置的编码是utf-8(对应字符集是不同的,所以)
clipboard.png
  所以对于我们在windows记事本中创建的txt文件直接复制到eclipse中就会出现乱码,如下所示,因为两个文件采用了不同的字符集,发生了乱码:
clipboard.png
clipboard.png
而且经过测试这种GB2312与UTF-8之间的相互转换的效果是不可逆的(因为发生了信息丢失),代码如下:

public class TestCharset {    public static void main(String[] args) throws IOException {        Charset utf8 = StandardCharsets.UTF_8;        Charset gbk2312 = Charset.forName("GB2312");        //将某段文字以gb2312编码后得到的字节数组,再以utf-8进行解码得到的文字是乱码,并且这段乱码中丢失了信息。        //所以不能再转换回utf-8了        ByteBuffer BytesExpressTextOnGBK2312 = gbk2312.encode("天生我才必有用");        CharBuffer Decode_BytesExpressTextOnGBK2312_UseUTF8 = utf8.decode(BytesExpressTextOnGBK2312);        System.out.println("将'天生我才必有用'按照GBK2312规则编码后得到的字节数组,再以UTF8解码得到的文字:\n"+Decode_BytesExpressTextOnGBK2312_UseUTF8);                ByteBuffer Encode__Decode_BytesExpressTextOnGBK2312_UseUTF8__UseUTF8 = utf8.encode(Decode_BytesExpressTextOnGBK2312_UseUTF8);        CharBuffer Decode___Encode__Decode_BytesExpressTextOnGBK2312_UseUTF8__UseUTF8___UseGBK2312 = gbk2312.decode(Encode__Decode_BytesExpressTextOnGBK2312_UseUTF8__UseUTF8);        System.out.println("将上面的文字再反向以UTF8编码得到的字节数组,再按照GBK2312解码得到的文字:\n"+Decode___Encode__Decode_BytesExpressTextOnGBK2312_UseUTF8__UseUTF8___UseGBK2312);                System.out.println("-----------------------------------------------分割线-----------------------------------------------");                //同样 将某段文字以utf8编码后得到的字节数组,再以utf-8进行解码得到的文字是乱码,并且这段乱码中丢失了信息        //逆向后大部分文字也不能恢复,不过比上面的完全不能恢复好了一些        ByteBuffer BytesExpressTextOnUTF8 = utf8.encode("天生我才必有用");        CharBuffer Decode_BytesExpressTextOnUTF8_UseGBK2312 = gbk2312.decode(BytesExpressTextOnUTF8);        System.out.println("将'天生我才必有用'按照UTF8规则编码后得到的字节数组,再以GBK2312解码得到的文字:\n"+Decode_BytesExpressTextOnUTF8_UseGBK2312);                ByteBuffer Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312= gbk2312.encode(Decode_BytesExpressTextOnUTF8_UseGBK2312);        CharBuffer Decode___Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312___UseUTF8 = utf8.decode(Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312);        System.out.println("将上面的文字再反向以GBK2312编码得到的字节数组,再按照UTF8解码得到的文字:\n"+Decode___Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312___UseUTF8);    }}/**将'天生我才必有用'按照GBK2312规则编码后得到的字节数组,再以UTF8解码得到的文字:�����Ҳű�����将上面的文字再反向以UTF8编码得到的字节数组,再按照GBK2312解码得到的文字:锟斤拷锟斤拷锟揭才憋拷锟斤拷锟斤拷-----------------------------------------------分割线-----------------------------------------------将'天生我才必有用'按照UTF8规则编码后得到的字节数组,再以GBK2312解码得到的文字:澶╃������蹇�����将上面的文字再反向以GBK2312编码得到的字节数组,再按照UTF8解码得到的文字:天�??????�?????**/

复制文字时可能不会出现乱码

  那为什么我们直接从txt复制文字过去就不会变成乱码呢?

  :对于复制粘贴文字而言,虚拟机软件(比如我们的java虚拟机)、远程主机软件都会有一个「介于两系统之间的」剪贴板,「连接起」这两个系统的各自剪贴板,并做一些编码格式转换的工作。这样我们复制过去的文字就肯定不会出现乱码了。
  按照这个说法,推测就是说eclispe当中会有一个针对windows系统的剪贴板,比如当我们从txt中复制过去的文字(实际上应该是字节数组)与eclispe中的我们设置的编码不同的话,先会按照txt原本的编码规则对字节数组进行解码得到文字,然后再按照eclispe我们设置的编码规则进行编码,这然后就可以粘贴过去而不出错了。但也只是一种推测可能原理不是这样,但肯定的是做了一些编码转换的工作。

扩展文章:

转载地址:http://viino.baihongyu.com/

你可能感兴趣的文章
在windows上秒开应用程序
查看>>
【20180611】MySQL OOM
查看>>
memcached
查看>>
Python面向对象编程(一)
查看>>
决心书
查看>>
如何把图片上的文字转换成word?
查看>>
7z命令行
查看>>
C语言编程实现 输入一个非负整数,返回组成它的数字之和(递归方法)
查看>>
c3p0
查看>>
redis cluster 集群搭建(增、删、改、查) :5.0.2
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
引号-下划线,连接多个变量
查看>>
游戏LOGO它应该长什么样?
查看>>
我的友情链接
查看>>
Office365 之分配、回收License工具
查看>>
38线程1-Thread-local-Timer
查看>>
为Exchange server 2013 申请多域名证书
查看>>
处理svn的 File '/aa' is out of date
查看>>
解决 Ubuntu 16.04 LTSSublime text3中文问题
查看>>