为什么“&reg”被渲染为“®”而没有边界分号?

我遇到了一个问题,这个问题是通过我们的Google adwords驱动的营销活动揭示出来的。 使用的标准参数之一是“区域”。 当用户search并点击赞助商链接时,Google会生成一个长URL来跟踪点击,并在引用者中发送大量内容。 我们捕获这个logging,我们注意到“Region”参数是错误的。 应该是什么

http://ravercats.com/meow?foo=bar&region=catnip 

而是通过:

 http://ravercats.com/meow?foo=bar®ion=catnip 

我已经证实,这发生在所有的浏览器。 我的理解是HTML实体语法定义如下:

 &VALUE; 

前导边界是&符号,结束边界是分号。 看起来很简单。 问题在于,这个实体并没有得到尊重,并且在我们的系统中造成了各种各样的破坏。

有谁知道这是为什么发生? 这是DTD中的错误吗? (我正在寻找目前的HTML DTD,看看我是否可以理解它)我试图找出什么是常见的浏览器,使之发生,因此我寻找的DTD。

这是你可以使用的一个certificate。 拿这个代码,制作一个HTML文件,并在浏览器中显示它:

 <html> <a href="http://foo.com/bar?foo=bar&region=US&register=lowpass&reg_test=fail&trademark=correct">http://foo.com/bar?foo=bar&region=US&register=lowpass&reg_test=fail&trademark=correct</a> </html> 

编辑:对于所有build议我需要转义整个url的人,上面的示例url就是这个例子。 真正的url是直接来自Google,我无法控制它是如何构build的。 这些build议虽然有效,但并不回答“为什么会发生这种情况”。

尽pipe有效的字符引用在最后总是有分号,但为了兼容性的原因,一些不带分号的无效命名字符引用被现代浏览器的HTMLparsing器所识别。

要么你知道整个列表是什么,要么你遵循HTML5规则,当&是有效的,而不被转义(例如,后跟一个空格),否则总是以&amp; 每当有疑问时。

作为参考,不带分号的已识别字符引用的完整列表是:

AElig AMP Aacute Acirc Agrave Aring Auml COPY Ccedil ETH Eacute Ecirc Egrave Euml GT Iacute Icirc Igrave Iuml LT Ntilde Oacute Ocirc Ograve Oregon Oracute Oirute Oircum Ogrant Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Oircum Orau Oste,Otilde,Ouml,QUOT,REG,THORN,Uacute,Ucirc,Ugrave,Uuml,Yacute,aacute,acirc,acute,aelig,agrave,amp,aring,atilde,auml,brvbar,ccedil,cedil, curren,deg,divide,eacute,ecirc,egrave,eth,euml,frac12,frac14,frac34,gt,iacute,icirc,iexcl,igrave,iquest,iuml,laquo,lt,macr,micro,middot,nbsp, </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> </s> ugrave,uml,uuml,yacute,yen,yuml

但是,应该注意的是,只有在属性值中,如果下一个字符是a =或字母数字ASCII字符,则上述列表中的命名字符引用不会通过符合HTML5parsing器进行处理。

有关带或不带结尾分号的命名字符引用的完整列表,请参见此处

这是一个非常混乱的业务,取决于上下文(文本内容与属性值)。

forms上,通过HTML规范直到并包括HTML 4.01,如果下一个字符不是一个名称字符,则实体引用可能不会出现分号。 因此,例如&region=在语法上是正确的,但未定义,因为实体region尚未定义。 XHTML使得所需的尾随分号。

浏览器传统上是按照其他规则来玩的。 由于查询URL的通用语法,他们parsing例如href="http://ravercats.com/meow?foo=bar&region=catnip"以便&region不被视为实体引用,而仅仅是文本数据。 作者主要使用这样的结构,尽pipe它们在forms上是不正确的。

相反,这个问题似乎是说,实际上效果不错。 当string不在属性值中但在文本内容中时,会出现问题,这种情况并不常见:我们通常不会在文本中编写URL。 在文本中, &region=被处理,因此&reg被识别为实体引用(对于“®”),其余的只是字符数据。 这种奇怪的行为正在HTML5 CR中正式生效 ,其中条款8.2.4.69标记字符引用描述了“双重标准”:

如果字符引用正在作为属性的一部分被使用,并且匹配的最后一个字符不是“;” (U + 003B)字符,下一个字符是“=”(U + 003D)字符,或者在ASCII数字,大写ASCII字母或小写ASCII字母的范围内,那么由于历史原因,所有字符在U + 0026 AMPERSAND字符(&)之后匹配必须是未消耗的,并且不返回任何内容。

因此,在属性值中 ,即使&reg=不会被视为包含字符引用,并且更less&region= 。 (但由于下划线字符, reg_test=是不同的情况。)

文字内容中 ,适用其他规则。 构造&region=会导致一个parsing错误(通过HTML5 CR规则),但是具有定义良好的error handling: &reg被识别为字符引用。

也许试试把你的&amp; ? &符号是必须在HTML中转义的字符,因为它们被保留用作实体的一部分。

1:以下标记首先无效(使用W3C标记validation服务进行validation):

 <a href="http://foo.com/bar?foo=bar&region=US&register=lowpass&reg_test=fail&trademark=correct"></a> 

在上面的例子中, &字符应该被编码为&amp; 如下所示:

 <a href="http://foo.com/bar?foo=bar&amp;region=US&amp;register=lowpass&amp;reg_test=fail&amp;trademark=correct"></a> 

2:浏览器是宽容的; 他们试图从破碎的HTML中理解。 在你的情况下,所有可能有效的HTML实体都被转换成HTML实体。

逃离你的输出!

简单地说,您需要将url格式编码为html格式以获得准确的表示(理想情况下,您可以使用模板引擎variables转义函数,但使用htmlspecialchars($url)htmlentities($url)在php中除外)。

看到你的testing用例,然后在这个jsfiddle中正确编码的html: http : //jsfiddle.net/tchalvakspam/Fp3W6/

不活动的代码在这里:

 <div> Unescaped: <br> <a href="">http://foo.com/bar?foo=bar&region=US&register=lowpass&reg_test=fail&trademark=correct</a> </div> <div> Correctly escaped: <br> http://foo.com/bar?foo=bar&amp;region=US&amp;register=lowpass&amp;reg_test=fail&amp;trademark=correct </div> 

在我看来,你从谷歌收到的不是一个实际的url,而是一个指向一个url(查询string)的variables。 所以,这就是为什么它被渲染时被parsing为注册标记。

我会说,你欠url编码,并解码它,每当处理它。 像其他包含特殊实体的variables一样。

为了防止发生这种情况,您应该对url进行编码 ,这会在url中replace字符(如&符号后面的一个%和一个hex数字)。