当我在解决Linux下解压zip文件的问题时发现编码方面的问题还蛮多的, 在此记录.
Windows下编码方式的坑
🔗 知乎-Windows记事本的ANSI, Unicode, UTF-8这三种编码模式有什么区别?
🔗 「带 BOM 的 UTF-8」和「无 BOM 的 UTF-8」有什么区别?网页代码一般使用哪个
总结一下, 在Windows中:
- 所谓的「ANSI」指的是对应当前系统 locale 的遗留(legacy)编码
- 所谓的「Unicode」指的是带有 BOM 的小端序 UTF-16
- 所谓的「UTF-8」指的是带 BOM 的 UTF-8
之前一直听说Windows自带的记事本不要用, 会自动在文件开头加一个东西, 导致一些地方显示该文件开头有一个 "?" , 一直不知道是多了个啥, 原来是UTF-8/UTF-16编码的文件会在文件开头多一个 BOM. UTF-8与带BOM的UTF-8的区别在于带BOM的UTF-8文件开头有U+FEFF. 微软使用带BOM的UTF-8 的目的是将UTF-8与ASCII等编码方式区分开, 但这就导致了在很多时候UTF-8默认是不带BOM时文件读取有误.
GBK不是一种编码格式
GBK并不是一种编码格式, 而是一个字符集. 我觉得很low的是GBK的全称是 Guojia Biaozhun Kuozhan 🤦 不过也可以理解, 毕竟这三个字母是从"国标扩"的首拼发展来的.
自整理的中文字符集发展史
以下是我整理的中文字符集发展史:
- 最开始中国国家标准化管理委员会发布了
GB/T2312-1980
(GB为国标首拼, T是推, 指推广, 非强制性的标准), 包括了6763个常用汉字, 发布以后许多有汉字的语言也采用了这个字符集. 但这个字符集很不够, 缺少很多生僻字, 繁体字, 对日本, 韩国汉字收录也很不全. - 微软根据GB2312做出了
cp936
(code page936), 字符集方案为GB2312, 编码方案为 EUC-CN. - 1993年Unicode1.1发布后国标委抄过来发布了
GB 13000.1-1993
. - 这时微软利用cp936中收录GB2312-1980后未使用完的码位收录了GB13000.1-1993的全部字符, 这样既兼容了GB2312又添加了许多字符. 这个新版本cp936最早实现于Windows95简体中文版. 这之后微软又陆陆续续给cp936加了几个字符. 直到2002年IANA将cp936的字符集注册为
GBK
. GBK共收录21886个汉字和图形符号. - 再后来因为字符还是不全 (比如emoji什么的)以及其他原因发布了
GB 18030-2000
, 在 Windows的对应代码页为cp54936
. 但值得一提的是尽管2000年就有了新标准, 但Windows系统的系统语言为简体中文时的默认代码页仍为cp936. 原因可以参考这里. 文章大意: 微软划水, 国家也没上心. - 最近的一版标准字符集是
GB 18030-2005
. 但这个好像有点复杂, 我并没有太看懂, 反正和之前的几个标准很不同. 怎么个不一样可以参考这里.
Unicode与UTF-8是不同层面的东西
我曾经以为Unicode是一种编码格式, 就像UTF-8是一种编码格式, 但后来我发现这两者根本是不同层面上的东西.
Unicode与UCS
两者关系
与Unicode (统一码) 同一层面的概念是UCS (Universal Character Set, 通用字符集). UCS和Unicode分别由ISO (国际标准化组织) 和一帮软件制造商 (Xerox, 苹果等成立了个统一码联盟) 先后发起. 后来他们才合并工作. 实际上两者是差不多的玩意. 我并不相信统一码联盟在成立时不知道ISO已经在开发UCS了 (UCS项目启动比Unicode早4年), 而在这样的情况下还要开发Unicode, 个人认为是商业原因. 直到现在这仍然是两个独立的项目, 但项目进度同步且两个组织都宣称要和对方保持兼容. 个人觉得很蠢🤷
两者差异
硬要说的话其实Unicode与UCS有一些细节不同.
比如ISO/IEC 10646-1使用四种不同风格的变体来显示简体中文, 繁体中文, 日语, 韩语汉字, 但Unicode2.0中只有简体中文的变体. 因此有过日本人无法接受Unicode的说法. (现在应该是没这问题了)
应该还有小区别吧? 其他区别我不知道, 也懒得找了😁
现代编码模型
Unicode是一套有些复杂的编码系统, 并不是单纯一个字符集方案或是一个编码方案, 而是构建出了一个 现代编码模型. (这段话对UCS同理)
这个模型分五层:
- 抽象字符集(Abstract character repertoire)
- 编码字符集(Coded Character Set)
- 字符编码方式(Character Encoding Form)
- 字符编码方案(Character Encoding Scheme)
- 传输编码语法(Transfer Encoding Syntax)
按我的理解: 抽象字符集是一个抽象字符的集合, 不关注字形, 更关注字意. 比如钟和鐘的抽象字符是同一个, 而编码字符集是一个映射, 规定把一个抽象字符映射到哪个字符平面, 哪个码点. 但这并不是一一映射, 为了兼容或者有的字 (比如CJK字) 可能有多个变体. 字符编码方式又称为"storage format", 我猜这是因为这一层将码点编码成码元(这里的编码方式就是Unicode Transformation Format, 包括UTF-8, UTF-16, UTF-32等方法, 也有GBK, Big5等), 而码元正是每次从文件中读取到的字符的格式.
Unicode编码模型详细阐述可以参考这里