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

如何使用PHP替换字符串中的非SGML字符?,php,html,validation,character-encoding,Php,Html,Validation,Character Encoding,我用PHP4和HTML4.01(字符集为ISO-8859-15,即拉丁语-9)编写了一个留言簿。数据用字符集(ISO-8859-1,即拉丁语-1)保存在MySQL数据库中 当有人从不同的字符集输入字符时,浏览器似乎会发送编码的数据(实际上我没有检查编码的位置,…) 无论如何,在某些情况下,字符似乎没有保存在数据库中。因此,当我在HTML4.01文档中添加并显示数据时,验证程序返回一条错误消息: 非SGML字符号146 您在文本中使用了非法字符。HTML使用标准的UNICODE联合字符库,并且 未

我用PHP4和HTML4.01(字符集为ISO-8859-15,即拉丁语-9)编写了一个留言簿。数据用字符集(ISO-8859-1,即拉丁语-1)保存在MySQL数据库中

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

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

非SGML字符号146

您在文本中使用了非法字符。HTML使用标准的UNICODE联合字符库,并且 未定义(除其他外)65个字符代码(0到31个字符,包括0到31个字符,127个字符 有时用于排版引用的 专有字符集中的标记和类似标记。验证器具有 在文档中找到这些未定义的字符之一。这个 字符可能以卷曲引号或商标的形式出现在浏览器上 符号,或其他一些奇特的符号;然而,在另一台计算机上, 它很可能会以完全不同的角色出现,或者什么都没有 一点也不

最好是用最接近的等效字符替换该字符 ASCII字符,或使用适当的字符实体。更多 有关web上字符编码的信息,请参阅Alan Flavell的 优秀的HTML字符集问题参考

此错误也可能由格式中嵌入的字符触发 文档由一些文字处理器处理。如果您使用文字处理器进行编辑 在HTML文档中,请确保使用“另存为ASCII”或类似命令 命令保存文档而不保存格式信息


我现在使用的是PHP5.2.17,并使用了一些htmlspecialchars,但没有任何效果。如何对这些字符进行编码,以便不再出现验证错误?

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

SGML引用ISO 8859-1(请注意ISO和8859-1之间的空格,它不像您使用的字符集那样是连字符)。它不允许控制字符,但允许三个(此处:):

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

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

我建议您验证进入应用程序的输入是否不允许使用控制字符。如果您认为这些字符最初代表的是有用的东西,例如可以实际读取的字母(例如,不是控制字符),则在处理数据时,编码可能会在某个点中断

根据问题中给出的信息,很难说在哪里,因为您只指定了输入编码和数据库字段的编码——但这两个编码已经不匹配(这不应该产生您要问的问题,但可能会产生其他问题)。在这两个位置旁边,还有数据库客户机连接字符集(问题中未指定)、输出编码(问题中未指定)和响应内容编码(问题中未指定)

将整体编码更改为UTF-8以支持更大范围的字符可能是有意义的,但这确实是一种可能

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

维基百科页面还指出,大多数浏览器将ISO-8859-1视为Windows-1252/CP1252/CP-1252。无法处理这些字符,for HTML实体不包含代码点(PHP5.3,未针对5.4进行测试)。您需要创建自己的翻译表,并将其用于替换ISO 8859-15 for 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
);

希望这对现在更有用。请参见上面链接的Wikipedia页面,了解windows-1242和ISO 8859-15中的一些字符,但它们位于不同的位置。你可能应该考虑在你的网站上使用UTF-8。

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

如果您使用其他编码,而用户输入的字符在该编码中没有表示形式,则这是一种错误情况,浏览器可能会以任意方式处理。现代浏览器做了一些原则上很奇怪但在实践中很有用的事情:它们将字符表示为字符引用,如
。在这种情况下,接收到的数据与用户实际键入字符的情况相同(但这是如此理论化,以至于浏览器供应商显然忽视了这个问题)

在您的案例中,服务器端会发生什么还不清楚,但它可能涉及许多类型的处理。在任何情况下,通常都不能将ISO-8859-15存储为ISO-8859-1编码(ISO-8859-15旨在用其他字符替换ISO-8859-1中的某些字符)。目前还不清楚您的软件如何处理字符引用,如
。软件用字符引用来代替它们,虽然肯定是可能的,但这有点奇怪<