核心内容摘要
AI写作与文本处理工具排行榜:AIGC论文助手TOP10
BOM头BOM头全程Byte Order Mark (字节顺序标记), 是Unicode编码标准中最早是用于UTF32/16中标识字节顺序的特殊字符后来随着UTF-8的出现为了兼容又有了标识文本编码格式的作用。
最初主要是为了解决UTF32/16编码方案中大小端的问题(大端BE高字节在前小端LE低字节在前)。
所以需要在字符串前增加一个特殊标记以方便识别解析。
随着UTF-8的出现不再需要BOM头。
但微软为了方便自家软件能快速区分UTF-8与ANSI编码而额外引入了/* by
hk - online tools website :
hk/zh/dnstt.html */ 非标准拓展。
因此有了独特的UTF-8 BOM 编码方式不同Unicode编码中的BOM头表现编码格式BOM头字节序列长度说明UTF-8 BOM (微软特色)EF BB BF3字节仅作编码标识无字节顺序问题UTF-8---UTF-16 BE大端FE FF2字节表示高位字节在前UTF-16 LE小端FF FE2字节表示低位字节在前UTF-32 BE00 00 FE FF4字节表示高位字节在前UTF-32 LEFF FE 00 004字节表示低位字节在前眼见为实使用文本编辑器选择另存为保存为不同的编码方案/* by
hk - online tools website :
hk/zh/dnstt.html */ public static void Run() { var utf8_path C:\Users\liu\Documents\utf-
txt; var utf8_bom_path C:\Users\liu\Documents\utf-8 bom.txt; var utf16_le_path C:\Users\liu\Documents\utf-16 be.txt; var utf16_be_path C:\Users\liu\Documents\utf-16 le.txt; var utf8 BitConverter.ToString(File.ReadAllBytes(utf8_path)); Console.WriteLine(utf8 无bom: utf8 \n); var utf8_bom BitConverter.ToString(File.ReadAllBytes(utf8_bom_path)); Console.WriteLine(utf8 有bom: utf8_bom \n); var utf16_le BitConverter.ToString(File.ReadAllBytes(utf16_le_path)); Console.WriteLine(utf-16 be大端: utf16_le \n); var utf16_be BitConverter.ToString(File.ReadAllBytes(utf16_be_path)); Console.WriteLine(utf-16 le小端: utf16_be \n); }为什么UTF-8不需要BOMBOM的本质是为了解决UTF-16/32大小端歧义的问题而UTF8的编码特性从根本上解决了BOM要处理问题所以BOM对UTF-8而言既无必要而且还属于额外附加的内容。
UTF-16/32为什么需要假如我要传输一个字符串“中“Unicode编码U4E2D在我传输给你的过程中它可以是FE-FF-4E-2D大端也可以是FF-FE-2D-4E(小端)如果我没有标识字节顺序你如何解析UTF-8 核心编码规则要想知道为什么UTF-8不需要BOM先从它的原理开始说起。
可变长编码用
个字节表示一个Unicode字符码点越小占用的字节数越小。
标准的字节格式每个字符的起始字节会有一个特殊标识来表示该字符占用的总字节数后续的字节用固定格式来表示相当于有一个标准模板来定义UTF-8字符。
字符占用字节数起始字节二进制格式续字节二进制格式可表示的Unicode码点范围说明1字节0xxxxxxx无续字节U0000~U007F对应ASCII字符2字节110xxxxx10xxxxxxU0080~U07FF欧洲、中东等字符3字节1110xxxx10xxxxxxU0800~UFFFF中文、日文、韩文等常用字符4字节11110xxx10xxxxxxU10000~U10FFFF罕见字符、emoji等眼见为实以中举例确定字节数中这个字符的码点为U4E2D在U0800~UFFFF范围内占用 3 字节。
十六进制转换成二进制4E2D转成二进制得到0100 1110 0010 1101按照UTF-8模板格式填充已知占用3字节模板格式为1110xxxx10xxxxxx10xxxxxx(起始字节2个续字节)-----------得到UTF-8编码111001001011100010101101二进制转换成十六进制111001000xE4101110000xB8101011010xAD最终中这个字符的UTF-8编码序列是E4 B8 AD也就是我们日常中经常看到的UTF-8 十六进制表示眼见为实以A举例确定字节数A的码点为U0041在U0000~U007F访问内占用1字节。
十六进制转换成二进制0041转成二进制得到0100 0001按照UTF-8模板格式填充1字节的模板格式为:0xxxxxxx(起始字节)----得到UTF-8编码:01000001二进制转换成十六进制实际上又转换回来又变回了0041。
UTF-8 以一种很偷鸡又巧妙的办法 实现了与ASCII的兼容。
因为ASCII只占用7bit最高位默认为0而UTF-81字节的模板也是0xxxxxxx 从而实现了与ASCII的兼容回到主题UTF-8 不存在大小端问题由于UTF-8的可变长编码与标准的字节格式所以每个字符的格式是固定的有明确的先后顺序。
比如中的U4E2DUTF-8编码是E4-B8-AD 这三个字节的顺序是唯一且固定的解析时如果顺序颠倒就会解析失败所以不用管大端还是小端严格按照顺序解析即可。
UTF-8能够自我解析/识别无需BOM作为签名BOM 还有一个附加作用作为文件编码的 “签名”帮助软件快速识别 Unicode 编码格式。
但对于 UTF-8 而言这种 “签名” 也是多余的。
因为解析软件可以通过扫描文本的二进制内容根据UTF-8的格式规则上面提到的0xxxxxxx、110xxxxx等直接判断文件是否为 UTF-8 编码无需依赖文件开头的 BOM 标记为什么有UTF-8 BOM的存在UTF-8 BOM并非Unicode官方标准而是微软为解决兼容问题而留下的历史包袱。
早期的Windows默认编码是本地化ANSI它是Windows早期为适配本地语言设计的历史编码方案它千好万好为windows全球化立下了汗马功劳但有一个致命的缺点文件开头没有任何特殊标识。
比如中文系统默认 GBK/GB2312英文系统默认 ISO-
日文系统默认 Shift_JIS—— 这些 ANSI 编码都是无标记的多字节编码和 UTF-8 一样文件开头没有任何特殊标识。
眼见为实比如用户在记事本中写了字符中保存为 UTF-8无 BOM下次打开时记事本没有任何标记可以判断这是 UTF-8可能会按照系统ANSI比如GBK来解析导致出现乱码。
为什么中文在UTF-16下占用2字节反而在UTF-8中占用3字节了简单来说就是运气问题UTF-8 的字节数是按码点容量分层设计的中文的码点大小决定了它只能落在 3 字节区间。
我们日常使用的 99% 以上的中文码点都在BMP 平面的U4E00一~U9FA5龥区间而UTF-16的编码规则是对BMP平面字符直接用2字节编码,对SMP平面用4字节编码。
而刚好落在BMP的中文码点自然而然的就使用2字节编码但UTF-8的编码规则是根据Unicode 码点的大小来决定字节数而中文的U4E00~U9FA5刚好落在了U0800 ~ UFFFF这个3字节码点的区间内因此要遵守3字节编码的规则。
为什么UTF-8不把中文设计为2字节主要是2字节的UTF-8区间U0080 ~ U07FF容量有限装不下这么多中文。
2字节的UTF-8 去掉前面的11010 标识位只剩下5611位的有效容量只能表示2^112048个码点容纳不下中文。
只有3字节的有效容量是46616 可以表示2^1665536个码点刚好覆盖整个 BMP 平面足以容纳所有中文常用字。