感觉奇怪。给浏览器输入一个网址,为什么浏览器能够以正确的编码方式来显示页面?
如果说浏览器先把页面以string 形式下下来。那也只是把二进制码下了下来。怎么把二进制代码转换为人眼能看懂的字符?
应该要先读出chatset后面的编码方式,然后以这个编码方式把整个网页编码显示。那么问题是chatset本身就是要在知道编码方式的前提下才能解析进而获得编码方式。大家来讨论一下。
如果说浏览器先把页面以string 形式下下来。那也只是把二进制码下了下来。怎么把二进制代码转换为人眼能看懂的字符?
应该要先读出chatset后面的编码方式,然后以这个编码方式把整个网页编码显示。那么问题是chatset本身就是要在知道编码方式的前提下才能解析进而获得编码方式。大家来讨论一下。
参考.using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;class Program
{
// 获取网页的HTML内容,根据网页的charset自动判断Encoding
static string GetHtml(string url)
{
return GetHtml(url, null);
} // 获取网页的HTML内容,指定Encoding
static string GetHtml(string url, Encoding encoding)
{
byte[] buf = new WebClient().DownloadData(url);
if (encoding != null) return encoding.GetString(buf);
string html = Encoding.UTF8.GetString(buf);
encoding = GetEncoding(html);
if (encoding == null || encoding == Encoding.UTF8) return html;
return encoding.GetString(buf);
} // 根据网页的HTML内容提取网页的Encoding
static Encoding GetEncoding(string html)
{
string pattern = @"(?i)\bcharset=(?<charset>[-a-zA-Z_0-9]+)";
string charset = Regex.Match(html, pattern).Groups["charset"].Value;
try { return Encoding.GetEncoding(charset); }
catch (ArgumentException) { return null; }
} // 根据网页的HTML内容提取网页的Title
static string GetTitle(string html)
{
string pattern = @"(?si)<title(?:\s+(?:""[^""]*""|'[^']*'|[^""'>])*)?>(?<title>.*?)</title>";
return Regex.Match(html, pattern).Groups["title"].Value.Trim();
} // 打印网页的Encoding和Title
static void PrintEncodingAndTitle(string url)
{
string html = GetHtml(url);
Console.WriteLine("[{0}] [{1}]", GetEncoding(html), GetTitle(html));
} // 程序入口
static void Main()
{
PrintEncodingAndTitle("http://www.msdn.net/");
PrintEncodingAndTitle("http://www.cnblogs.com/");
PrintEncodingAndTitle("http://www.cnblogs.com/skyiv/");
PrintEncodingAndTitle("http://www.csdn.net/");
PrintEncodingAndTitle("http://news.163.com/");
}
}
/* 程序输出:
[] [MSDN: Microsoft Developer Network]
[System.Text.UTF8Encoding] [博客园 - 程序员的网上家园]
[System.Text.UTF8Encoding] [空间/IV - 博客园]
[System.Text.UTF8Encoding] [CSDN.NET - 中国最大的IT技术社区,为IT专业技术人员提供最全面的信息传播和服务平台]
[System.Text.DBCSCodePageEncoding] [新闻中心_网易新闻]
*/
和页面的Head里面的meta http-equiv="Content-Type" content="text/html; charset=GB2312
// 根据网页的HTML内容提取网页的Encoding
static Encoding GetEncoding(string html)
{
string pattern = @"(?i)\bcharset=(?<charset>[-a-zA-Z_0-9]+)";
string charset = Regex.Match(html, pattern).Groups["charset"].Value;
try { return Encoding.GetEncoding(charset); }
catch (ArgumentException) { return null; }
}我请问,你用到的html,它是个字符串,既然要从它里面匹配charset出来,就说明事先已经对它进行了编码解析了(注意:同一二进制流,以不同方式编码,显然会得到不同的字符串,即使是乱码也是字符串呀),而浏览器是怎么知道用什么编码方式来编码页面的二进制流的呢。另外,你用了正则,那我想,pattern是什么编码方式的,Regex.Match()这个方法就应该会以什么编码方式对html进行了匹配的,但是你的pattern到底是以什么方式编码的呢。
有朋友说浏览器通过responseHeader来知道页面的编码方式。那应该是web服务器与浏览器之间有一个默认的对responseHeader的编码规范,比如responseHeader一定要用ANSI编码,这样才可能再没有任何编码提示的情况下正确解析页面。
浏览器正确解析页面,显示出来,用户能看懂后。我想问,程序员用程序把页面字符串获得后,要从中取得某些字符或者匹配某些内容。又是怎么知道页面字符串是什么编码的?因为既然已经可以用代码来处理了,就说明下载已经完成,不可能再去读responseHeader了。
说是.net环境里,字符都是以utf16编码的。那就是不管responseHeader里是什么编码,或者chatset是什么,一律编码为utf16? utf16这个编码方式不大了解,可能是一个字符用比其它编码方式都多的“位”来存储,因为只有这样才能“通吃”其它编码方式,而不会乱码。有了解的可以说说。
假设.net环境下都是这种编码的话。那么说我们写的代码都是这个编码的,从外界读取的字符等都是这个方式编码的,那就统一了,对字符串操作也就自然不用每次都指定编码方式了,呵呵,是不是这样理解的,大鸟出来放话。
用代码也可以读取responseHeader.如果responseHeader里提供了编码方式数据,就很容易处理了。
问题是,很多网站的responseHeader并没有加入编码方式。在这种情况下,就只能针对具体的网站进行具体处理。
while ((line = reader.ReadLine()) != null)
{
buffer += line + "\r\n";
}
string strEncoding = GetEncoding(buffer); Encoding encoding;
if (strEncoding.ToUpper() == "UTF-8")
encoding = Encoding.UTF8;
else if (strEncoding.ToUpper() == "UTF-7")
encoding = Encoding.UTF7;
else if (strEncoding.ToUpper() == "UNICODE")
encoding = Encoding.Unicode;
else if (strEncoding.ToUpper() == "GB2312")
encoding = Encoding.GetEncoding("GB2312");
else if (strEncoding.ToUpper() == "BIG5")
encoding = Encoding.GetEncoding("BIG5");
else
encoding = Encoding.Default; if (encoding != Encoding.Default)
{
request.Timeout = 6000;
request = (HttpWebRequest)WebRequest.Create(strUrl);
}
/// <summary>
/// 获取HTML文件编码
/// </summary>
/// <param name="inputString">HTML文件</param>
/// <returns></returns>
public static string GetEncoding(string inputString)
{
Regex r = new Regex("charset\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))", RegexOptions.IgnoreCase);
Match m = r.Match(inputString);
return m.Groups[1].Value.Replace("\"", "").Replace(">", "").ToUpper();
}
#endregion
}
/// 检查参数字节数组内容的编码,主要判断是UTF-8或是GB2312/GBK
/// Created by: b_wind, 2009-10
/// </summary>
/// <param name="raw">包含文本数据的字节数组</param>
/// <returns>推断出的数据编码</returns>
public static Encoding DetectEncoding(byte[] raw)
{
if (raw.Length >= 3)
{
if (raw[0] == 0xEF && raw[1] == 0xBB && raw[2] == 0xBF)
return Encoding.UTF8;
}
if (raw.Length >= 2)
{
if (raw[0] == 0xFF && raw[1] == 0xFE)
return Encoding.Unicode;
else if (raw[0] == 0xFE && raw[1] == 0xFF)
return Encoding.BigEndianUnicode;
}
int utf8_freq = 0;
for (int i = 0; i < raw.Length; i++)
{
int c = GetByte1Count(raw[i]);
if (c == 0)
continue;
if (c == 1 && i + 1 <= raw.Length - 1)
{
if ((raw[i + 1] >> 7) == 1)
return Encoding.Default;
}
if (c + i > raw.Length)
break;
for (int j = 1; j < c; j++)
{
if ((raw[i + j] >> 6) != 2)
{
return Encoding.Default;
}
else
{
if (j == c - 1)
{
utf8_freq++;
i += j;
}
}
}
if (utf8_freq >= 5)
return Encoding.UTF8;
}
if (utf8_freq > 2)
return Encoding.UTF8;
else
return Encoding.Default;
} private static int GetByte1Count(byte b)
{
if ((b & 0xFC) == 0xFC)
return 6;
else if ((b & 0xF8) == 0xF8)
return 5;
else if ((b & 0xF0) == 0xF0)
return 4;
else if ((b & 0xE0) == 0xE0)
return 3;
else if ((b & 0xC0) == 0xC0)
return 2;
else if ((b & 0x80) == 0x80)
return 1;
else
return 0;
}
Server: CWS/1.0.64
Date: Sun, 17 Jan 2010 10:33:33 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
X-UA-Compatible: IE=EmulateIE7
X-Powered-By: ASP.NET
Last-modified: 2010-01-17 18:26:17
Cache-control: private
Content-Encoding: gzip
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>编码问题,怎么取得页面的编码方式?进者分,17来讨论</title>
<link href="http://c.csdn.net/bbs/t/5/t5.css" rel="stylesheet" type="text/css" />
<link href="http://www.csdn.net/images/favicon.ico" rel="SHORTCUT ICON" />
<script type="text/javascript">
var tinfo = {
pdate: "2010-01-12 21:20:57"
,sid: "3036657c-277c-476c-982d-75f154e09050"
,tid: "d4563a47-bd15-4ec9-9417-f3a400b5a4cf"
,ba: "DotNET"
,sa: "ASPDotNET"
};
</script>
<script type="text/javascript" src="/u/t5/t5.js"></script>
<script type="text/javascript" src="http://counter.csdn.net/a/js/AreaCounter.js"></script>
</head>
<body id="bbscsdn_wrap"><div class="tad">
<iframe id="Topic_Top" marginwidth="0" marginheight="0" frameborder="0" scrolling="no" width="100%" height="0" src="/u/t5/include/ad1.asp" ></iframe>
</div>
<div class="nav">
<a href="#"><img src="http://c.csdn.net/bbs/t/5/i/pic_logo.gif" alt="" class="logo" /></a>
<ul class="txt">
<li><a href="http://www.csdn.net/" target="_blank">首页</a>|</li>
<li><a href="http://hi.csdn.net/" target="_blank">空间</a>|</li>
<li><a href="http://news.csdn.net/" target="_blank">新闻</a>|</li>
<li><a href="http://bbs.csdn.net/" target="_blank">论坛</a>|</li>
<li><a href="http://blog.csdn.net/" target="_blank">博客</a>|</li>
<li><a href="http://download.csdn.net/" target="_blank">下载</a>|</li>
<li><a href="http://book.csdn.net/" target="_blank">读书</a>|</li>
<li><a href="http://wz.csdn.net/" target="_blank">网摘</a>|</li>
<li><a href="http://live.csdn.net/" target="_blank">视频</a>|</li>
<li><a href="http://www.dearbook.com.cn/" target="_blank">书店</a>|</li>
<li><a href="http://www.programmer.com.cn/" target="_blank">程序员</a>|</li>
<!--<li><a href="http://www.itliyu.com/" target="_blank">求职招聘</a>|</li>-->
<li><a href="http://prj.csdn.net/" target="_blank">项目交易</a>|</li>
<li><a href="http://training.csdn.net/" target="_blank">培训</a>|</li>
<li><a href="http://daohang.csdn.net/" target="_blank">网址</a></li>
</ul>
.................[/code]
Server: CWS/1.0.64
Date: Sun, 17 Jan 2010 10:33:33 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
X-UA-Compatible: IE=EmulateIE7
X-Powered-By: ASP.NET
Last-modified: 2010-01-17 18:26:17
Cache-control: private
Content-Encoding: gzip
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>编码问题,怎么取得页面的编码方式?进者分,17来讨论</title>
<link href="http://c.csdn.net/bbs/t/5/t5.css" rel="stylesheet" type="text/css" />
<link href="http://www.csdn.net/images/favicon.ico" rel="SHORTCUT ICON" />
<script type="text/javascript">
var tinfo = {
pdate: "2010-01-12 21:20:57"
,sid: "3036657c-277c-476c-982d-75f154e09050"
,tid: "d4563a47-bd15-4ec9-9417-f3a400b5a4cf"
,ba: "DotNET"
,sa: "ASPDotNET"
};
</script>
<script type="text/javascript" src="/u/t5/t5.js"></script>
<script type="text/javascript" src="http://counter.csdn.net/a/js/AreaCounter.js"></script>
</head>
<body id="bbscsdn_wrap"><div class="tad">
<iframe id="Topic_Top" marginwidth="0" marginheight="0" frameborder="0" scrolling="no" width="100%" height="0" src="/u/t5/include/ad1.asp" ></iframe>
</div>
<div class="nav">
<a href="#"><img src="http://c.csdn.net/bbs/t/5/i/pic_logo.gif" alt="" class="logo" /></a>
<ul class="txt">
<li><a href="http://www.csdn.net/" target="_blank">首页</a>|</li>
<li><a href="http://hi.csdn.net/" target="_blank">空间</a>|</li>
<li><a href="http://news.csdn.net/" target="_blank">新闻</a>|</li>
<li><a href="http://bbs.csdn.net/" target="_blank">论坛</a>|</li>
<li><a href="http://blog.csdn.net/" target="_blank">博客</a>|</li>
<li><a href="http://download.csdn.net/" target="_blank">下载</a>|</li>
<li><a href="http://book.csdn.net/" target="_blank">读书</a>|</li>
<li><a href="http://wz.csdn.net/" target="_blank">网摘</a>|</li>
<li><a href="http://live.csdn.net/" target="_blank">视频</a>|</li>
<li><a href="http://www.dearbook.com.cn/" target="_blank">书店</a>|</li>
<li><a href="http://www.programmer.com.cn/" target="_blank">程序员</a>|</li>
<!--<li><a href="http://www.itliyu.com/" target="_blank">求职招聘</a>|</li>-->
<li><a href="http://prj.csdn.net/" target="_blank">项目交易</a>|</li>
<li><a href="http://training.csdn.net/" target="_blank">培训</a>|</li>
<li><a href="http://daohang.csdn.net/" target="_blank">网址</a></li>
</ul>
...............
谢谢vrhero同志的回答。你说“.NET在内存中的字符编码是UTF-16,但存储是用UTF-8”
那么我想推一下理:我们用vs打开一个.cs文件并编辑,这个过程应该是:
第一步:.net把以文件形式存储在硬盘上的.cs文件载入内存。这一步是把utf-8的编码转化为utf-16;
第二步:.net把内存中的这个.cs文件在.net环境下显示出来(就是在vs代码编辑器中)。为什么要强调是在".net环境下显示出来"呢?因为既然是utf-16编码的,其它的程序(比如计事本)就无法正确显示,因为它们不具体编码和解码utf-16的功能。而且这里还有个前提是:.net环境要维护一个对应关系:即每个utf-16字符对应的具体的人可以看明白的“字”。
第三步:我们要编辑改变这个.cs文件的内容。我们要通过键盘输入“字符”,或者更准确的讲是“一串按键动作”。那么我们输入的“字符”应该是先经过“输入法”处理的!我们切掉不同的输入法,按下一串相同的键,得到的字符从二进制角度讲是一样的,但是显示出来的“字符”是不一样的,也就是对同样的“信息”的编码是不同的。我们直接在vs编辑器中对.cs文件进行输入动作,那实际上是vs编辑器从输入法程序中获得“信息”并正确编码。而且要同时完成两个步骤:以utf-16编码方式对传过来的其它编码方式的“信息”往内存进行转存;并以utf-8编码方式往硬盘进行转存。总结:.net以uft-16来工作;以utf-8来保存结果,以备与其它程序兼容。
不知道以上理解是否正确,希望大家来确认。
对于此楼的回答,我的回答是,其实我想问的是:浏览器是怎么从页面请求的回复中读出(解析出)下面这些东西的:
TTP/1.1 200 OK
Server: CWS/1.0.64
Date: Sun, 17 Jan 2010 10:33:33 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
X-UA-Compatible: IE=EmulateIE7
X-Powered-By: ASP.NET
Last-modified: 2010-01-17 18:26:17
Cache-control: private
Content-Encoding: gzip
特别是:Content-Type: text/html; charset=utf-8这一行。
当然其它可以设想一下,要获得这些信息其它很容易,前提是又方有个“约定”(服务器和浏览器)。
什么约定呢?就是比如服务器向浏览器约定:我给你回复的内容,不管长短,前100位都是我们通讯所固定占据的位置。这些固定位置的信息全部以ascii编码,而不管其它内容是怎么编码的。在这100个固定位置里的第50~60位存储的是回复其它的内容(也就是用户真正希望看到的信息)的编码信息。
有了如上约定当然可以正确解析出任何服务器的回复了。
不知道我设想的对不对呢?