PHP、MySQL 和 XML = 乱码 HTML 输出
Posted
技术标签:
【中文标题】PHP、MySQL 和 XML = 乱码 HTML 输出【英文标题】:PHP, MySQL and XML = garbled HTML output 【发布时间】:2011-01-13 17:46:23 【问题描述】:我在 mysql 中有一个文本类型的字段,使用以下排序规则:utf8_general_ci
。
使用 DOMDocument 构建的变量填充此 XML 字段:
function ed_audit_node($dom, $field, $new, $old)
//create audit_detail node
$ad = $dom->createElement('audit_detail');
$fn = $dom->createElement('fieldname');
$fn->appendChild($dom->createTextNode($field));
$ad->appendChild($fn);
$ov = $dom->createElement('old_value');
$ov->appendChild($dom->createTextNode($old));
$ad->appendChild($ov);
$nv = $dom->createElement('new_value');
$nv->appendChild($dom->createTextNode($new));
$ad->appendChild($nv);
//append to document
return $ad;
这是我保存到数据库的方法( $xml 来自 $dom->saveXML() ):
function ed_audit_insert($ed, $xml)
global $visitor;
$sql = <<<EOF
INSERT INTO ed.audit
(employee_id, audit_date, audit_action, audit_data, user_id)
VALUES (
$ed[emp][employee_id],
now(),
'$ed[audit_action]',
'$xml',
$visitor[user_id]
);
EOF;
$req = mysql_query($sql,$ed['db']) or die(db_query_error($sql,mysql_error(),__FUNCTION__));
//snip
查看一个较旧的、并行的、稍微相关的线程,了解我是如何创建这个 XML 的: Another php XML parsing error: "Input is not proper UTF-8, indicate encoding!"
什么有效: - 查询数据库,选择字段并使用 jQuery (.ajax()) 输出并填充文本区域。 Firebug 和 textarea 与数据库中的内容相匹配(通过 Toad 确认)。
什么不起作用: - 将数据库中的文本输出到 html 页面。此 HTML 页面的内容类型为 ISO-8859-1,我无法更改。
这是将其输出到屏幕的代码:
$xmlData = simplexml_load_string($d['audit_data']);
foreach ($xmlData->audit_detail as $a)
echo "<p> straight from db = ".$a->new_value."</p>";
echo "<p> utf8_decode() = ".utf8_decode($a->new_value)."</p>";
我还为 Firefox 使用了字符集更改器扩展:尝试了 ISO-8859-1、UTF-8 和 1252,但均未成功。
如果是 UTF-8,我不应该看到里面有问号的菱形吗(因为它的 content-type = ISO-8859-1)?如果不是 UTF-8,那是什么?
编辑#1
这是我进行的其他测试的快照:
$xmlData = simplexml_load_string($d['audit_data']);
foreach ($xmlData->audit_detail as $a)
echo "<p>encoding is, straight from db, using mb_detect_encoding: ".mb_detect_encoding($a->new_value)."</p>";
echo "<p>encoding is, with utf8_decode, using mb_detect_encoding: ".mb_detect_encoding(utf8_decode($a->new_value))."</p>";
echo "<hr/>";
echo "<p> straight from db = <pre>".$a->new_value."</pre></p>";
echo "<p> utf8_decode() = <pre>".utf8_decode($a->new_value)."</pre></p>";
echo "<hr/>";
$iso88591_2 = iconv('UTF-8', 'ISO-8859-1', $a->new_value);
$iso88591_3 = mb_convert_encoding($a->new_value, 'ISO-8859-1', 'UTF-8');
echo "<p> iconv() = ".$iso88591_2."</p>";
echo "<p> mb_convert_encoding() = ".$iso88591_3."</p>";
编辑#2
我添加了 FF 专有标签 xmp。
代码:
$xmlData = simplexml_load_string($d['audit_data']);
foreach ($xmlData->audit_detail as $a)
echo "<p>encoding is, straight from db, using mb_detect_encoding: ".mb_detect_encoding($a->new_value)."</p>";
echo "<p>encoding is, with utf8_decode, using mb_detect_encoding: ".mb_detect_encoding(utf8_decode($a->new_value))."</p>";
echo "<hr/>";
echo "<p> straight from db = <pre>".$a->new_value."</pre></p>";
echo "<p> utf8_decode() = <pre>".utf8_decode($a->new_value)."</pre></p>";
echo "<hr/>";
$iso88591_2 = iconv('UTF-8', 'ISO-8859-1', $a->new_value);
$iso88591_3 = mb_convert_encoding($a->new_value, 'ISO-8859-1', 'UTF-8');
echo "<p> iconv() = ".$iso88591_2."</p>";
echo "<p> mb_convert_encoding() = ".$iso88591_3."</p>";
echo "<hr/>";
echo "<p>straight from db, using <xmp> = <xmp>".$a->new_value."</xmp></p>";
echo "<p>utf8_decode(), using <xmp> = <xmp>".utf8_decode($a->new_value)."</xmp></p>";
以下是页面中的一些元标记:
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="dc.language" scheme="ISO639-2/T" content="eng" />
IMO,最后一个元标记没有轴承。
编辑#3
源代码:
<p>encoding is, straight from db, using mb_detect_encoding: UTF-8</p><p>encoding is, with utf8_decode, using mb_detect_encoding: ASCII</p><hr/><p> straight from db = <pre>Ro马eç ³é ¥n franê¡©s</pre></p><p> utf8_decode() = <pre>Ro?e??n fran?s</pre></p><hr/><p> iconv() = Ro</p><p> mb_convert_encoding() = Ro?e??n fran?s</p><hr/><p>straight from db, using <xmp> = <xmp>Ro马eç ³é ¥n franê¡©s</xmp></p><p>utf8_decode(), using <xmp> = <xmp>Ro?e??n fran?s</xmp></p>
编辑#4
这是进入数据库的 SQL 语句:
INSERT INTO ed.audit
(employee_id, audit_date, audit_action, audit_data, user_id)
VALUES (
75,
now(),
'u',
'<?xml version="1.0"?>
<audit><audit_detail><fieldname>role_fra</fieldname><old_value>aRo马e砳頥n franꡩs</old_value><new_value>bRo马e砳頥n franꡩs</new_value></audit_detail></audit>
',
333
);
!请注意,此 XML 中的文本不一定与上面提供的屏幕截图相匹配。
编辑#5
这是我的新函数,它将 CDATA 标记包装在 old_value 和 new_value 节点的值周围:
function ed_audit_node($dom, $field, $new, $old)
//create audit_detail node
$ad = $dom->createElement('audit_detail');
$fn = $dom->createElement('fieldname');
$fn->appendChild($dom->createTextNode($field));
$ad->appendChild($fn);
$ov = $dom->createElement('old_value');
$ov->appendChild($dom->createCDATASection($old));
$ad->appendChild($ov);
$nv = $dom->createElement('new_value');
$nv->appendChild($dom->createCDATASection($new));
$ad->appendChild($nv);
//append to document
return $ad;
我还在 XML 文档中添加了编码:
$dom = new DomDocument('1.0', 'UTF-8');
这是我的新 simpleXML 调用:
$xmlData = simplexml_load_string($d['audit_data'], "SimpleXMLElement", LIBXML_NOENT | LIBXML_NOCDATA);
我也在 Toad 中看到了 CDATA 标签。但是,我仍然收到错误消息:
Warning: simplexml_load_string() [function.simplexml-load-string]: Entity: line 2: parser error : Input is not proper UTF-8, indicate encoding ! Bytes: 0xE9 0xE9 0x6C 0x65 in <snip>
编辑#6
我刚刚注意到 jQuery 调用在 CDATA 中返回了正确的重音字符。
【问题讨论】:
【参考方案1】:从技术上讲,您的字符串是 UTF8 格式的,但 HTML 编码的字符(由浏览器呈现时)不是 UTF8 格式。所以&#xa869;
是一个有效的 UTF8 字符串,但是从 Web 浏览器呈现到屏幕上的字符不是有效的 UTF8。
我也会像这样将你的回声包装到屏幕上(你的例子中的最后两行):
echo "<p>straight from db = <xmp>".$a->new_value."</xmp></p>";
echo "<p>utf8_decode() = <xmp>".utf8_decode($a->new_value)."</xmp></p>";
这将清楚地显示我在上面提出的观点。
编辑:
问题实际上是 PHP 的 simplexml_load_string() 中一个无法控制的未记录的“功能”。它会自动将所有字符从它们的 XML 实体形式海峡转换成它们的实际字符形式。避免这种情况的唯一方法是像这样使用 simplexml_load_string():
$data = simplexml_load_string(
'<?xml version="1.0" encoding="utf-8"?>
<audit>
<audit_detail>
<fieldname>role_fra</fieldname>
<old_value><![CDATA[aRo马e砳頥n franꡩs]]></old_value>
<new_value><![CDATA[bRo马e砳頥n franꡩs]]></new_value>
</audit_detail>
</audit>',
"SimpleXMLElement",
LIBXML_NOENT | LIBXML_NOCDATA
);
print "<PRE>";
print_r($data);
exit;
您必须将元素包装在<![CDATA[]]>
标记中,然后将 LIBXML_NOCDATA 选项传递给 xml 解析器。这将强制将 <![CDATA[]]>
标记中的内容转换为 String 类型,PHP 可以在 SimpleXMLObject 之外正确处理。
【讨论】:
可能想看这里:***.com/questions/374425/… @Geoffrey:即使使用 pre,文本仍然与我的示例中的相同(但字体大小会发生变化)。我在 IE 和 FF 中都试过了。 @dqhendricks:谢谢,我去看看。 @tekius 您可能想尝试标签。将在 FF 中工作以向您显示原始输出。 &#xA863
是有效的 UTF8 的概念有意义吗?utf8_decode($a->new_value);
也正在解码解码的信息,因此 & 和 # 正在变成它们自己的解码版本,因此所有的 ????您在输出中看到。原始输出的应该显示这个。我已经更新了上面的代码以反映更改。
以上是关于PHP、MySQL 和 XML = 乱码 HTML 输出的主要内容,如果未能解决你的问题,请参考以下文章