这是一篇程序员写给程序员的趣味读物所谓趣味是指可以比较轻松地了解一些原来不清楚的概念增进知识类似于打RPG游戏的升级整理这篇文章的动机是两个问题 问题一 使用Windows记事本的另存为可以在GBKUnicodeUnicode big endian和UTF这几种编码方式间相互转换同样是txt文件Windows是怎样识别编码方式的呢? 我很早前就发现UnicodeUnicode big endian和UTF编码的txt文件的开头会多出几个字节分别是FFFE(Unicode)FEFF(Unicode big endian)EFBBBF(UTF)但这些标记是基于什么标准呢? 问题二 最近在网上看到一个ConvertUTFc实现了UTFUTF和UTF这三种编码方式的相互转换对于Unicode(UCS)GBKUTF这些编码方式我原来就了解但这个程序让我有些糊涂想不起来UTF和UCS有什么关系 查了查相关资料总算将这些问题弄清楚了顺带也了解了一些Unicode的细节写成一篇文章送给有过类似疑问的朋友本文在写作时尽量做到通俗易懂但要求读者知道什么是字节什么是十六进制 big endian和little endian big endian和little endian是CPU处理多字节数的不同方式例如汉字的Unicode编码是C那么写到文件里时究竟是将C写在前面还是将写在前面?如果将C写在前面就是big endian如果将写在前面就是little endian endian这个词出自《格列佛游记》小人国的内战就源于吃鸡蛋时是究竟从大头(BigEndian)敲开还是从小头(LittleEndian)敲开由此曾发生过六次叛乱一个皇帝送了命另一个丢了王位 我们一般将endian翻译成字节序将big endian和little endian称作大尾和小尾 字符编码内码顺带介绍汉字编码 字符必须编码后才能被计算机处理计算机使用的缺省编码方式就是计算机的内码早期的计算机使用位的ASCII编码为了处理汉字程序员设计了用于简体中文的GB和用于繁体中文的big GB(年)一共收录了个字符包括个汉字和个其它符号汉字区的内码范围高字节从BF低字节从AFE占用的码位是*=其中有个空位是DFADFE GB支持的汉字太少年的汉字扩展规范GBK收录了个符号它分为汉字区和图形符号区汉字区包括个字符 从ASCIIGB到GBK这些编码方法是向下兼容的即同一个字符在这些方案中总是有相同的编码后面的标准支持更多的字符在这些编码中英文和中文可以统一地处理区分中文编码的方法是高字节的最高位不为按照程序员的称呼GBGBK都属于双字节字符集 (DBCS) 年的GB是取代GBK的正式国家标准该标准收录了个汉字同时还收录了藏文蒙文维吾尔文等主要的少数民族文字从汉字字汇上说GB在GB的个汉字的基础上增加了CJK扩展A的个汉字(Unicode码xxdb)一共收录了个汉字 CJK就是中日韩的意思Unicode为了节省码位将中日韩三国语言中的文字统一编码GB就是ISO/IEC 的中文版相当于Unicode GB的编码采用单字节双字节和字节方案其中单字节双字节和GBK是完全兼容的字节编码的码位就是收录了CJK扩展A的个汉字 例如UCS的x在GB中的编码应该是EFUCS的x在GB中的编码应该是EF 微软提供了GB的升级包但这个升级包只是提供了一套支持CJK扩展A的个汉字的新字体新宋体并不改变内码Windows 的内码仍然是GBK 这里还有一些细节 GB的原文还是区位码从区位码到内码需要在高字节和低字节上分别加上A 对于任何字符编码编码单元的顺序是由编码方案指定的与endian无关例如GBK的编码单元是字节用两个字节表示一个汉字 这两个字节的顺序是固定的不受CPU字节序的影响UTF的编码单元是word(双字节)word之间的顺序是编码方案指定的word内部的字节排列才会受到endian的影响后面还会介绍UTF GB的两个字节的最高位都是但符合这个条件的码位只有*=个所以GBK和GB的低字节最高位都可能不是不过这不影响DBCS字符流的解析在读取DBCS字符流时只要遇到高位为的字节就可以将下两个字节作为一个双字节编码而不用管低字节的高位是什么 UnicodeUCS和UTF 前面提到从ASCIIGBGBK到GB的编码方法是向下兼容的而Unicode只与ASCII兼容(更准确地说是与ISO兼容)与GB码不兼容例如汉字的Unicode编码是C而GB码是BABA Unicode也是一种字符编码方法不过它是由国际组织设计可以容纳全世界所有语言文字的编码方案Unicode的学名是Universal MultipleOctet Coded Character Set简称为UCSUCS可以看作是Unicode Character Set的缩写 根据维基百科全书()的记载历史上存在两个试图独立设计Unicode的组织即国际标准化组织(ISO)和一个软件制造商的协会()ISO开发了ISO 项目Unicode协会开发了Unicode项目 在年前后双方都认识到世界不需要两个不兼容的字符集于是它们开始合并双方的工作成果并为创立一个单一编码表而协同工作从Unicode开始Unicode项目采用了与ISO 相同的字库和字码 目前两个项目仍都存在并独立地公布各自的标准Unicode协会现在的最新版本是年的Unicode ISO的最新标准是ISO : UCS只是规定如何编码并没有规定如何传输保存这个编码例如汉字的UCS编码是C我可以用个ascii数字来传输保存这个编码也可以用utf编码:个连续的字节E B 来表示它关键在于通信双方都要认可UTFUTFUTF都是被广泛接受的方案UTF的一个特别的好处是它与ISO完全兼容UTF是UCS Transformation Format的缩写 IETF的RFC和RFC以RFC的一贯风格清晰明快又不失严谨地描述了UTF和UTF的编码方法我总是记不得IETF是Internet Engineering Task Force的缩写但IETF负责维护的RFC是Internet上一切规范的基础 内码和code page 目前Windows的内核已经支持Unicode字符集这样在内核上可以支持全世界所有的语言文字但是由于现有的大量程序和文档都采用了某种特定语言的编码例如GBKWindows不可能不支持现有的编码而全部改用Unicode Windows使用代码页(code page)来适应各个国家和地区code page可以被理解为前面提到的内码GBK对应的code page是CP 微软也为GB定义了code pageCP但是由于GB有一部分字节编码而Windows的代码页只支持单字节和双字节编码所以这个code page是无法真正使用的 UCSUCSBMP UCS有两种格式UCS和UCS顾名思义UCS就是用两个字节编码UCS就是用个字节(实际上只用了位最高位必须为)编码下面让我们做一些简单的数学游戏 UCS有^=个码位UCS有^=个码位 UCS根据最高位为的最高字节分成^=个group每个group再根据次高字节分为个plane每个plane根据第个字节分为行 (rows)每行包含个cells当然同一行的cells只是最后一个字节不同其余都相同 group 的plane 被称作Basic Multilingual Plane 即BMP或者说UCS中高两个字节为的码位被称作BMP 将UCS的BMP去掉前面的两个零字节就得到了UCS在UCS的两个字节前加上两个零字节就得到了UCS的BMP而目前的UCS规范中还没有任何字符被分配在BMP之外 UTF编码 UTF就是以位为单元对UCS进行编码从UCS到UTF的编码方式如下 UCS编码(进制)UTF 字节流(二进制) Fxxxxxxx FFxxxxx xxxxxx FFFFxxxx xxxxxx xxxxxx 例如汉字的Unicode编码是CC在FFFF之间所以肯定要用字节模板了xxxx xxxxxx xxxxxx将C写成二进制是 用这个比特流依次代替模板中的x得到 即E B 读者可以用记事本测试一下我们的编码是否正确需要注意UltraEdit在打开utf编码的文本文件时会自动转换为UTF可能产生混淆你可以在设置中关掉这个选项更好的工具是Hex Workshop UTF以位为单元对UCS进行编码对于小于x的UCS码UTF编码就等于UCS码对应的位无符号整数对于不小于x的UCS码定义了一个算法不过由于实际使用的UCS或者UCS的BMP必然小于x所以就目前而言可以认为UTF和UCS基本相同但UCS只是一个编码方案UTF却要用于实际的传输所以就不得不考虑字节序的问题 UTF的字节序和BOM UTF以字 |