【问题标题】:Encoding trouble with HttpWebResponseHttpWebResponse 的编码问题
【发布时间】:2023-04-04 02:25:01
【问题描述】:

这是一个sn-p的代码:

HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(request.RawUrl);
WebRequest.DefaultWebProxy = null;//Ensure that we will not loop by going again in the proxy
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
string charSet = response.CharacterSet;
Encoding encoding;
if (String.IsNullOrEmpty(charSet))
encoding = Encoding.Default;
else
encoding = Encoding.GetEncoding(charSet);

StreamReader resStream = new StreamReader(response.GetResponseStream(), encoding);
return resStream.ReadToEnd();

问题是如果我测试:http://www.google.fr

所有“é”都没有很好地显示。我尝试将 ASCII 更改为 UTF8,但它仍然显示错误。我已经在浏览器中测试了 html 文件,并且浏览器很好地显示了 html 文本,所以我很确定问题出在我用来下载 html 文件的方法上。

我应该改变什么?

删除了无效的 ImageShack 链接

更新 1:代码和测试文件已更改

【问题讨论】:

  • "é" 应该仍然可以工作,即使是 ASCII。您是在输出到一个文件并确定它不工作,还是在返回的 sb.ToString() 上中断指向并在 Quick Watch 中查看它并确定它失败了?
  • 不,尖锐的重音在 ASCII 中永远不会起作用,它只包含最多 127 位的 Unicode。
  • (以防万一有人反对并谈论“扩展 ASCII” - 请参阅 msdn.microsoft.com/en-us/library/…
  • 这里的 zabulus 答案怎么样?看起来简单多了:stackoverflow.com/questions/7634113/…
  • 这和 Jon 4 年前回答的差不多 :)

标签: c# encoding


【解决方案1】:

首先,编写该代码的更简单方法是使用 StreamReader 和 ReadToEnd:

HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(myURL);
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
    using (Stream resStream = response.GetResponseStream())
    {
        StreamReader reader = new StreamReader(resStream, Encoding.???);
        return reader.ReadToEnd();
    }
}

那么“只是”找到正确编码的问题。你是如何创建文件的?如果它使用记事本,那么您可能想要Encoding.Default - 但这显然是不可移植的,因为它是您的 PC 的默认编码。

在运行良好的 Web 服务器中,响应将在其标头中指明编码。话虽如此,在某些情况下,响应标头有时会声明一件事,而 HTML 声明另一件事。

【讨论】:

  • 事实上,我正在尝试在世界各地获取文件,但我得到了一些错误的输出(PNG 文件格式不正确)并且文本写得不好(所有字符都像“é”)。
  • 如果您尝试读取任意 HTML,则需要检查标头,有时还需要检查 HTML 的开头(它可以像 XML 一样宣传编码)。有时你必须检测它可能不正确并通过启发式猜测!
  • 好的,我看一下标题。我和你一起玩过代码并且 StreamReader(resStream, true) 不起作用(应该找到带有字节的编码......)我会尝试从标题中获取它。我稍后会发布。
  • 只是想知道您是否了解这些年来“确定在服务器不提供时使用什么字符集”变得多么复杂(请参阅 my comment 对 Alex 的回答。)
  • @LynnCrumbling:不,还没有。
【解决方案2】:

如果在服务器的内容类型标头中没有指定CharacterSet,则默认为“ISO-8859-1”(与HTML 中的“charset”元标记不同)。 我将 HttpWebResponse.CharacterSet 与 HTML 的 charset 属性进行比较。如果它们不同 - 我使用 HTML 中指定的字符集再次重新读取页面,但这次使用正确的编码。

查看代码:

    string strWebPage = "";
    // create request
    System.Net.WebRequest objRequest = System.Net.HttpWebRequest.Create(sURL);
    // get response
    System.Net.HttpWebResponse objResponse;
    objResponse = (System.Net.HttpWebResponse)objRequest.GetResponse();
    // get correct charset and encoding from the server's header
    string Charset = objResponse.CharacterSet;
    Encoding encoding = Encoding.GetEncoding(Charset);
    // read response
    using (StreamReader sr = 
           new StreamReader(objResponse.GetResponseStream(), encoding))
    {
        strWebPage = sr.ReadToEnd();
        // Close and clean up the StreamReader
        sr.Close();
    }

    // Check real charset meta-tag in HTML
    int CharsetStart = strWebPage.IndexOf("charset=");
    if (CharsetStart > 0)
    {
        CharsetStart += 8;
        int CharsetEnd = strWebPage.IndexOfAny(new[] { ' ', '\"', ';' }, CharsetStart);
        string RealCharset = 
               strWebPage.Substring(CharsetStart, CharsetEnd - CharsetStart);

        // real charset meta-tag in HTML differs from supplied server header???
        if(RealCharset!=Charset)
        {
            // get correct encoding
            Encoding CorrectEncoding = Encoding.GetEncoding(RealCharset);

            // read the web page again, but with correct encoding this time
            //   create request
            System.Net.WebRequest objRequest2 = System.Net.HttpWebRequest.Create(sURL);
            //   get response
            System.Net.HttpWebResponse objResponse2;
            objResponse2 = (System.Net.HttpWebResponse)objRequest2.GetResponse();
            //   read response
            using (StreamReader sr = 
              new StreamReader(objResponse2.GetResponseStream(), CorrectEncoding))
            {
                strWebPage = sr.ReadToEnd();
                // Close and clean up the StreamReader
                sr.Close();
            }
        }
    }

【讨论】:

  • 我认为这应该被标记为答案。这实际上是从任何网页获取编码并正确解码它们。但问题是这在 Windows phone 中不起作用,因为它的响应实现不支持 Response.CharacterSet
  • 太棒了!正是我想要的。我已经有一个循环来重试意外错误,所以我只需要将 charset 和 realcharset 转换为局部变量以避免请求的额外声明。
  • 嗯,现在是 2020 年,这不再是真的了。事实上,它变得非常复杂。有关这只野兽的完整摘要,请查看this answer。 TL;博士:RFC 7231 现在说没有定义编码,除非你是 XML 内容,在这种情况下,它是 us-ascii。但当然,它的意义远不止于此。
【解决方案3】:

如果您不想下载该页面两次,我使用 How do I put a WebResponse into a memory stream? 稍微修改了 Alex 的代码。这是结果

public static string DownloadString(string address)
{
    string strWebPage = "";
    // create request
    System.Net.WebRequest objRequest = System.Net.HttpWebRequest.Create(address);
    // get response
    System.Net.HttpWebResponse objResponse;
    objResponse = (System.Net.HttpWebResponse)objRequest.GetResponse();
    // get correct charset and encoding from the server's header
    string Charset = objResponse.CharacterSet;
    Encoding encoding = Encoding.GetEncoding(Charset);

    // read response into memory stream
    MemoryStream memoryStream;
    using (Stream responseStream = objResponse.GetResponseStream())
    {
        memoryStream = new MemoryStream();

        byte[] buffer = new byte[1024];
        int byteCount;
        do
        {
            byteCount = responseStream.Read(buffer, 0, buffer.Length);
            memoryStream.Write(buffer, 0, byteCount);
        } while (byteCount > 0);
    }

    // set stream position to beginning
    memoryStream.Seek(0, SeekOrigin.Begin);

    StreamReader sr = new StreamReader(memoryStream, encoding);
    strWebPage = sr.ReadToEnd();

    // Check real charset meta-tag in HTML
    int CharsetStart = strWebPage.IndexOf("charset=");
    if (CharsetStart > 0)
    {
        CharsetStart += 8;
        int CharsetEnd = strWebPage.IndexOfAny(new[] { ' ', '\"', ';' }, CharsetStart);
        string RealCharset =
               strWebPage.Substring(CharsetStart, CharsetEnd - CharsetStart);

        // real charset meta-tag in HTML differs from supplied server header???
        if (RealCharset != Charset)
        {
            // get correct encoding
            Encoding CorrectEncoding = Encoding.GetEncoding(RealCharset);

            // reset stream position to beginning
            memoryStream.Seek(0, SeekOrigin.Begin);

            // reread response stream with the correct encoding
            StreamReader sr2 = new StreamReader(memoryStream, CorrectEncoding);

            strWebPage = sr2.ReadToEnd();
            // Close and clean up the StreamReader
            sr2.Close();
        }
    }

    // dispose the first stream reader object
    sr.Close();

    return strWebPage;
}

【讨论】:

  • .NET 4 及更高版本应该有一个 Stream.CopyTo(Stream) 方法来简化它。
  • 为什么必须将缓冲区大小设置为 1024?不能一口气读完整个流吗?为什么是 1024?为什么不把它调大一点?
【解决方案4】:

我在强大的协议分析器 WireShark 的帮助下研究了同样的问题。我认为 httpWebResponse 类存在一些设计缺陷。实际上,整个消息实体是在你第一次调用 HttpWebRequest 类的 GetResponse() 方法时下载的,但是框架没有地方将数据保存在 HttpWebResponse 类或其他地方,导致你必须获取响应流第二次。

【讨论】:

    【解决方案5】:

    从 WebRequest 请求网页“www.google.fr”时仍然存在一些问题。

    我用 Fiddler 检查了原始请求和响应。问题来自谷歌服务器。响应 HTTP 标头设置为 charset=ISO-8859-1,文本本身使用 ISO-8859-1 编码,而 HTML 表示 charset=UTF-8。这是不连贯的,并导致编码错误。

    经过多次测试,我设法找到了解决方法。只需添加:

    myHttpWebRequest.UserAgent = "Mozilla/5.0";
    

    到您的代码,Google Response 将神奇地完全变成 UTF-8。

    【讨论】:

      【解决方案6】:

      这是一次下载的代码。

      String FinalResult = "";
      HttpWebRequest Request = (HttpWebRequest)System.Net.WebRequest.Create( URL );
      HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
      Stream ResponseStream = Response.GetResponseStream();
      StreamReader Reader = new StreamReader( ResponseStream );
      
      bool NeedEncodingCheck = true;
      
      while( true )
      {
          string NewLine = Reader.ReadLine(); // it may not working for zipped HTML.
          if( NewLine == null )
          {
              break;
          }
      
          FinalResult += NewLine;
          FinalResult += Environment.NewLine;
      
          if( NeedEncodingCheck )
          {
              int Start = NewLine.IndexOf( "charset=" );
              if( Start > 0 )
              {
                  Start += "charset=\"".Length;   
                  int End = NewLine.IndexOfAny( new[] { ' ', '\"', ';' }, Start );
      
                  Reader = new StreamReader( ResponseStream, Encoding.GetEncoding(
                      NewLine.Substring( Start, End - Start ) ) ); // Replace Reader with new encoding.
      
                  NeedEncodingCheck = false;
              }
          }
      }
      
      Reader.Close();
      Response.Close();
      

      【讨论】:

        【解决方案7】:

        这里有一些很好的解决方案,但它们似乎都试图从内容类型字符串中解析出字符集。这是一个使用 System.Net.Mime.ContentType 的解决方案,它应该更可靠,更短。

         var client = new System.Net.WebClient();
         var data = client.DownloadData(url);
         var encoding = System.Text.Encoding.Default;
         var contentType = new System.Net.Mime.ContentType(client.ResponseHeaders[HttpResponseHeader.ContentType]);
         if (!String.IsNullOrEmpty(contentType.CharSet))
         {
              encoding = System.Text.Encoding.GetEncoding(contentType.CharSet);
         }
         string result = encoding.GetString(data);
        

        【讨论】:

          猜你喜欢
          • 2019-04-26
          • 2012-02-26
          • 2023-03-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-13
          相关资源
          最近更新 更多