使用WebClient和WebRequest之间的编码差异?

4
在使用 WebRequest 获取某个西班牙报纸的索引时,我无法正确地获取发音符号。它们会产生这种奇怪的字符:。但是,使用 WebClient 下载来自同一 URI 的响应时,我可以得到适当的响应。
为什么会出现这种差异呢?
var client = new WebClient();
string html = client.DownloadString(endpoint);

vs

WebRequest request = WebRequest.Create(endpoint);
using (WebResponse response = request.GetResponse())
{
    Stream stream = response.GetResponseStream();
    StreamReader reader = new StreamReader(stream);
    string html = reader.ReadToEnd();
}

我猜这可能是因为WebClient.DownloadString方法查看content-type头信息(如“text/html; charset=utf-16”)来推断编码方式,但您的WebRequest方法使用UTF8(即StreamReader的默认值)。 - spender
在这种情况下,“content-type”头部没有指定字符集。 - bevacqua
@Nico。然后,它应该根据前导(以检测BOM和其他提示)对UTF-8、UTF-16LE、UTF-16BE和UTF-32进行测试,最后默认使用默认的传统字符集,这恰好与您机器上的站点匹配。由于HTTP假定Latin-1(毕竟,这是90年代初),因此最好将其明确用作“我不知道”的选择。 - Jon Hanna
1个回答

4
您在创建流读取器时,只是假设实体采用UTF-8编码,而没有明确设置编码方式。您应该检查HttpWebResponseCharacterSet(在WebResponse基类中未公开),并使用适当的编码打开StreamReader
否则,如果它将不是UTF-8的内容读取为UTF-8,则会遇到在UTF-8中无效的八位序列,并且必须替换为U+FFFD替换字符()作为最佳解决方案。
WebClient几乎就是这样做的:DownloadString是一个更高级别的方法,其中WebRequest及其派生类让您进入较低级别,它有一个单独的调用,用于“发送GET请求到URI,检查头以查看正在使用哪种内容编码,以防需要取消gzip或解压缩,查看正在使用哪种字符编码,使用该编码和流设置文本读取器,然后调用ReadAll()”。正常的高级大块指令与低级小块指令的优缺点适用。

另外,如果您想镜像WebClient具体执行的操作,请将StreamReader更改为StreamReader reader = new StreamReader(stream, System.Text.Encoding.Default) - Chris Haas
@ChrisHaas 不,这样做只会更糟糕 - 尽管在某些情况下可能有效 - 因为它只适用于一种旧编码,而不适用于UTF-8。它检查头文件并设置正确的编码 - 这可能与System.Text.Encoding.Default相同,但很可能不同。如果没有在标头中明确请求字符集,则按顺序尝试UTF-8、UTF-16LE、UTF-16BE和UTF-32,看看结果是否有任何意义。最后,如果失败了,它将使用自己的“Encoding”属性。 - Jon Hanna
@ChrisHaas 我们假设如果没有找到 BOM 或者零八位字节,也没有显式设置 Encoding,那么 System.Text.Encoding.Default 将被使用。 - Jon Hanna
我只是在说如何模仿WebClient的行为,而不是评价它好坏。文档中提到WebClient默认使用System.Text.Encoding.Default。个人而言,在处理无法控制的网站时,我通常会检查原始字节本身,并在无法解决问题时回退到头部信息。 - Chris Haas
1
@ChrisHaas 我看到你是对的,文档确实是这样说的。但是代码比文档说的更好,如果你在ILSpy或Reflector中查看的话。它首先检查Content-Type头并尝试从中获取编码,然后进行一些BOM测试,最后使用其Encoding属性(默认为System.Text.Encoding.Default)作为最后的手段。 - Jon Hanna

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