Encoding.UTF8.GetString返回含BOM字符串的设计逻辑咨询
UTF8.GetString 保留BOM而StreamReader自动移除的设计原因
首先看这段写入内存流的代码:
using var mem = new MemoryStream(); await using var writer = new StreamWriter(mem, Encoding.UTF8); await writer.WriteLineAsync("Test"); await writer.FlushAsync(); mem.Position = 0;
用Encoding.UTF8.GetString读取时会抛出异常:
var x = Encoding.UTF8.GetString(mem.ToArray()); if (x[0] != 'T') throw new Exception("Bom is present in string");
原因是返回的字符串包含UTF-8 BOM(U+FEFF),导致第一个字符不是预期的'T'。
而用StreamReader读取则能正常运行,返回的字符串不含BOM:
using var reader = new StreamReader(mem, Encoding.UTF8); var x = await reader.ReadToEndAsync(); if (x[0] != 'T') throw new Exception("Bom is present in string");
设计差异的核心原因
这两个API的定位完全不同:
Encoding.GetString是严格的字节-字符解码器
它的职责是精准转换输入的每一段字节序列,不会做任何额外的语义处理。BOM是StreamWriter写入的字节的一部分,解码后自然会保留对应的U+FEFF字符。这个API是给需要底层字节控制的场景设计的——比如解析文件头、处理自定义二进制格式时,你需要完整还原字节对应的所有字符,包括编码标识类的BOM。StreamReader是文本内容读取工具
它专门为读取“可阅读的文本”设计,默认遵循文本文件的通用规则:UTF-8 BOM只是用来标记文件编码的元信息,不属于文本内容本身,所以会自动检测并移除BOM,返回纯粹的文本内容。这符合绝大多数开发者读取文本时的预期:拿到的是实际写入的内容,而非编码相关的标记。
额外提示:如果不想让StreamWriter写入BOM,可以创建编码时指定new UTF8Encoding(false)(第二个参数encoderShouldEmitUTF8Identifier设为false),这样两种读取方式都会得到不含BOM的字符串。
内容的提问来源于stack exchange,提问作者Anders
相关产品推荐
相关产品推荐

