为什么我们需要统一码?
在(不太)早期,存在的只是ASCII https://en.wikipedia.org/wiki/ASCII。这没关系,因为所需要的只是一些控制字符、标点符号、数字和字母,就像这句话中的那样。不幸的是,今天的全球互通和社交媒体的奇怪世界并没有被预见到,在同一份文件中看到英语、阿拉伯语、汉语、עִבְרִת、ελληνικά和ភាសាខ្មែរ并不少见(我希望我没有打破任何旧的规则)。浏览器)。
但为了便于讨论,我们假设 Joe Average 是一名软件开发人员。他坚持认为他只需要英语,因此只想使用 ASCII。这对乔来说可能很好user,但这对乔来说不太好软件开发人员。世界上大约有一半的人使用非拉丁字符,而使用 ASCII 对这些人来说可以说是不体贴的,最重要的是,他正在将他的软件封闭在一个庞大且不断增长的经济体中。
因此,一个包容性的字符集包括all需要语言。就这样来了Unicode https://en.wikipedia.org/wiki/Unicode。它为每个字符分配一个唯一的编号,称为代码点。与其他可能的集合相比,Unicode 的一个优点是前 256 个代码点与ISO-8859-1 http://en.wikipedia.org/wiki/ISO-8859-1,因此也是 ASCII。此外,绝大多数常用字符只能用两个字节来表示,在一个称为基本多语言平面 (BMP) https://en.wikipedia.org/wiki/Plane_%28Unicode%29#Basic_Multilingual_Plane。现在需要一种字符编码来访问这个字符集,正如问题所问,我将集中讨论UTF-8和UTF-16。
内存注意事项
那么有多少字节可以访问这些编码中的哪些字符呢?
- 1 字节:标准 ASCII
- 2 个字节:阿拉伯语、希伯来语、大多数欧洲文字(尤其不包括Georgian http://en.wikipedia.org/wiki/Georgian_alphabet)
- 3字节:BMP
- 4字节:所有Unicode字符
现在值得一提的是,BMP 中未包含的字符包括古代文字、数学符号、音乐符号以及更罕见的字符。中文、日语和韩语 (CJK) http://www.unicode.org/faq/han_cjk.html人物。
如果您主要使用 ASCII 字符,那么 UTF-8 无疑更节省内存。但是,如果您主要使用非欧洲脚本,则使用 UTF-8 的内存效率可能比 UTF-16 低 1.5 倍。处理大量文本(例如大型网页或冗长的 Word 文档)时,这可能会影响性能。
编码基础知识
Note: If you know how UTF-8 and UTF-16 are encoded, skip to the next section for practical applications.
-
UTF-8:对于标准 ASCII (0-127) 字符,UTF-8 代码是相同的。如果需要与现有 ASCII 文本向后兼容,这使得 UTF-8 成为理想选择。其他字符需要 2-4 个字节。这是通过在每个字节中保留一些位来指示它是多字节字符的一部分来完成的。特别地,每个字节的第一位是
1
以避免与 ASCII 字符冲突。
-
UTF-16:对于有效的 BMP 字符,UTF-16 表示只是其代码点。然而,对于非 BMP 字符 UTF-16 引入了代理对。在这种情况下,两个两字节部分的组合映射到非 BMP 字符。这些两字节部分来自 BMP 数字范围,但 Unicode 标准保证它们作为 BMP 字符无效。另外,由于UTF-16以两个字节为基本单位,因此受到字节顺序 http://en.wikipedia.org/wiki/Endianness。为了补偿,预留字节顺序标记可以放置在指示字节顺序的数据流的开头。因此,如果您正在读取 UTF-16 输入,并且未指定字节顺序,则必须检查这一点。
可以看出,UTF-8 和 UTF-16 彼此根本不兼容。因此,如果您正在执行 I/O,请确保您知道您正在使用哪种编码!有关这些编码的更多详细信息,请参阅UTF FAQ http://www.unicode.org/faq/utf_bom.html.
实际编程注意事项
字符和字符串数据类型:它们是如何用编程语言编码的?如果它们是原始字节,那么当您尝试输出非 ASCII 字符时,可能会遇到一些问题。另外,即使字符类型基于 UTF,也不意味着字符串是正确的 UTF。它们可能允许非法的字节序列。通常,您必须使用支持 UTF 的库,例如ICU http://site.icu-project.org/适用于 C、C++ 和 Java。无论如何,如果您想输入/输出默认编码以外的内容,则必须先对其进行转换。
推荐、默认和主导编码:当选择使用哪种 UTF 时,通常最好遵循适合您工作环境的推荐标准。例如,UTF-8 在 Web 上占主导地位,自 HTML5 以来,它一直是推荐编码 http://www.w3.org/TR/html5/document-metadata.html#charset。相反,两者.NET https://en.wikipedia.org/wiki/.NET_Framework and Java https://en.wikipedia.org/wiki/Java_%28programming_language%29环境基于 UTF-16 字符类型。令人困惑(且不正确)的是,经常提到“Unicode 编码”,它通常指的是给定环境中占主导地位的 UTF 编码。
图书馆支持:您正在使用的库支持某种编码。哪一个?他们支持极端情况吗?由于需要是发明之母,UTF-8 库通常会正确支持 4 字节字符,因为 1、2 甚至 3 字节字符可能会频繁出现。然而,并非所有所谓的 UTF-16 库都正确支持代理对,因为它们很少出现。
计算字符数:存在结合Unicode 中的字符。例如,代码点 U+006E (n) 和 U+0303(组合波形符)形成 ñ,但代码点 U+00F1 形成 ñ。它们看起来应该相同,但简单的计数算法将在第一个示例中返回 2,而在后者中返回 1。这不一定是错误的,但也可能不是期望的结果。
比较是否相等:A、А 和 A 看起来相同,但它们分别是拉丁文、西里尔文和希腊文。还有 C 和Ⅽ 等情况。一个是字母,另一个是罗马数字。此外,我们还需要考虑组合字符。有关更多信息,请参阅Unicode 中的重复字符 http://en.wikipedia.org/wiki/Duplicate_characters_in_Unicode.
代理对:这些在 Stack Overflow 上经常出现,所以我只提供一些示例链接:
- 获取字符串长度 https://stackoverflow.com/questions/12907022/python-getting-correct-string-length-when-it-contains-surrogate-pairs
- 删除代理对 https://stackoverflow.com/questions/12867000/how-to-remove-surrogate-characters-in-java
- 回文检查 https://stackoverflow.com/questions/12340930/string-is-palindrome-or-not/12341017#12341017