为什么在没有BOM的情况下检测UTF-8能通过,但有BOM时却不行?

后端开发 2026-07-08

我有一个文件读取器,它读取的文本文件都是UTF-8,但有些没有字节顺序标记(BOM)。 基本上,这将给我我在寻找的结果:

var utf8NoBom = new UTF8Encoding(false);
aReaderWithAFile = new StreamReader(fileName, utf8NoBom)
aReaderWithAFile.Read()
if (Equals(aReaderWithAFile.CurrentEncoding, utf8NoBom))
{
    Console.WriteLine("No BOM detected")
}
else
{
    Console.WriteLine("BOM detected");
}

但这将始终显示“检测到BOM”,即使文件没有BOM。

var utf8WithBom = new UTF8Encoding(true);
aReaderWithAFile = new StreamReader(fileName, utf8WithBom)
aReaderWithAFile.Read()
if (Equals(aReaderWithAFile.CurrentEncoding, utf8WithBom))
{
    Console.WriteLine("BOM detected");
}
else
{
    Console.WriteLine("No BOM detected");
}

有人能解释一下,为什么会这样吗?

编辑:为澄清。我用第一个示例来检查一个文件是否带BOM的 UTF-8,这个问题已经解决。第二个示例将不起作用,因为我会得到误报。 我想要的只是理解这个行为。为什么在第二个示例中Equals会返回true? 我想知道背后的技术原因。

解决方案

一个 StreamReader,在构造函数中没有设置编码,将始终默认为 Encoding.UTF8,这在 等价于 new UTF8Encoding(true)。在这种情况下,CurrentEncoding 报告的编码对UTF-8文件而言永远不会改变,无论 detectEncodingFromByteOrderMarks ‒ 如果 trueStreamReader 只是检查流开头的UTF-8前缀,并在存在时跳过它,而不需要干涉编码。简单地说,它并不关心此时的编码是否为UTF-8,它只是检查是否存在预期的前缀,因此如果在那里找不到它,就不知道要把 new UTF8Encoding(true) 改为 new UTF8Encoding(false)

你可以在这里看到将要执行的相关代码 这里 。请注意对 IsPreamble 调用的注释:

// Check for preamble before detect encoding. This is not to override the // user supplied Encoding for the one we implicitly detect. The user could // customize the encoding which we will loose, such as ThrowOnError on UTF8

这与编码自动检测不同,编码自动检测发生在随后,在对 DetectEncoding 的调用中。此方法实际上可以改变 CurrentEncoding 的值。

通过显式传入一个 new UTF8Encoding(false) 来初始化 StreamReader,以利用这一自动检测能力(将 detectEncodingFromByteOrderMarks 维持在 true)。在这种情况下,这将指示默认编码,但 StreamReader 仍然会寻找一个已识别的Unicode BOM,并且可以把 CurrentEncoding 切换到它在那里找到的编码(UTF-8、UTF-16小端、UTF-16大端、UTF-32小端以及UTF-32大端)。在这种情况下,你实际上可以区分是否存在用于UTF-8的 BOM,因为它将开始将编码报告为 Encoding.UTF8

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章