WebClient类的DownloadString方法的缺陷,使用downloaddata更好

来源:互联网 发布:php 对象类型转换 编辑:程序博客网 时间:2024/04/29 14:04

问题发现:

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

 


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()的源代码片段:

 

 


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

 

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

 

    然后看看猜测的过程

 


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 str2in strArray)
        {
           
if (str2== "charset")
            {
                flag
= true;
            }
           
else if (flag)
            {
               
return Encoding.GetEncoding(str2);
            }
        }
    }
   
catch (Exception exception)
    {
       
if (((exceptionis ThreadAbortException)|| (exceptionis StackOverflowException))|| (exceptionis 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来编码


原创粉丝点击