【问题标题】:Character encoding error, cannot write valid XML from MySQL via PHP字符编码错误,无法通过 PHP 从 MySQL 写入有效的 XML
【发布时间】:2012-01-17 08:09:39
【问题描述】:

有问题的提要是:http://api.inoads.com/snowstorm/feed.xml

这是我用于生成的 PHP 代码:

<?php

$database =  'xxxx';
$dbconnect = mysql_pconnect('xxxx', 'xxxx', 'xxxx');
mysql_select_db($database, $dbconnect);

$query = "SELECT * FROM the_queue WHERE id LIKE '%'    ORDER BY id DESC LIMIT 25";
$result = mysql_query($query, $dbconnect);

while ($line = mysql_fetch_assoc($result))
        {
            $return[] = $line;
        }

$now = date("D, d M Y H:i:s T");

$output = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
            <rss version=\"2.0\">
                <channel>
                    <title>The Queue</title>
                    <link>http://readapp.net</link>
                    <description>A curated reading list.</description>
                    <language>en-us</language>
                    <pubDate>$now</pubDate>
                    <lastBuildDate>$now</lastBuildDate>
            ";

foreach ($return as $line)
{
    $output .= "<item><title>".htmlspecialchars($line['title'])."</title>
    <description>".htmlspecialchars($line['description'])."</description>
                    <link>".htmlspecialchars($line['link'])."</link>
                    <pubDate>".htmlspecialchars($line['pubDate'])."</pubDate>
                </item>";
}
$output .= "</channel></rss>";

$fh = fopen('feed.xml', 'w');
fwrite($fh, $output);
?>

什么可能导致错误?

这是来自提要验证器的链接:http://validator.w3.org/feed/check.cgi?url=http%3A%2F%2Fapi.inoads.com%2Fsnowstorm%2Ffeed.xml

【问题讨论】:

  • 欢迎来到 Stack Overflow!您没有在查询中进行任何错误检查。在mysql_query() 通话后,您需要执行此操作。否则,如果查询失败,您的脚本将中断。 manual on mysql_query() 或此reference question. 中概述了如何执行此操作
  • 你的数据是什么字符串编码的?您需要在 &lt;?xml&gt; 标记中指定它。例如&lt;?xml version="1.0" encoding="..."?&gt;
  • @AbhiBeckert UTF-8 - 我已经修改了上面的帖子以反映这一点
  • @deceze 引号和问号存在问题 - 我已更新帖子以显示这一点。
  • mysql 扩展已过时,即将弃用。新代码应该使用mysqli或PDO,两者都有重要的优势,比如支持prepared statements。

标签: php mysql xml utf-8 character-encoding


【解决方案1】:

问题是你在数据库中保存了这个带有引号的字符串(我假设)。如果这是真的,PHP 正在删除引号(这是正确的),因为不会导致错误(SQL 注入 ex)。因此,您必须删除数据库中的引号,并在生成 XML 文件时添加它们。在我看来这是最简单的。并尽量避免双引号“。你应该使用单引号 '。在双 PHP 解析器中还会检查其中的内容。所以尝试从数据库中删除 qoutes 并在生成 XML 时添加它们。应该有帮助。

【讨论】:

  • 不,"Dave替换成&amp;quot;Dave只是对XML进行转义的方式问题,两者是等价的。将Dave?” 替换为Dave?? 更可能是编码问题,并且由于PHP 或SQL 都没有对 进行特殊处理,因此与避免任何注入无关。
【解决方案2】:

您遇到的另一个错误是日期格式。日期必须采用 RFC-822 格式,格式必须是“Wed, 02 Oct 2002 08:00:00 EST”,而不是“July/August 2008”。

【讨论】:

  • 日期是手动输入的,谢谢指出。
【解决方案3】:

htmlentities 的重点是用这些实体替换所有定义了 HTML 字符实体的字符。如果您真的不想要任何字符实体(如您想要的结果所示),请不要使用htmlentities

默认情况下,htmlentities 使用 latin-1 字符集,因此它会阻塞智能引号(实际上,所有多字节字符),这是您看到问号的地方。一种解决方法是使用htmlspecialchars 转换一组更有限的字符(&、、' 和")。这仍然会转换双引号,因为这就是htmlspecialchars 的重点,除非你将ENT_NOQUOTES 指定为第二个参数。另一个解决方法是将字符集指定为第三个参数(这不排除使用htmlspecialchars)。

要么的第四个参数指定是否对已经编码的字符进行编码。是否进行双重编码取决于源数据。

$line['description'] = '"Dave, stop. Stop, will you? Stop, Dave. Will you stop, Dave?” ... “Dave, my mind is going,” HAL says, forlornly. “I can feel it. I can feel it.”';

echo "<description>" . htmlspecialchars($line['description'], ENT_NOQUOTES, 'UTF-8', false) . "</description>";

另见:

【讨论】:

  • 我已经尝试过了,但我收到以下错误:此提要未验证。 “utf8”编解码器无法解码位置 606 中的字节 0x94:意外代码字节(可能是高位字符?)
  • 我收到以下警告:预计参数 2 很长
  • 如果此代码导致提要中仅此特定项目的描述为空,原因是什么?
  • 有关解码字节流的错误消息可能是验证器中的错误。请注意,如果您将提要文档粘贴到“直接输入验证”表单中,则不会生成无效字符错误。或者,使用htmlspecialchars 将智能引号替换为命名字符实体(ENT_NOQUOTES 仅适用于普通单引号和双引号)。但是,据报道,一些提要阅读器在命名字符实体方面存在问题;忽略验证器错误消息可能会更好。
  • 现在我们来解决真正的问题。这个问题来自XY problem。你应该做的是创建一个minimal test case。忘记生成提要的代码并创建可能导致应用崩溃的最小的静态 RSS 文件。然后创建在静态 RSS 提要上崩溃的最小的应用程序。通过这个重点示例,您可能会看到它崩溃的实际原因。如果没有,请使用您的最小样本创建一个新问题,询问解析器为何在您的提要上崩溃。
【解决方案4】:

这里有一个问题:

$output = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
...

有一个包含“?>”的字符串。这是 php 的终结标记。它会给你一个错误。

您可以通过这种方式避免这些问题:

$output = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">
...

【讨论】:

  • PHP 解析器完全能够处理嵌入在字符串中的 PHP 关闭标签,无论是单引号、双引号、nowdoc 还是 heredoc。
【解决方案5】:

您说 XML 文件是 UTF-8,但是当我下载它并在我的文本编辑器中打开它时,它会自动检测 windows latin1 编码,并且引号显示完美。

如果我强制我的文本编辑器使用 UTF-8,它会显示一条错误消息,因为 UTF-8 编码存在非法字符。

因此,您的数据不是 UTF-8,它是 latin1。您需要准确找出发生这种情况的位置。它可以是以下任何一种或几种:

是用户输入内容的 HTML 页面设置为 UTF-8 吗?

如果没有,浏览器将发送 latin1 引号。要解决这个问题,&lt;head&gt; 中的 first 标记需要是:

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  ...
</head>

是否每个浏览器都正确遵守该页面 HTML 中的 UTF-8 设置?

如果您指定 UTF-8 并且页面包含在该编码中非法的字符,则某些浏览器可能会决定使用不同的编码,尽管有 &lt;meta&gt; 标签。每个浏览器的检查方法都不同。

插入数据库时​​的 MySQL 连接是否设置为使用 UTF-8?

您需要在这里使用 UTF-8,否则 MySQL 可能会尝试为您转换编码,通常会损坏它们。设置编码:

$database =  'xxxx';
$dbconnect = mysql_pconnect('xxxx', 'xxxx', 'xxxx');
mysql_select_db($database, $dbconnect);
mysql_query('SET NAMES utf8', $dbconnect);

MySQL 表(和单个列)是否设置为使用 UTF-8?

再次,为了避免 MySQL 进行自己的错误转换,您需要确保它对表和单个注释使用 UTF-8。对数据库进行结构转储并检查:

CREATE TABLE `the_queue` (
  ...
) ... DEFAULT CHARSET=utf8;

还要确保在任何列上都没有类似的内容:

`description` varchar(255) CHARACTER SET latin1,

读取数据库时的MySQL连接是否设置为使用UTF-8?

您的读取连接也必须是utf8。所以仔细检查一下。

您是否在 PHP 中做任何无法处理 UTF-8 的事情?

PHP 有一些函数不能用于 utf-8 字符串,因为它会损坏它们。其中一个功能是htmlentities(),因此请确保您始终使用htmlspecialchars()。测试这一点的最简单方法是开始注释掉大部分代码以查看编码中断的位置。

【讨论】:

  • 谢谢阿比,太好了。
猜你喜欢
  • 1970-01-01
  • 2018-07-03
  • 2016-01-01
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 2011-05-29
  • 1970-01-01
  • 2011-09-05
相关资源
最近更新 更多