utf8

RFC 3629

ISO/IEC 10646-1定义了一个大的字符集, 叫做Universal Character Set(UCS). 包纳了世
界上多数的书写系统. 但是, 最开始通过的UCS的编码, 和许多现在的程序和协议不兼容,
这就导致了UTF-8的开发, 也就是这个memo所描述的. utf8保留了完整的US-ASCII范围, 提
供了和文件系统, parser以及其他依靠US-ASCII, but are transparent to other
values..

介绍

10646..... Unicode标准也定义了相同的字符集合, 还进一步定义了额外的字符属性以及实
现者感兴趣的其他程序细节. 到目前为止, Unicode的改变和10646的增订都是相互跟进的,
因此两者的character repertoires和code point的分配仍旧是同步的.

ISO/IEC 10646和Unicode给他们的常用repertoire定义了一些编码形式: UTF-8, UCS-2,
UTF-16, UCS-4以及UTF-32. 在一个编码格式(encoding form)中, 每一个字符都是一个或者
多个编码单元的形式呈现的. 除了UTF-8之外的所有标准UCS编码格式的编码单元都大于一个
字节, 这使得他们在目前许多的使用8甚至是7个bit字符的程序和协议中难以使用.

而这里讲到的UTF-8, 则是使用单字节的编码单元. 使用一个字节(octec, 8bit字节)中的所
有bit, 但是保留了完整的US-ASCII范围: US-ASCII字符编码为一个有着寻常US-ASCII值的
字节,所有值在这个范围的字节都只用来标示一个US-ASCII字符.

UTF-8以变长字节数的形式编码UCS字符, 其中字节的数目, 每个字节的值, 都取决于在
ISO/IEC 10646中分配给字符的整数值(character number, 也就是(...), code
position,code point或者Unicode scalar value). 这个编码形式有一些特点(所有的数值
都是十六进制)

  • 从U+0000到U+007F(US-ASCII repertoire)的字符编号对应字节00到7F(7bit US-ASCII值
    ). 一个纯ASCII字符串同样也是一个合法的UTF-8字符串.
  • US-ASCII字节值在别的UTF-8编码的字节流中不会出现. 这提供了与文件系统或者其他给
    予US-ASCII值进行解析但是对其他值是透明的(不管?)的软件(比如C库中的printf函数)的
    兼容性
  • 在UTF-8和其他编码格式之间相互转换很简单.
  • 一个多字节序列中的第一个字节会指示这个序列中有多少个字节.
  • 字节值C0,C0,F5,FF不会出现
  • 在一个字节流中任一个位置都很容易找到(定位?)字符边界.
  • UTF-8字符串的字节值字典排序和按照字符编号排序是一样的. 当然这个并没有什么用处,
    因为按照字符编号几乎总是说不通的.
  • Boyer-Moore快速搜索算法可以用于UTF-8数据.(..)
  • UTF-8字符串可以相当可靠地看做这样一个简单的算法, 即, 其他编码的字符串看起来是
    一个合法的UTF-8字符串的概率很低, 且随串的长度增加而减小

UTF-8由Ken Thompson在1992年9月根据Rob Pike指定的设计标准, 以定义一个可以以非破坏
性的方式用于Plan9操作系统的UCS转换格式为目标, 制定的. 汤普森的设计xxxx, 最后成为
了UTF-8

UTF-8定义

UTF-8由Unicode标准定义. xxx

在UTF-8中, U+0000到U+10FFFF范围的字符(UTF-16可及范围)使用1到四个字节的序列编码.
单字节序列唯一的一个字节的最高位bit为0, 剩下的7个bit用于编码字符编号. 在一个n个
字节的序列中(n > 1), 初始字节的高n个bit设置wei 0, 之后是一个设置为0的bit. 该字节
中剩下的bit包含来自要被编码的字符中的bit. 之后的字节的高位bit设置为1, 之后的bit
设置为0, 剩下6个bit用于编码要被编码的字符的bit.

下面这个表总结了不同的字节类型. 字符x标示对应的bit可以用于编码字符编号.

   Char. number range  |        UTF-8 octet sequence
      (hexadecimal)    |              (binary)
   --------------------+---------------------------------------------
   0000 0000-0000 007F | 0xxxxxxx
   0000 0080-0000 07FF | 110xxxxx 10xxxxxx
   0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
   0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

将一个字符编码为UTF-8的过程如下:

  1. 根据字符编号和上面的表判断需要的字节的数量. 改表中的各行是互斥的, 也就是只有
    一个字符只有一个合法的编码方式.
  2. 根据表中第二列准备每一个字节的高位bit.
  3. 在标记为x中的bit中填充来自字符编号的bit. 从右往左填充.

UTF的定义禁止编码U+D800到U+DFFF之间的字符编号, 这些是保留给UTF-16编码格式的(as
surrogate pairs(??)), 不直接标示字符. 当以UTF-8格式编码UTF-16数据的时候, 需要先
解码UTF-16数据来获得字符编号, 之后再按照上面的方法编码为UTF-8. 这个和CESU-8不同(
一个类似UTF-8的编码, 不是针对互联网使用的). CESU-8操作类似UTF-8但是编码UTF-16值
而非字符编码(code point). 这会导致对在0xFFFF智商的字符编号出现不同的结果, 这些字
符的CESU-8编码在UTF-8中是不合法的.
(被这个搞糊了, UTF16不干我事啊,真是要命)

解码UTF-8字符的按照如下过程进行:

  1. 初始化一个二进制数, 所有bit都设置为0. 最多需要21个bit.
  2. 根据序列中的字节数和表中的第二列判断哪些bit编码了字符编号(character number).
  3. 将序列中的bit复制到二进制数中, 首先是最后一个字节的低位bit, 直到没有x

对以上解码算法的实现必须要防止对非法序列的解码. 比如, 某些天真的实现可能会将过长
的UTF-8序列C0 80解码为字符U+0000(本来只要使用一个字节编码的), 或者将surrogate
pair, ED A1 8C ED BE B4, 解码为U+233B4(???). 解码非法序列可能会有安全影响或者导
致其他问题.
(11101101,10100001,10001100,11101101,10111110,10110100)
(1101100001001100 ==> D84C) (1101111110110100 ==> DFB4)
(???)

UTF-8字节序列语法

这里给出一个ABNF语法

 UTF8-octets = *( UTF8-char )
   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
   UTF8-1      = %x00-7F
   UTF8-2      = %xC2-DF UTF8-tail
   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
                 %xF4 %x80-8F 2( UTF8-tail )
   UTF8-tail   = %x80-BF

(UTF-3编码的要大于07FF, ED 80那里可能是,D800-DFFF的范围吧...不包含D800-DFFF)

字节序标识(BOM)

UCS 字符U+FEFF "ZERO WIDTH NO-BREAK SPACE", 也被非正式地成为"BYTE ORDER MARK".(
还真有这个字符, 看起来光标卡了一下一样, 得多按一下..), 可以用作文本中的0长度空格
. 但是其BOM的名称表明了这个字符的第二个可能额用法. 给一个UCS字符流前缀一个U+FEFF
字符作为"签名"... 收到这样一个流序列的接受者可以使用初始字符看作是流由UCS字符构
成的提示,以及识别涉及到了那个UCS编码, 以及, 对于有多字节编码单元(UTF-8不是的吧
..), 作为识别字节顺序的一种方式.. UTF-8是用的是单字节的编码单元, 最有一个功能是
没用的,BOM总是序列EF BB BF的形式.

很重要的一点是, 要知道, 除了出现在流开始位置, 任何U+FEFF都必须非解释为0宽度
non-breaking space, 不能解释为签名. 解释为签名的时候, Unicode标准建议将初始的
U+FEFF字符在处理文本之后移除. 在一些情况下, 这个移除是必须的.(比如, 在剪接两个字
符串的时候, 不这样做的话, 可能会导致出现不必要的0宽空格), 但是xxxx. 因此推荐忽略
而非去除, 只在真的必要的时候, 移除...
(不管就好了..)

posted on 2019-10-25 21:33  jakio6  阅读(206)  评论(0编辑  收藏  举报