WebClient类的DownloadString方法的缺陷

问题发现:

用以下代码获取的网页源代码,大部分中文显示正常,一部分成为??

 

ContractedBlock.gif ExpandedBlockStart.gif Code
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
namespace Test_GetUTF8Website
{
   
class Program
    {
       
static void Main(string[] args)
        {
            WebClient client
= new WebClient();
           
string s = client.DownloadString("http://www.cnblogs.com");
           
string res = Encoding.UTF8.GetString(Encoding.Default.GetBytes(s));
            Console.WriteLine(res);
            Console.ReadLine();
        }
    }
}

 

思考:

    先看看DownloadString()的源代码片段:

 

 

ContractedBlock.gifExpandedBlockStart.gif
WebRequest request;
byte[] bytes = this.DownloadDataInternal(address, out request);
string retValue = this.GuessDownloadEncoding(request).GetString(bytes);

 

    DownloadString()首先把数据下载回来,形式是byte[],然后猜测数据的编码,问题就在于它是猜测- -

 

    然后看看猜测的过程

 

ContractedBlock.gif ExpandedBlockStart.gif Code
private Encoding GuessDownloadEncoding(WebRequest request)
{
   
try
    {
       
string contentType = request.ContentType;
       
if (contentType == null)
        {
           
return this.Encoding;
        }
       
string[] strArray = contentType.ToLower(CultureInfo.InvariantCulture).Split(new char[] { ';', '=', ' ' });
       
bool flag = false;
       
foreach (string str2 in strArray)
        {
           
if (str2 == "charset")
            {
                flag
= true;
            }
           
else if (flag)
            {
               
return Encoding.GetEncoding(str2);
            }
        }
    }
   
catch (Exception exception)
    {
       
if (((exception is ThreadAbortException) || (exception is StackOverflowException)) || (exception is OutOfMemoryException))
        {
           
throw;
        }
    }
   
catch
    {
    }
   
return this.Encoding;
}

 

 
 

    可以看出,它获取request的ContentType来猜测Charset。Everything seems to be OK so far.

    问题在于谁来给request的ContentType赋值呢?
    在DownloadString()里面,WebRequest声明后到猜测前,只有DownloadDataInternal函数,也是DownloadDataInternal()给request赋值。而DownloadDataInternal()里面是GetWebRequest()给request赋值。

    GetWebRequest()里面的CopyHeadersTo()有ContentType的赋值,但是无论是http://www.cnblogs.com还是http://g.cn都无法获得ContentType

    现在ContentType找不到,WebClient会以Encoding.Default(在简体中文windows里是GB2132,CodePage=936的Encoding)把byte[]进行编码成字符串,传统的思路是自己获得网页的编码,自己把编码后的字符串解码回byte[],再用网页的编码编码为字符串
    但是诡异的是,如果我们用Encoding.Default把byte[]编码成字符串,再同样用Encoding.Default解码回byte[],前后两个byte[]的长度会不同。这样就导致我们把DownloadString()返回的字符串解码成byte[]回来,这个byte[]已经是被修改了的。自然我们手动改成正确编码时字符串会出错。

    ps:我留意到,错误显示为??的地方,在本来的html里面是中文紧跟符号(尖括号,逗号等),猜测是符号的字节对齐导致编解码的错误,具体有待进一步考证。

 

解决方法:使用webclient.DownloadData方法获取原汁原味的byte[],再手动用相应的Encoding来编码

转载于:https://www.cnblogs.com/ukessi/archive/2009/02/09/1386567.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值