我应该使用什么?UTF8还是UTF16?

19

我需要国际化我的应用程序。

假设我有一个控件(例如备忘录),用户可以输入一些文本。用户可能来自日本、俄罗斯、加拿大等地。我想将字符串保存为TXT文件以供以后使用。我将使用自己的函数来写文本,而不是像TMemo.SaveToFile()这样的东西。

我如何将字符串保存到磁盘上?是使用UTF8还是UTF16格式?


1
你是单独保存一个字符串吗?我认为答案取决于上下文。用户是否有他们期望的文件格式?RTF?HTML?XML?我不认为性能、内存使用或磁盘使用会决定这个问题,我认为用户期望和用户体验(它是否正常工作)将要求您直接从用户那里了解到这种情况。而且我怀疑他们是否在意。他们只想让它工作。 - Warren P
1
值得一读:http://utf8everywhere.org/ - Arnaud Bouchez
3个回答

39
它们之间的主要区别在于UTF8向后兼容ASCII,只要您仅使用前128个字符,不支持Unicode的应用程序仍然可以处理数据(这可能是优点或缺点,具体取决于您的情况)。特别是在转换为UTF16时,每个API函数都需要调整为16位字符串,而对于UTF8,如果旧的API函数不执行任何字符串处理,则通常可以将其保持不变。此外,UTF8不依赖于字节序,而UTF16则依赖于字节序,这可能会使字符串I/O复杂化。
一个常见的误解是,UTF16更容易处理,因为每个字符始终占用恰好两个字节。不幸的是,这是不正确的。UTF16是一种可变长度编码,其中一个字符可能占用2或4个字节。因此,与UTF8相关的任何可变长度问题所涉及的困难同样适用于UTF16。
最后是存储大小:关于UTF16的另一个常见谬论是,对于大多数外语来说,它比UTF8更存储有效。UTF8对于所有欧洲语言来说需要较少的存储空间,可以用一个或两个字节进行编码。非BMP字符在UTF8和UTF16中均占用4个字节。唯一一种情况是,如果您的文本主要由范围在U+0800到U+FFFF之间的字符组成,则中文,日语和印地语字符存储在UTF16中。
James McNellis在2014年的BoostCon上做了一次出色的演讲,详细讨论了不同编码之间的各种权衡。即使这个演讲标题为“C++中的Unicode”,但整个第一部分实际上是与语言无关的。 Boostcon的Youtube频道提供了完整演讲的视频记录,而幻灯片可在github上找到

29

根据数据的语言而定。

如果您的数据大多是西方语言,并且希望减少所需的存储量,则应选择UTF-8,因为对于这些语言,它将占用UTF-16约一半的存储空间。读取数据时需要将其转换为UTF-16,这是Windows默认使用的编码,也是Delphi的(Unicode)字符串。

如果您的数据主要是非西方语言,则UTF-8可能比UTF-16占用更多存储空间,因为对于某些字符,每个字符可能占用多达 4 字节。(请参见@KennyTM的评论)

基本上:使用代表性的用户数据进行一些测试,看哪个在存储要求和加载时间方面表现更好。我们有一些惊喜,即UTF-16比我们想象的要慢。不必从UTF-8转换为UTF-16可以提高性能,但由于UTF-16的数据量更大,因此磁盘访问导致性能损失。


3
实际上,与额外的I/O开销相比,即使数据存储在固态硬盘上,UTF8到UTF16的转换开销几乎总是可以忽略不计的。 - Eric Grange
7
UTF-8 最多只能使用4个字节。UTF-16 中的代理对不应该单独转换为 UTF-8。 - kennytm
@KennyTM:你能提供更多关于这个的信息资源链接吗?这与UTF-8编码允许最多6个字节的事实相矛盾。所以我想了解更多关于这个的知识。 - Marjan Venema
1
@MarjanVenema:请参考http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf中的3-6和3-7表格。允许使用最多6个字节的编码并不意味着这是一个格式良好的序列,因为Unicode的值最大只能到0x10ffff。 - kennytm

10
首先,请注意Windows下的标准编码是UCS2(直到Windows 2000)或UTF-16(自XP起),Delphi本地的“string”类型自Delphi 2009以来也使用相同的本地格式(“string = UnicodeString char = WideChar”)。
在所有情况下,假设1个WideChar == 1个Unicode字符是不安全的 - 这是surrogate问题。
关于选择UTF-8或UTF-16,它取决于存储本身:
  • 如果您的文件是纯文本文件(包括XML),您可以使用UTF-8或UTF-16 - 但您必须在文件开头使用BOM,否则应用程序(如记事本)打开时可能会混淆 - 对于XML,这由您的库处理(如果不是,请更换另一个库);
  • 如果您确定您的内容大多是7位ASCII,请使用UTF-8和相关的BOM;
  • 如果您的文件是某种数据库或自定义二进制格式,则最好的格式肯定是UTF-16 / UCS2,即默认的Delphi 2009+ string布局,以及默认的数据库API布局;
  • 一些文件格式需要或更喜欢UTF-8(例如JSON甚至SQLite3),即使UTF-8文件对于亚洲字符可能比UTF-16更大。
例如,我们在客户端服务器框架中使用了UTF-8,因为我们使用JSON作为交换格式(需要UTF-8),而且由于SQLite3喜欢UTF-8。当然,我们必须编写一些专用的函数和类,以避免转换为/从字符串(对于字符串=UnicodeString类型,自Delphi 2009以来速度较慢,并且在使用字符串=AnsiString类型之前可能会丢失一些数据。请参见此帖子此单元)。最简单的方法是依赖于字符串=UnicodeString类型,使用RTL函数直接处理UTF-16编码,并避免转换。不要忘记你之前的问题
如果磁盘空间和读写速度成为问题,考虑使用压缩而不是更改编码。有一些实时压缩算法可供选择(比ZIP更快),例如LZO或我们的SynLZ。

2
Windows在Windows 2000中转换为UTF-16,而不是XP。 - Remy Lebeau
2
Unicode标准建议不要在UTF-8中使用BOM。 - mjn
@mjn 你说得对,我的评论是来自Windows世界的实践,这并不是官方标准。但是查找BOM比扫描整个上下文以验证UTF-8(或非UTF-8)更快、更容易。例如,在Delphi RTL中,我认为没有简单的方法来检查UTF-8的有效性。这是一个有争议的话题 - 参见这个SO问题 - 就像Windows不遵循某些建议时一样... - Arnaud Bouchez
@RemyLebeau 我不确定普通的Windows 2000(没有服务包)是否处理代理项和整个UTF-8编码 - 参见http://blogs.msdn.com/b/michkap/archive/2005/05/11/416552.aspx。但是这样的普通Windows 2000已经被弃用了。即使维基百科上关于此点的参考也是可疑的(与SQL Server和UTF-8有关)。 - Arnaud Bouchez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接