为什么我们需要Unicode?
在(不太)早期,所有存在的都是ASCII。这没关系,因为只需要几个控制字符、标点符号、数字和字母,就像这句话中的一样。不幸的是,没有预见到当今全球互通和社交媒体的奇怪世界,在同一份文件中看到英语、العربية、汉语、עִבְרִית、ελληνικά和ភាសាខ្មែរ 并不少见(希望我没有破坏任何旧的浏览器)。
但为了争论,假设 Joe Average 是一名软件开发人员。他坚持认为他永远只需要英语,因此只想使用 ASCII。这对于用户Joe 来说可能很好,但对于软件开发人员Joe 来说就不好了。世界上大约有一半的人使用非拉丁字符,而使用 ASCII 对这些人来说可能是不体谅的,最重要的是,他正在关闭他的软件,以适应一个庞大且不断增长的经济体。
因此,需要一个包含所有语言的包容性字符集。于是出现了Unicode。它为每个字符分配一个唯一编号,称为代码点。与其他可能的集合相比,Unicode 的一个优点是前 256 个代码点与ISO-8859-1相同,因此也与 ASCII 相同。此外,绝大多数常用字符在称为基本多语言平面 (BMP)的区域中只能用两个字节表示。现在需要一个字符编码来访问这个字符集,正如问题所要求的那样,我将专注于 UTF-8 和 UTF-16。
内存注意事项
那么有多少字节可以访问这些编码中的哪些字符呢?
- 1 个字节:标准 ASCII
- 2 个字节:阿拉伯语、希伯来语、大多数欧洲文字(最显着的不包括格鲁吉亚语)
- 3 个字节:BMP
- 4 个字节:所有 Unicode 字符
- 2个字节:BMP
- 4 个字节:所有 Unicode 字符
现在值得一提的是,不在 BMP 中的字符包括古文字、数学符号、音乐符号,以及更稀有的中文、日文和韩文 (CJK)字符。
如果您主要使用 ASCII 字符,那么 UTF-8 肯定会更节省内存。但是,如果您主要使用非欧洲脚本,则使用 UTF-8 的内存效率可能比 UTF-16 低 1.5 倍。在处理大量文本时,例如大型网页或冗长的 word 文档,这可能会影响性能。
编码基础
注意:如果您知道 UTF-8 和 UTF-16 是如何编码的,请跳到下一节进行实际应用。
- 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 以两个字节为基本单位,因此受到字节序的影响。作为补偿,可以在数据流的开头放置一个保留的字节顺序标记,以指示字节顺序。因此,如果您正在读取 UTF-16 输入,并且未指定字节顺序,则必须检查这一点。
可以看出,UTF-8 和 UTF-16 几乎无法相互兼容。因此,如果您正在执行 I/O,请确保您知道您使用的是哪种编码!有关这些编码的更多详细信息,请参阅UTF FAQ。
实际编程注意事项
字符和字符串数据类型:它们是如何在编程语言中编码的?如果它们是原始字节,那么在您尝试输出非 ASCII 字符的那一刻,您可能会遇到一些问题。此外,即使字符类型基于 UTF,也不意味着字符串是正确的 UTF。它们可能允许非法的字节序列。通常,您必须使用支持 UTF 的库,例如用于 C、C++ 和 Java的ICU 。在任何情况下,如果您想输入/输出默认编码以外的其他内容,则必须先对其进行转换。
Recommended, default, and dominant encodings: When given a choice of which UTF to use, it is usually best to follow recommended standards for the environment you are working in. For example, UTF-8 is dominant on the web, and since HTML5,它一直是推荐的编码。相反,.NET和Java环境都基于 UTF-16 字符类型。令人困惑(并且错误地),经常提到“Unicode 编码”,它通常是指在给定环境中占主导地位的 UTF 编码。
库支持:您使用的库支持某种编码。哪一个?他们支持极端案例吗?由于必要性是发明之母,UTF-8 库通常会正确支持 4 字节字符,因为 1、2 甚至 3 字节字符可能会频繁出现。但是,并非所有声称的 UTF-16 库都正确支持代理对,因为它们很少出现。
计数字符: Unicode 中存在组合字符。例如,代码点 U+006E (n) 和 U+0303(组合波浪号)形成 ñ,但代码点 U+00F1 形成 ñ。它们看起来应该相同,但是一个简单的计数算法将在第一个示例中返回 2,而在后一个示例中返回 1。这不一定是错误的,但也可能不是预期的结果。
比较相等性: A、А 和 Α 看起来相同,但它们分别是拉丁文、西里尔文和希腊文。你也有像 C 和 Ⅽ 这样的情况。一个是字母,另一个是罗马数字。此外,我们还需要考虑组合字符。有关详细信息,请参阅Unicode 中的重复字符。
代理对:这些在 Stack Overflow 上经常出现,所以我只提供一些示例链接: