2MUCH

认识编码

2022-12-19


概念

字符集 vs. 编码方式:编码方式就是将字符集里的字符映射成可以被计算机识别的若干位二进制来表示。

对于一个字符集来说要正确编码转码一个字符需要三个关键元素:字库表(character repertoire)、编码字符集(coded character set)、字符编码(character encoding form)。 其中:字库表是一个相当于所有可读或者可显示字符的数据库,字库表决定了整个字符集能够展现表示的所有字符的范围。编码字符集,即用一个编码值code point来表示一个字符在字库中的位置。字符编码,将编码字符集和实际存储数值之间的转换关系。一般来说都会直接将code point的值作为编码后的值直接存储。例如在ASCII中A在表中排第65位,而编码后A的数值是0100 0001也即十进制的65的二进制转换结果。

https://www.cnblogs.com/faetbwac/p/16355273.html

字符集和字符编码一般都是成对出现的,如ASCII、IOS-8859-1、GB2312、GBK,都是即表示了字符集又表示了对应的字符编码。Unicode比较特殊,有多种字符编码(UTF-8,UTF-16等)

https://www.cnblogs.com/chiguozi/p/5860364.html

字符集对比

字符集名称 字符集范围 编码格式 备注
ASCII 英文或其他西欧语言 单字节
GB2312 汉字 双字节
GBK 更多汉字(21886个汉字和图形符号) 双字节 完全向下兼容GB2312
GB18030 支持简体中文、繁体中文 藏文、蒙文、维吾尔文等主要的少数民族文字 可变多字节(1、2、4字节) 完全兼容GB2312,基本兼容GBK

常见的支持中文的字符集的对比

常用的支持中文的字符集有:

  1. GB2312
  2. GBK
  3. GB18030
  4. BIG5
  5. HZ
  6. Unicode

其中,GB2312、GBK、GB18030是中国国家标准,主要用于简体中文的编码,其中GB2312最早发布于1981年,GBK在GB2312的基础上扩展了更多的中文字符,GB18030则是GB2312和GBK的超集,包含更多的汉字和非汉字字符。可以理解为:GB18030> GBK> GB2312

BIG5是中国台湾的中文字符集,主要用于繁体中文的编码。

HZ是中华人民共和国邮电部于1995年发布的一种编码规范,主要用于在邮件中传输中文字符。

Unicode是一种国际化字符集,支持世界上几乎所有的字符,其中包括中文字符。在Unicode中,中文字符主要有两种编码方式,分别是UTF-8和UTF-16。其中,UTF-8是一种可变长度的编码方式,可以根据字符的不同选择1到4个字节编码,是现在互联网应用中最常用的字符集之一。

ASCII

American Standard Code for Information Interchange,美国标准信息交换代码

主要用来表示英文或其他西欧语言(在扩展版中)

编码格式

GB2312

GB2312(或称GB2312-80,80表示1980年发布)为了支持中文的表示,国人发明的。它是中华人民共和国国家标准简体中文字符集。基本满足汉字的使用,但是对于人名、古汉语等方面出现的罕用字和繁体字,GB 2312不能处理,因此后来GBK及GB 18030汉字字符集相继出现以解决这些问题。

分区表示:GB 2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。

举例来说,“啊”字是GB 2312之中的第一个汉字,它的区位码就是1601。 10–15区及88–94区则未有编码。

https://www.cnblogs.com/chiguozi/p/5860364.html

编码格式

每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。 “高位字节”使用了0xA1–0xF7(把01–87区的区号加上0xA0),“低位字节”使用了0xA1–0xFE(把01–94加上0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0–0xF7,“低位字节”的范围是0xA1–0xFE。

GBK

GBK(Chinese Internal Code Extension Specification),汉字内码扩展规范

K:Kuo Zhan(扩展)的K

GB 2312-80只收录6763个汉字,有不少汉字,如部分在GB 2312-80推出以后才简化的汉字(如“啰”),部分人名用字(如中国前总理朱镕的“镕”字),台湾及香港使用的繁体字,日语及朝鲜语汉字等,并未有收录在内。GBK对GB 2312-80进行扩展, 总计拥有 23940 个码位,共收录21886个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号883 个。

编码格式

GBK 亦采用双字节表示,总体编码范围为8140-FEFE,首字节在81-FE 之间,尾字节在40-FE 之间。GBK向下完全兼容GB2312-80编码。支持GB2312-80编码不支持的部分中文姓,中文繁体,日文假名,还包括希腊字母以及俄语字母等字母。不过这种编码支持韩国字,也是其在实际使用中与unicode编码相比欠缺的部分。

GB18030

全称《信息技术 中文编码字符集》,是中华人民共和国国家标准所规定的变长多字节字符集。其对GB 2312-1980完全向后兼容,与GBK基本向后兼容,并支持Unicode(GB 13000)的所有码位。GB 18030-2005共收录汉字70,244个。

特点:

https://www.cnblogs.com/chiguozi/p/5860364.html

GB 18030主要有以下特点:

编码格式

Unicode

称为万国码国际码统一码单一码。为了解决使用不同国家的编码格式所造成的乱码问题,国际编码格式Unicode应运而生。

编码格式

UCS-2版本:双字节编码;UCS-4版本:4字节编码

但是,有个问题:如果都是按照统一长度的编码,那么英文本身只需用1字节表示,却要统一用2/4字节表示,很浪费传输和存储空间。于是,一些基于Unicode的编码方式就应运而生。Unicode是一个字符集,给所有的字符分配一个码值。这里面只是分配码值,而不是最后的编码实现,编码的实现有很多不同的方式如utf-8utf-16utf-32。简单来说,Unicode只是业界标准,但是具体一个字符占多少字节,要取决于如UTF-8等的编码方式。

关于utf-8utf-16utf-32三者的优劣势,也参考文章:

https://www.cnblogs.com/wpcockroach/p/3907324.html

UTF-8最适合用来作为字符串网络传输的编码格式。UTF-16最适合当作本地字符串编码格式。如果定义好了网络传输协议,那么UTF-16也非常合适当作网络字符串传输的编码格式,特别是中文等远东地区字符集。比起UTF-8来说,节省一点点流量。UTF-32没什么特殊癖好或者需求的话,暂时还用不上。

UTF-8

UTF,Unicode Transformation Format。在表示纯英文时,Unicode比ASCII码多了一倍,很不划算。于是有了变长表示的UTF-8。背景介绍可参考: http://utf8everywhere.org/zh-cn#

UTF-8编码将Unicode字符按数字大小编码为1-6个字节,英文字母被编码成一个字节,常用汉字被编码成三个字节,如果你编译的文本是纯英文的,那么用UTF-8就会非常节省空间。

image-20221220090626617

img

UTF-16

也是变长编码,每个字符编码为2或4字节。

UTF-32

固定长度的编码,始终占用 4 个字节。

因此,只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因为它们没有单字节编码。

关于Unicode文件的BOM

https://www.cnblogs.com/flyingeagle/articles/9825302.html

BOM (byte-order mark),即字节顺序标记,它是插入到以UTF-8、UTF16或UTF-32编码Unicode文件开头的特殊标记,用来识别Unicode文件的编码类型。对于UTF-8来说,BOM并不是必须的,因为BOM用来标记多字节编码文件的编码类型和字节顺序(big-endian或little-endian)。

该选择带BOM还是不带BOM的UTF8编码呢?首先需要认识到的是,带BOM是微软这边的做法。所以如果是要在其他平台上使用的话,就得用不带BOM的!不带BOM才是标准的UTF8,UTF8也不需要BOM。

宽字符和窄字符(多字节字符)

有的编码方式采用 1~n 个字节存储,是变长的,例如 UTF-8、GB2312、GBK 等;如果一个字符使用了这种编码方式,我们就将它称为多字节字符,或者窄字符。

有的编码方式是固定长度的,不管字符编号大小,始终采用 n 个字节存储,例如 UTF-32、UTF-16 等;如果一个字符使用了这种编码方式,我们就将它称为宽字符。

Unicode 字符集可以使用窄字符的方式存储,也可以使用宽字符的方式存储;GB2312、GBK、Shift-JIS 等国家编码一般都使用窄字符的方式存储;ASCII 只有一个字节,无所谓窄字符和宽字符。

原文链接:https://blog.csdn.net/guxiaonuan/article/details/78678043

MIME类型

MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的标准,用来表示文档、文件或字节流的性质和格式。

浏览器通常使用 MIME 类型(而不是文件扩展名)来确定如何处理URL,因此 We b服务器在响应头中添加正确的 MIME 类型非常重要。如果配置不正确,浏览器可能会无法解析文件内容,网站将无法正常工作,并且下载的文件也会被错误处理。

https://www.runoob.com/http/mime-types.html

MIME 类型通用结构: type/subtype

第二个参数是细分后的类型。常见的文本类型:text/plain 表示文本文件的默认值。

数据库中的编码知识

以ORACLE和MYSQL为例

查看数据库设置的字符集

oralce:

SELECT * FROM nls_database_parameters WHERE parameter LIKE '%CHARACTERSET';
-- 查看字符长度
SELECT LENGTH('字符串') FROM dual;
-- 查看字节长度
SELECT LENGTHB('字符串') FROM dual;

mysql:

SHOW VARIABLES LIKE '%character%';
-- 查看字符长度
SELECT CHAR_LENGTH('字符串'); -- 返回2
-- 查看字节长度
SELECT LENGTH('字符串'); -- 返回6

编码的应用

getBytes方法对比

String.getBytes("xxx")方法是将字符串编码为字节数组,根据编码方式的不同,生成的字节数组也是不同的。以下对比GBK和UTF-8的编码:

public static void main(String[] args) throws UnsupportedEncodingException {  
    String str1 = "测试";  
    String str2 = "abc";  
    byte[] str1gbk = str1.getBytes("GBK");  
    byte[] str1uft8 = str1.getBytes("UTF-8");  
    byte[] str2gbk = str2.getBytes("GBK");  
    byte[] str2uft8 = str2.getBytes("UTF-8");  
    // print  
    System.out.println("str1gbk="+ Arrays.toString(str1gbk));  
    System.out.println("str1uft8="+ Arrays.toString(str1uft8));  
    System.out.println("str2gbk="+ Arrays.toString(str2gbk));  
    System.out.println("str2uft8="+ Arrays.toString(str2uft8));  
}

输出结果:

str1gbk=[-78, -30, -54, -44]
str1uft8=[-26, -75, -117, -24, -81, -107]
str2gbk=[97, 98, 99]
str2uft8=[97, 98, 99]

实例2:

ublic static void test2() throws UnsupportedEncodingException {  
    String str = "测试";  
    // string -> byte[] GBK encoding  
    byte[] gbkBytes = str.getBytes("GBK");  
    // byte[] -> string -> byte[] -> string  
    // 由于最外层的new String没有指定编码方式,故使用的是默认的编码方式。但是此编码方式非GBK,所以结果是乱码  
    String strgbk = new String(new String(gbkBytes, "GBK").getBytes("GBK"));  
    System.out.println(strgbk);  
    // byte[] -> string -> byte[] -> string  
    // 当最后的getBytes()和最外层的new String都没有指定编码方式,即都是使用默认的编码方式。故抵消后获取的字符串是正常的  
    String strgbk1 = new String(new String(gbkBytes, "GBK").getBytes());  
    System.out.println(strgbk1);  
}

输出结果:

����
测试

Linux中判断文件编码

可以使用linux的file命令来获取文件类型,如:

file -bi file
# -b : 不展示文件名; -i : 展示MIME类别

# 列出目录下所有文件的类型
> file *                                                      
1111:         directory                   
1.txt:        empty                                                 
2.txt:        ASCII text                                             
testCut.txt:  UTF-8 Unicode text                                     

参考链接

https://www.cnblogs.com/klb561/p/12064095.html#/

https://www.cnblogs.com/klb561/p/12064095.html#/

https://www.cnblogs.com/chiguozi/p/5860364.html

https://baike.baidu.com/item/gb18030/3204518?fr=aladdin

https://www.cnblogs.com/strive-sun/p/15211950.html

https://www.cnblogs.com/yougoo/p/11958649.html

https://blog.csdn.net/guxiaonuan/article/details/78678043