为什么要编码
不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言。由于人类的语言有太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元—— byte 来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解。我们可以把计算机能够理解的语言假定为英语,其它语言要能够在计算机中使用必须经过一次翻译,把它翻译成英语。这个翻译的过程就是编码。所以可以想象只要不是说英语的国家要能够使用计算机就必须要经过编码。这看起来有些霸道,但是这就是现状,这也和我们国家现在在大力推广汉语一样,希望其它国家都会说汉语,以后其它的语言都翻译成汉语,我们可以把计算机中存储信息的最小单位改成汉字,这样我们就不存在编码问题了。
所以总的来说,编码的原因可以总结为:
计算机中存储信息的最小单元是一个字节即 8 个 bit,所以能表示的字符范围是 0~255 个
(8位可能是0或者1,有 2 * 2 * 2 *2 * 2 * 2 * 2 * 2 = 256 种可能 )
,人类要表示的符号太多,无法用一个字节来完全表示。
要解决这个矛盾必须需要一个新的数据结构 char,从 char 到 byte 必须编码 。
如何“翻译”
明白了各种语言需要交流,经过翻译是必要的,那又如何来翻译呢?计算中提拱了多种翻译方式,常见有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。它们都可以被看作为字典,它们规定了转化的规则,按照这个规则就可以让计算机正确的表示我们的字符。目前的编码格式很多,例如 GB2312、GBK、UTF-8、UTF-16 这几种格式都可以表示一个汉字,那我们到底选择哪种编码格式来存储汉字呢?这就要考虑到其它因素了,是存储空间重要还是编码的效率重要。根据这些因素来正确选择编码格式,下面简要介绍一下这几种编码格式。
常用编码
- ASCII 码
((American Standard Code for Information Interchange): 美国信息交换标准代码)
用一个字节的低 7 位表示,总共有 128 个,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。
标准的ASCII码用一个字节中的7为二进制码来表示一个字符,这个编码的字符就是ASCII码值,从0000000到1111111公有128个编码,可用来表示128个字符!” “随着计算机的发展和深入,7位的字符有时已不够用,为此国际标准化组织又制定了ISO2022标准,它在保持ISO646兼容的基础上,规定了扩充ASCII字符集为8位代码,可表示256个字符
https://blog.csdn.net/ericliutyy/article/details/5657601
- ISO8859-1:国际编码
可描述任何文字信息,中文需要转码
128 个字符显然是不够用的,于是 ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,它们是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵盖了大多数西欧语言字符,所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符
- GB2312/GBK:中文国际编码
- GB2312:只包含简体中文
它的全称是《信息交换用汉字编码字符集 基本集》,它是双字节编码,总的编码范围是 A1-F7,其中从 A1-A9 是符号区,总共包含 682 个符号,从 B0-F7 是汉字区,包含 6763 个汉字。 - GBK:包含简体中文和繁体中文两种
全称叫《汉字内码扩展规范》,是国家技术监督局为 windows95 所制定的新的汉字内码规范,它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。 也是双字节。
- UNICODE:十六进制编码
传递字符信息时造成传输的数据较大
ISO 试图想创建一个全新的超语言字典,世界上所有的语言都可以通过这本字典来相互翻译。可想而知这个字典是多么的复杂,关于 Unicode 的详细规范可以参考相应文档。
- UTF编码:
Unicode Transformation Format是一种UNICODE的长度可变编码,常见UTF-8
还有UTF-8/16/32
实际项目中,往往都以UTF-8编码为主,写代码时建议将文件编码统一设置为UTF-8
-
UTF-16
说到 UTF 必须要提到 Unicode(Universal Code 统一码),ISO 试图想创建一个全新的超语言字典,世界上所有的语言都可以通过这本字典来相互翻译。可想而知这个字典是多么的复杂,关于 Unicode 的详细规范可以参考相应文档。
UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。 -
UTF-8
UTF-16 统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而 UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由 1~6 个字节组成。
编码发展总结
1) ANSI的出现
他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为”字节“。再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为”计算机“。
开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。 他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作:遇上0×10, 终端就换行;遇上0×07, 终端就向人们嘟嘟叫;遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,于是就把这些0×20以下的字节状态称为”控制码”。他们又把所有的空 格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号
一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉
很好,于是大家都把这个方案叫做 ANSI 的”Ascii”编码
2) GB2312/GBK
中国人民通过对 ASCII 编码的中文扩充改造,产生了 GB2312 编码,可以表示6000多个常用汉字。
汉字实在是太多了,包括繁体和各种字符,于是产生了 GBK 编码,它包括了 GB2312 中的编码,同时扩充了很多。
中国是个多民族国家,各个民族几乎都有自己独立的语言系统,为了表示那些字符,继续把 GBK 编码扩充为 GB18030 编码。
3) UNICODE/UTF-8/UTF-16/UTF-32
每个国家都像中国一样,把自己的语言编码,于是出现了各种各样的编码,如果你不安装相应的编码,就无法解释相应编码想表达的内容。
终于,有个叫 ISO 的组织看不下去了。他们一起创造了一种编码 UNICODE ,这种编码非常大,大到可以容纳世界上任何一个文字和标志。所以只要电脑上有 UNICODE 这种编码系统,无论是全球哪种文字,只需要保存文件的时候,保存成 UNICODE 编码就可以被其他电脑正常解释。
UNICODE 在网络传输中,出现了两个标准 UTF-8 和 UTF-16,分别每次传输 8个位和 16个位。于是就会有人产生疑问,UTF-8 既然能保存那么多文字、符号,为什么国内还有这么多使用 GBK 等编码的人?因为 UTF-8 等编码体积比较大,占电脑空间比较多,如果面向的使用人群绝大部分都是中国人,用 GBK 等编码也可以。
Unicode和UTF区别
Unicode 是「字符集」
UTF-8 是「编码规则」
字符集:为全世界每一个字符分配一个唯一的id(学名码位,码点,Code Point)
编码规则:将码位转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)
UTF-8/16/32区别
-
UTF8:存在单字节编码,兼容ASCII;当编码为一个字节,则设最高比特位为0;
当编码超过一个字节,则需要几个字节,就在第一个字节从最高位开始令连续的几个比特位为1,之后的字节最高位为10。
可变宽度编码,向后兼容ASCII。ASCII字符(U + 0000至U + 007F)占1个字节,代码点U + 0080至U + 07FF占2个字节,代码点U + 0800至U + FFFF占3个字节,代码点U + 10000至U + 10FFFF占用4个字节。适合英语文本,不适合亚洲文本。
正常英文1,中文3 -
UTF16:使用2或4个字节进行存储。对于Unicode编号范围在0~FFFF之间的字符,统一用两个字节存储,无需字符转换,直接存储Unicode编号。
对于Unicode字符编号在10000-10FFFF之间的字符,UTF16用四个字节存储,简单说就是:将Unicode字符编号(3字节)分为两部分,高位部分(Unicode字符编号中占1.5个字节)用一个值介于 D800-DBFF (110110yy yyyyyyyy,y为0/1)之间的双字节存储,低位部分用一个值介于 DC00-DFFF (110111xx xxxxxxxx,x为0/1)的双字节存储。而介于D800-DFFF之间的编码在Unicode中是预留的,不安排字符,如果Unicode中有字符的编号是这之间的值,会引发冲突和歧义,很有可能一个不常见字符(存储为四个字节)最后被读成两个常见字符(存储为两个字节)。
可变宽度编码。代码点U + 0000至U + FFFF占用2个字节,代码点U + 10000至U + 10FFFF占用4个字节。对英语文本不利,对亚洲文本不利。
正常英文4,中文4** -
UTF32:用固定长度的字节存储字符编码,不管Unicode字符编号需要几个字节,全部都用4个字节存储,直接存储Unicode编号。无需经过字符编号向字符编码的转换步骤,提高效率,用空间换时间。
固定宽度编码。所有代码点占用四个字节。巨大的内存消耗,但是操作起来很快。很少用
固定英文4,中文4**
各编码所占字节
英文 | 中文 | 备注 | |
---|---|---|---|
ASCII | 1 | - | 用一个字节的低7位,共128个 |
ISO-8859-1 | 1 | - | 一个字节的8位都使用,共256个 |
GB2312 | 1 | 2 | 只表示简体中文,兼容ASCII |
GBK | 1 | 2 | 表示简体中文和繁体中文,兼容GB2312,兼容ASCII |
GB18030 | 1 | 2 | 对GBK的扩展,表示多个民族语言,兼容GBK/GB2312/ASCII |
UTF-8 | 1 | 3 | 可变宽度编码。有1/2/3/4种可能。正常英文1,中文3 |
UTF-16 | 4 | 4 | 可变宽度编码。英文,中文占2个或4个字节,正常情况占4个 |
UTF-32 | 4 | 4 | 固定宽度编码,所有代码点占用四个字节。巨大的内存消耗,但是操作起来很快。 |
最准确方法:
"需要判断字符串".getBytes("编码格式").length
单个英文字母:
字节数:2;编码:UTF-16BE // tbd 不清楚含义
字节数:2;编码:UTF-16LE
单个中文汉字:
字节数:2;编码:UTF-16BE
字节数:2;编码:UTF-16LE
java中获得制定编码的string所占字节方式
//getBytes:强行改变编码
public static void main(String[] args) throws UnsupportedEncodingException {
System.out.println("A".getBytes(StandardCharsets.US_ASCII).length+" "+"嗨".getBytes("ASCII").length);
// 1 1
System.out.println("A".getBytes("ISO8859-1").length+" "+"嗨".getBytes("ISO8859-1").length);
// 1 1
System.out.println("A".getBytes("GB2312").length+" "+"嗨".getBytes("GB2312").length);
// 1 2
System.out.println("A".getBytes("GBK").length+" "+"嗨".getBytes("GBK").length);
// 1 2
System.out.println("A".getBytes("GB18030").length+" "+"嗨".getBytes("GB18030").length);
// 1 2
System.out.println("A".getBytes("UTF-8").length+" "+"嗨".getBytes("UTF-8").length);
// 1 3
System.out.println("A".getBytes("UTF-16").length+" "+"嗨".getBytes("UTF-16").length);
// 4 4
System.out.println("A".getBytes("UTF-32").length+" "+"嗨".getBytes("UTF-32").length);
// 4 4
}
获得当前系统环境属性中文件编码
public class Test {
public static void main(String[] args) {
System.getProperties().list(System.out);
}
}
中有file.encoding=UTF-8
getProperties返回一个Properties对象
getProperties取得当前系统属性
list列出
list()参数,输出到参数代表的流中
参照:
https://www.zhihu.com/question/23374078
https://blog.csdn.net/m0_37732829/article/details/80774400
https://blog.csdn.net/yuxiangaaaaa/article/details/78060661
https://blog.csdn.net/cuiyaoqiang/article/details/52056175