认识编码
2022-12-19
概念
字符集 vs. 编码方式:编码方式就是将字符集里的字符映射成可以被计算机识别的若干位二进制来表示。
对于一个字符集来说要正确编码转码一个字符需要三个关键元素:字库表(character repertoire)、编码字符集(coded character set)、字符编码(character encoding form)。 其中:字库表是一个相当于所有可读或者可显示字符的数据库,字库表决定了整个字符集能够展现表示的所有字符的范围。编码字符集,即用一个编码值code point来表示一个字符在字库中的位置。字符编码,将编码字符集和实际存储数值之间的转换关系。一般来说都会直接将code point的值作为编码后的值直接存储。例如在ASCII中A在表中排第65位,而编码后A的数值是0100 0001也即十进制的65的二进制转换结果。
- 字符(Character) 是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。
- 字符集(Character set) 是一个系统支持的所有抽象字符的集合。通常以二维表的形式存在,二维表的内容和大小是由使用者的语言而定。如ASCII,GBxxx,Unicode等。
- 字符编码(Character encoding) 是把字符集中的字符编码为特定的二进制数,以便在计算机中存储。每个字符集中的字符都对应一个唯一的二进制编码。
字符集和字符编码一般都是成对出现的,如ASCII、IOS-8859-1、GB2312、GBK,都是即表示了字符集又表示了对应的字符编码。Unicode比较特殊,有多种字符编码(UTF-8,UTF-16等)
字符集对比
字符集名称 | 字符集范围 | 编码格式 | 备注 |
---|---|---|---|
ASCII | 英文或其他西欧语言 | 单字节 | |
GB2312 | 汉字 | 双字节 | |
GBK | 更多汉字(21886个汉字和图形符号) | 双字节 | 完全向下兼容GB2312 |
GB18030 | 支持简体中文、繁体中文 藏文、蒙文、维吾尔文等主要的少数民族文字 | 可变多字节(1、2、4字节) | 完全兼容GB2312,基本兼容GBK |
常见的支持中文的字符集的对比
常用的支持中文的字符集有:
- GB2312
- GBK
- GB18030
- BIG5
- HZ
- 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,美国标准信息交换代码
主要用来表示英文或其他西欧语言(在扩展版中)
编码格式
- 使用7位(1字节,高位是0)表示128种字符:标准(基础)ASCII 码
- 使用8位表示256种字符:扩展ASCII 码(EASCII),多的一位表示附加的128 个特殊符号字符、外来语字母和图形符号
GB2312
GB2312(或称GB2312-80,80表示1980年发布)为了支持中文的表示,国人发明的。它是中华人民共和国国家标准简体中文字符集。基本满足汉字的使用,但是对于人名、古汉语等方面出现的罕用字和繁体字,GB 2312不能处理,因此后来GBK及GB 18030汉字字符集相继出现以解决这些问题。
分区表示:GB 2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。
- 01–09区为特殊符号。
- 16–55区为一级汉字,按拼音排序。
- 56–87区为二级汉字,按部首/笔画排序。
举例来说,“啊”字是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主要有以下特点:
- 和UTF-8一样都采用多字节编码,每个字可以由1个、2个或4个字节组成。
- 编码空间庞大,最多可定义161万个字元。
- 支持中国国内少数民族的文字,不需要动用造字区。
- 汉字收录范围包含繁体汉字以及日韩汉字。
编码格式
- 单字节,其值从0x00到0x7F。
- 双字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x40到0xFE(不包括0x7F)。
- 四字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x30到0x39,第三个字节从0x81到0xFE,第四个字节从0x30到0x39。
Unicode
称为万国码、国际码、统一码、单一码。为了解决使用不同国家的编码格式所造成的乱码问题,国际编码格式Unicode应运而生。
编码格式
UCS-2版本:双字节编码;UCS-4版本:4字节编码
但是,有个问题:如果都是按照统一长度的编码,那么英文本身只需用1字节表示,却要统一用2/4字节表示,很浪费传输和存储空间。于是,一些基于Unicode的编码方式就应运而生。Unicode
是一个字符集,给所有的字符分配一个码值。这里面只是分配码值,而不是最后的编码实现,编码的实现有很多不同的方式如utf-8
、utf-16
、utf-32
。简单来说,Unicode
只是业界标准,但是具体一个字符占多少字节,要取决于如UTF-8等的编码方式。
关于utf-8
、utf-16
、utf-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就会非常节省空间。
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