如何使用PHP替换String中的非SGML字符?

时间:2012-03-16 12:08:44

标签: php html validation character-encoding

我使用PHP4和HTML 4.01编写了留言簿(使用charset ISO-8859-15,即latin-9)。数据使用字符集(ISO-8859-1,即latin-1)保存在MySQL数据库中。

当某人从不同的字符集输入字符时,似乎浏览器会发送编码的数据(实际上我没有检查它编码的位置,......)。

无论如何,在某些情况下,似乎字符不会保存在数据库中。因此,当我添加在HTML4.01文档中显示数据时,验证器返回错误消息:

  

非SGML字符编号146

     

您在文字中使用了非法字符。 HTML使用标准的UNICODE Con​​sortium角色保留曲目,然后离开   undefined(其中包括)65个字符代码(0到31,包括127和127)   有时用于印刷报价的159(含)   专有字符集中的标记和类似标记。验证器有   在文档中找到了这些未定义的字符之一。该   字符可能会在您的浏览器中显示为卷曲引号或商标   符号,或其他一些花哨的字形;然而,在另一台计算机上   它可能看起来是一个完全不同的角色,或者什么也不是   一点都不。

     

最好的办法是用最近的等值替换字符   ASCII字符,或使用适当的字符实体。更多   关于网络上字符编码的信息,请参阅Alan Flavell的   优秀的HTML字符集问题参考。

     

也可以通过格式化嵌入的字符来触发此错误   一些文字处理器的文件。如果您使用文字处理器进行编辑   您的HTML文档,请务必使用“另存为ASCII”或类似内容   命令保存文档而不格式化信息。

我现在正在使用PHP5.2.17,并且使用了htmlspecialchars,但没有任何效果。如何编码thoses字符,以便不再有验证错误?

2 个答案:

答案 0 :(得分:3)

在ISO-8859-1和ISO-8859-15中,字符编号146是来自C1 range的控制字符MW(消息等待)。

SGML是指ISO 8859-1(请注意ISO和8859-1之间的空格,它不是您使用的字符集中的连字符)。它不允许控制字符,只有三个(这里:SGML in HTML):

  

在HTML文档字符集中,只允许三个控制字符:水平      标签,回车和换行(代码位置9,13和10)。

因此,您确实传递了非法字符。它不存在SGML / HTML实体,您可以将其替换为。

我建议您验证进入应用程序的输入,它不允许控制字符。如果您认为这些字符最初代表一个有用的东西,比如可以实际读取的字母(例如,不是控制字符),那么当您处理数据时,编码可能会在某个时刻被破坏。

根据你问题中提供的信息,很难说在哪里,因为你只指定输入编码和数据库的编码 - 但这两者已经不匹配(这不应该产生你要问的问题)关于,但它可以产生其他问题)。在这两个地方旁边,还有数据库客户端连接字符集(在您的问题中未指定),输出编码(在您的问题中未指定)和响应内容编码(在您的问题中未指定)。

将整体编码更改为UTF-8以支持更广泛的字符可能是有意义的,但这实际上是可能

编辑:上面的部分有点严格。在我看来,你收到的输入实际上不是ISO-8859-1(5),而是其他东西,比如windows代码页。我可能会说,这是Windows-1252 (cp1252)­Wikipedia。与ISO-8859-1(128-159)的C1范围相比,它有几个非控制字符。

维基百科页面还指出,大多数浏览器将ISO-8859-1视为Windows-1252 / CP1252 / CP-1252。 PHP htmlentities() function无法处理这些字符,HTML实体的translation table不包含代码点(PHP 5.3,未针对5.4进行测试)。您需要创建自己的翻译表并将其与strtr一起使用,以替换ISO 8859-15中不适用于Windows-1252的字符:

/*
 * mappings of Windows-1252 (cp1252)  128 (0x80) - 159 (0x9F) characters:
 * @link http://en.wikipedia.org/wiki/Windows-1252
 * @link http://www.w3.org/TR/html4/sgml/entities.html
 */
$cp1252HTML401Entities = array(
    "\x80" => '€',    # 128 -> euro sign, U+20AC NEW
    "\x82" => '‚',   # 130 -> single low-9 quotation mark, U+201A NEW
    "\x83" => 'ƒ',    # 131 -> latin small f with hook = function = florin, U+0192 ISOtech
    "\x84" => '„',   # 132 -> double low-9 quotation mark, U+201E NEW
    "\x85" => '…',  # 133 -> horizontal ellipsis = three dot leader, U+2026 ISOpub
    "\x86" => '†',  # 134 -> dagger, U+2020 ISOpub
    "\x87" => '‡',  # 135 -> double dagger, U+2021 ISOpub
    "\x88" => 'ˆ',    # 136 -> modifier letter circumflex accent, U+02C6 ISOpub
    "\x89" => '‰',  # 137 -> per mille sign, U+2030 ISOtech
    "\x8A" => 'Š',  # 138 -> latin capital letter S with caron, U+0160 ISOlat2
    "\x8B" => '‹',  # 139 -> single left-pointing angle quotation mark, U+2039 ISO proposed
    "\x8C" => 'Œ',   # 140 -> latin capital ligature OE, U+0152 ISOlat2
    "\x8E" => 'Ž',    # 142 -> U+017D
    "\x91" => '‘',   # 145 -> left single quotation mark, U+2018 ISOnum
    "\x92" => '’',   # 146 -> right single quotation mark, U+2019 ISOnum
    "\x93" => '“',   # 147 -> left double quotation mark, U+201C ISOnum
    "\x94" => '”',   # 148 -> right double quotation mark, U+201D ISOnum
    "\x95" => '•',    # 149 -> bullet = black small circle, U+2022 ISOpub
    "\x96" => '–',   # 150 -> en dash, U+2013 ISOpub
    "\x97" => '—',   # 151 -> em dash, U+2014 ISOpub
    "\x98" => '˜',   # 152 -> small tilde, U+02DC ISOdia
    "\x99" => '™',   # 153 -> trade mark sign, U+2122 ISOnum
    "\x9A" => 'š',  # 154 -> latin small letter s with caron, U+0161 ISOlat2
    "\x9B" => '›',  # 155 -> single right-pointing angle quotation mark, U+203A ISO proposed
    "\x9C" => 'œ',   # 156 -> latin small ligature oe, U+0153 ISOlat2
    "\x9E" => 'ž',    # 158 -> U+017E
    "\x9F" => 'Ÿ',    # 159 -> latin capital letter Y with diaeresis, U+0178 ISOlat2
);

$outputWithEntities = strtr($output, $cp1252HTML401Entities);

如果你想要更安全,你可以省去命名的实体,只选择那些应该在非常旧的浏览器中工作的数字:

$cp1252HTMLNumericEntities = array(
    "\x80" => '€',   # 128 -> euro sign, U+20AC NEW
    "\x82" => '‚',   # 130 -> single low-9 quotation mark, U+201A NEW
    "\x83" => 'ƒ',    # 131 -> latin small f with hook = function = florin, U+0192 ISOtech
    "\x84" => '„',   # 132 -> double low-9 quotation mark, U+201E NEW
    "\x85" => '…',   # 133 -> horizontal ellipsis = three dot leader, U+2026 ISOpub
    "\x86" => '†',   # 134 -> dagger, U+2020 ISOpub
    "\x87" => '‡',   # 135 -> double dagger, U+2021 ISOpub
    "\x88" => 'ˆ',    # 136 -> modifier letter circumflex accent, U+02C6 ISOpub
    "\x89" => '‰',   # 137 -> per mille sign, U+2030 ISOtech
    "\x8A" => 'Š',    # 138 -> latin capital letter S with caron, U+0160 ISOlat2
    "\x8B" => '‹',   # 139 -> single left-pointing angle quotation mark, U+2039 ISO proposed
    "\x8C" => 'Œ',    # 140 -> latin capital ligature OE, U+0152 ISOlat2
    "\x8E" => 'Ž',    # 142 -> U+017D
    "\x91" => '‘',   # 145 -> left single quotation mark, U+2018 ISOnum
    "\x92" => '’',   # 146 -> right single quotation mark, U+2019 ISOnum
    "\x93" => '“',   # 147 -> left double quotation mark, U+201C ISOnum
    "\x94" => '”',   # 148 -> right double quotation mark, U+201D ISOnum
    "\x95" => '•',   # 149 -> bullet = black small circle, U+2022 ISOpub
    "\x96" => '–',   # 150 -> en dash, U+2013 ISOpub
    "\x97" => '—',   # 151 -> em dash, U+2014 ISOpub
    "\x98" => '˜',    # 152 -> small tilde, U+02DC ISOdia
    "\x99" => '™',   # 153 -> trade mark sign, U+2122 ISOnum
    "\x9A" => 'š',    # 154 -> latin small letter s with caron, U+0161 ISOlat2
    "\x9B" => '›',   # 155 -> single right-pointing angle quotation mark, U+203A ISO proposed
    "\x9C" => 'œ',    # 156 -> latin small ligature oe, U+0153 ISOlat2
    "\x9E" => 'ž',    # 158 -> U+017E
    "\x9F" => 'Ÿ',    # 159 -> latin capital letter Y with diaeresis, U+0178 ISOlat2
);

希望现在更有帮助。请参阅上面链接的维基百科页面,其中包含Windows-1242 ISO 8859-15 中的某些字符,但位于不同点。您可能应该考虑在您的网站上使用UTF-8。

答案 1 :(得分:2)

具有文本输入字段的网页应采用UTF-8编码,因为这是确保用户输入的所有字符都能正确传输的唯一方法。如何在服务器端处理它们(例如,拒绝某些特定范围之外的字符)是一个不同的问题。

如果您使用其他编码并且用户输入的字符在该编码中没有任何表示,则这是浏览器可以以他们喜欢的任何方式处理的错误条件。现代浏览器在实践中做了一些非常奇怪的事情,虽然在实践中很有用:它们将字符表示为字符引用,例如’表示正确的单引号(')。在这种情况下,收到的数据与用户实际键入字符’的情况相同(但这是理论上的,浏览器供应商显然忽略了这个问题)。

您的案例中服务器端发生的情况尚不清楚,但可能涉及多种类型的处理。在任何情况下,您都不能通常以ISO-8859-1编码ISO-8859-15(ISO-8859-15被设计为替换 ISO-8859-1中某些字符的其他字符) 。目前还不清楚您的软件对’等字符引用的作用。软件可以用’(基于使用windows-1252作为文档字符集,与HTML规则相反;它们在技术上未定义 - 不是等字符引用来替换它们,这有点奇怪,但肯定是可能的。非法的HTML,但浏览器广泛支持HTML5将其转化为规则)。