资讯专栏INFORMATION COLUMN

关于UTF-8所占用的字节以及UTF-8与GB2312之间的关系

tianhang / 1712人阅读

摘要:一规则下的汉字使用编码方式的文件,一个汉字所占用的是三个字节,而其他字母控制字符之类还是按照的编码方式,即占一个字节。

一、UTF-8规则下的汉字

  使用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("srcIO系统汉字");
        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]

**/

java的char类型,只有两个字节,在采用utf-8编码时,为什么可以存储汉字?:

  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来存放中文字符的。
详细参考回答java编译器编码和JVM编码问题?

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

  关于字符集与编码方式的关系:字符集就是字符的集合,如ASCII,GBK,BIG5,Unicode等,编码方式是即可理解为定义在字符集上的映射规则。
  对于unicode字符集,有utf8,utf16,utf32等多种编码方式,但对于其他字符集,只有一种默认的编码方式:比如,ASCII,GBK,GB2312等,不仅仅代表字符集,同时也代表了(默认的)的编码方式。

复制文件时的乱码

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

  而我自己在eclipse里面设置的编码是utf-8(对应字符集是不同的,所以)

  所以对于我们在windows记事本中创建的txt文件直接复制到eclipse中就会出现乱码,如下所示,因为两个文件采用了不同的字符集,发生了乱码:


而且经过测试这种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解码得到的文字:
"+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解码得到的文字:
"+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解码得到的文字:
"+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解码得到的文字:
"+Decode___Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312___UseUTF8);
    }
}
/**
将"天生我才必有用"按照GBK2312规则编码后得到的字节数组,再以UTF8解码得到的文字:
�����Ҳű�����
将上面的文字再反向以UTF8编码得到的字节数组,再按照GBK2312解码得到的文字:
锟斤拷锟斤拷锟揭才憋拷锟斤拷锟斤拷
-----------------------------------------------分割线-----------------------------------------------
将"天生我才必有用"按照UTF8规则编码后得到的字节数组,再以GBK2312解码得到的文字:
澶╃������蹇�����
将上面的文字再反向以GBK2312编码得到的字节数组,再按照UTF8解码得到的文字:
天�??????�?????
**/
复制文字时可能不会出现乱码

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

扩展文章:Java编码问题原因以及解决

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72377.html

相关文章

  • 细说Unicode(一) Unicode初认识

    摘要:所以中国人自己创造了一种字符编码,每个汉字和符号用两个字节来表示。第一个字节称为高位字节,第二个字节称为低位字节。而目前为止我们使用最广泛的中文编码还是。 网站开发中经常会被乱码问题困扰。知道文件编码错误会导致乱码,但对其中的原理却知之甚少。偶然从某篇文章了解了Unicode,发现从这条线出发也牵引出了一系列缺失的知识点。通过研读文章,基本了解了一些以前不明白的问题,所以整理了几篇,从...

    loostudy 评论0 收藏0
  • PyTips 0x09 - Python 中 Unicode 正确用法

    摘要:只包含了个基本拉丁字母阿拉伯数目字和英式标点符号一共个字符,因此只需要不占满一个字节就可以存储,而则涵盖的数据除了视觉上的字形编码方法标准的字符编码外,还包含了字符特性,如大小写字母,共可包含个字符,而到现在只填充了其中的个位置。 项目地址:https://git.io/pytips 0x07 和 0x08 分别介绍了 Python 中的字符串类型(str)和字节类型(byte),以及...

    shenhualong 评论0 收藏0
  • 编码-1

    摘要:关于的参考知乎上的一个回答传送门以我自己的理解就是首先得分清楚编码问题在不同的环境中,编码是不同的。但是如果换成的是知乎的话,则表示的是将这个汉字用编码的形式存放。 文章启发来源: cnblogs 阮一峰 知乎 字符编码方式 note from wiki: 从维基百科上得到的一些理解,一个字符的unicode编码是确定的,但是在传输过程中,由于不同系统平台的设计不一致,所以对uni...

    IamDLY 评论0 收藏0
  • 字符编码那些事

    摘要:字符编码的那些事前言之前看到中对扩展了不少新特性,字符串操作更加友好,比如,,。其中涉及到不少字符编码的知识,为了更好理解这些新特性,本文对字符编码相关知识做一个较全面的梳理和总结。 字符编码的那些事 前言 之前看到ES6中对String扩展了不少新特性,字符串操作更加友好,比如u{1f914},codePointAt(),String.fromCodePoint()。其中涉及到不少字...

    shadajin 评论0 收藏0
  • 字符编码那些事

    摘要:字符编码的那些事前言之前看到中对扩展了不少新特性,字符串操作更加友好,比如,,。其中涉及到不少字符编码的知识,为了更好理解这些新特性,本文对字符编码相关知识做一个较全面的梳理和总结。 字符编码的那些事 前言 之前看到ES6中对String扩展了不少新特性,字符串操作更加友好,比如u{1f914},codePointAt(),String.fromCodePoint()。其中涉及到不少字...

    kviccn 评论0 收藏0

发表评论

0条评论

阅读需要支付1元查看
<