【问题标题】:Need help understanding UTF encodings需要帮助了解 UTF 编码
【发布时间】:2011-09-12 22:18:09
【问题描述】:

您好,我注意到当我使用 UTF-8 编码(无 BOM)保存文本文件时,我能够使用 C# 上的 UTF-16 编码完美地读取它。现在这让我有点困惑,因为 UTF-8 只使用 8 位,对吧?而且 utf-16 每个字符占用 16 位。

现在假设我在这个文件中以 UTF-8 格式写入字符串“ab”,那么字母“a”有一个字节,“b”有另一个字节。

好的,但是如何在使用 UTF-16 字符集时读取这个 UTF-8 文件?在我看来,在读取文件时,“ab”的两个字节会被误认为只有一个包含两个字节的字符。因为 UTF-16 需要这 2 个字节。

我是这样读的(t.txt 编码为 UTF-8):

using(StreamReader sr = new StreamReader(File.OpenRead("t.txt"), Encoding.GetEncoding("utf-16")))
{
    Console.Write(sr.ReadToEnd());
    Console.ReadKey();
}

【问题讨论】:

  • UTF-8 在处理英语时使用 8 位 - 但如果处理其他语言,UTF-8 可能是 16、24 甚至更多位。
  • UTF-16 需要 2 或 4 个字节。
  • @Sai,哦,我以为 utf-8 总是 8 位长,而当使用 16 位时,它会被称为 utf-16。所以我可以有 16 位并且仍然使用 utf-8 而不是 utf-16?
  • @tchris 好的,但是如果 utf-16 至少需要 2 个字节,并且编码为 utf-8 的文件可能只有 1 个字节的字符。解码为 utf-16 时它是如何工作的?当他知道字符只使用引擎盖下的 1 个字节时,它是否只是添加了一个 0x00 字节?但如果他这样做了,那么与 utf-8 没有区别。我不明白。
  • @Delta 与 UTF-8 不同的字符可能有不同的长度。例如,常规英文字符将占用 8 位,但其他字符集(例如泰米尔语)将占用更多位。你可以看看joelonsoftware.com/printerFriendly/articles/Unicode.html——里面有很好的解释。

标签: c# encoding utf-8 utf-16 utf


【解决方案1】:

The '8' means it uses 8-bit blocks to represent a character. 这并不意味着每个字符占用固定的 8 位。每个字符的块数从 1 到 4 不等(尽管理论上字符最长可达 6 个字节)。

试试这个简单的测试,

  • 使用不带 BOM 编码的 UTF8 创建文本文件(比如 Notepad++)
  • 使用File.ReadAllBytes() 读取文本文件(就像您在代码中所做的那样)。 byte[] utf8 = File.ReadAllBytes(@"E:\SavedUTF8.txt");
  • 检查每个字符占用的字节数。
  • 现在尝试对编码为 ANSI byte[] ansi = File.ReadAllBytes(@"E:\SavedANSI.txt"); 的文件进行相同操作
  • 比较两种编码每个字符的字节数。

注意,File.ReadAllBytes() 尝试根据字节顺序标记的存在自动检测文件的编码。可以检测编码格式 UTF-8 和 UTF-32(big-endian 和 little-endian)。


有趣的结果
SavedUTF8.txt 包含字符

  • a :字节数组中的字节数 = 1
  • © (UTF+00A9)(Alt+0169) : 字节数组中的字节数 = 2
  • : (UTF+E0A080)(Alt+14721152) 字节数组中的字节数 = 3

ANSI 编码始终占用 8 位(即在上面的示例中,字节数组的大小始终为 1,而与文件中的字符无关)。正如@tchrist 所指出的,UTF16 每个字符占用 2 或 4 个字节(而不是每个字符固定的 2 个字节)。


编码表(来自here
以下字节序列用于表示一个字符。要使用的序列取决于字符的 Unicode 编号:

U-00000000 – U-0000007F:    0xxxxxxx
U-00000080 – U-000007FF:    110xxxxx 10xxxxxx
U-00000800 – U-0000FFFF:    1110xxxx 10xxxxxx 10xxxxxx
U-00010000 – U-001FFFFF:    11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 – U-03FFFFFF:    111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 – U-7FFFFFFF:    1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

xxx位位置用二进制表示的字符代码号的位填充。最右边的 x 位是最低有效位。只能使用可以表示字符码号的最短的多字节序列。请注意,在多字节序列中,第一个字节中前导 1 的位数与整个序列中的字节数相同。


确定字符的大小

表示非 ASCII 字符的多字节序列的第一个字节始终在 0xC0 到 0xFD 的范围内,它指示该字符后面有多少字节。

这意味着 2 字节字符 (110) 的前导位与 3 字节字符 (1110) 的前导位不同。这些前导位可用于唯一标识一个字符占用的字节数。


更多信息

【讨论】:

  • 是的,所以 utf-8 在 charcode 大于 127 时使用 2 个字节等。这让我很好奇你怎么知道下一个字符何时使用 1、2、3 或4字节。谢谢!
  • 我已经更新了答案以阐明字符字节大小的计算。顺便说一句,不需要知道下一个字符的大小。一旦指针到达下一个字符的位序列,就会计算大小。
  • 这个答案是错误的! UTF-16 是可变宽度,使用 16 位代码单元,就像 UTF-8 是可变宽度,使用 8 位代码单元一样。此声明是谎言:Similarly, UTF16 will always consume 16 bits per character (it is fixed length as compared to variable length in UTF8)
  • 我的立场是正确的。正如@tchrist 所指出的,UTF16 每个字符可以占用 2 到 4 个字节。我已经更新了答案以反映相同的情况。
【解决方案2】:
【解决方案3】:

查看http://www.joelonsoftware.com/articles/Unicode.html,它将回答您所有的 unicode 问题

【讨论】:

  • 好文章,他说 utf-8 可以存储任何代码点,而其他编码如 iso-8859-1、windows-1252 等只是一些。现在我想知道为什么不是每个人都只使用 utf-8。
  • 大多数较新的浏览器建议在网页上使用 UTF-8。至于其他应用程序,我认为采用它的障碍是每个字符的可变长度。与固定长度编码不同,使用offset = n * encodingLength 不能简单地到达第n 个字符。
  • @Devendra:那么你最好使用 UTF-32,因为 UTF-16 没有这个属性。任何认为他们可以使用简单的 UTF-16 索引来获取 ?ᵗʰ 字符的人都犯了严重错误,正如这条评论所证明的那样。并且有很多很好的理由为网页使用 UTF-8。有些在this answers的????????????????部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 2017-06-19
  • 2016-05-03
  • 1970-01-01
  • 2021-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多