摘要:一规则下的汉字使用编码方式的文件,一个汉字所占用的是三个字节,而其他字母控制字符之类还是按照的编码方式,即占一个字节。
一、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编码问题?
关于字符集与编码方式的关系:字符集就是字符的集合,如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,发现从这条线出发也牵引出了一系列缺失的知识点。通过研读文章,基本了解了一些以前不明白的问题,所以整理了几篇,从...
摘要:只包含了个基本拉丁字母阿拉伯数目字和英式标点符号一共个字符,因此只需要不占满一个字节就可以存储,而则涵盖的数据除了视觉上的字形编码方法标准的字符编码外,还包含了字符特性,如大小写字母,共可包含个字符,而到现在只填充了其中的个位置。 项目地址:https://git.io/pytips 0x07 和 0x08 分别介绍了 Python 中的字符串类型(str)和字节类型(byte),以及...
阅读 1435·2021-09-02 19:23
阅读 1586·2021-08-11 11:19
阅读 640·2019-08-30 15:55
阅读 1651·2019-08-30 12:50
阅读 2240·2019-08-30 11:23
阅读 2179·2019-08-29 13:13
阅读 1499·2019-08-28 18:13
阅读 3343·2019-08-26 11:53