智能引号未正确转换为 UTF8

Posted

技术标签:

【中文标题】智能引号未正确转换为 UTF8【英文标题】:smart quotes not converting properly into UTF8 【发布时间】:2012-03-12 17:54:39 【问题描述】:

我有一个 php 脚本可以导入和解析 XML 文件并将数据保存到数据库中:

数据库排序规则:utf8_general_ci,字符集:utf8 页面的字符集:utf-8 XML 文件:ANSI,包含智能引号(来自 MS Word)

因此,在导入期间,我在将 XML 文件中的文本保存到数据库并随后显示在页面上之前,对文本执行 utf8_encode()

但是当成功导入并保存到数据库中时,

数据库:智能引号保存为?字符(从CMD查看) 页面:智能引号显示为框

关于为什么智能引号没有正确转换的任何想法,即使使用utf8_encode()

编辑:

@Tomalak:XML​​ 文件实际上是.txt,没有 XML 声明 (<?xml ... ?>),也没有根元素。我的脚本实际上添加了一个根元素,以便解析器工作:

utf8_encode('<article>' . file_get_contents($xmlfile) . '</article>');

好像我需要添加一个 XML 声明..?如果有,应该是什么样子?

【问题讨论】:

您能否发布您的 XML 文件的 XML 声明 (<?xml ... ?>) 以及智能引号中的字符代码(使用十六进制编辑器)? @Tomalak,我编辑了我的问题来回答你的评论 你有你的错误。添加 XML 声明 <?xml encoding="Windows-1252"?><article> . file_get_contents($xmlfile) . </article> 并删除 utf8_encode() 部分。然后使用DOMDocument 解析生成的字符串。只需确保 encoding 声明与文本文件中的字节匹配即可。 (至少我认为它应该这样工作。) @Tomalak:如果您可以将该评论重写为答案,我会将其标记为已接受的答案:) 但是有一个问题,您怎么知道应该使用“windows-1252”?因为我尝试了“iso-8859-1”但它不起作用(我在 Firefox 中打开了 XML (txt) 文件,报告该文件为 iso-8859-1) @Tomalak:还有,怎么不用utf8_encode转成utf8,因为数据库是utf8的? 【参考方案1】:

是的,问题在于智能引号有两种类型。

windows-1252 和 latin-1

utf8_encode 假定它是 latin-1,所以它得到了错误的智能引号。

如果我们知道输入数据是windows-1252,我们可以使用iconv进行转换。

$string=iconv ( "Windows-1252" , "UTF-8" , $oldstring );

这才是真正的答案。

【讨论】:

【参考方案2】:

如果您的 XML 字符串(即文件内容)不是编码为 UTF-8,您需要一个表示文件编码的 XML 声明。如果缺少 XML 声明,解析器将采用 UTF-8。

只要您不使用“特殊”字符(即 ASCII 范围之外的任何字符),即使您的文件不是真正 UTF-8 编码,它也可以在没有声明的情况下工作。这是因为 UTF-8 与 ASCII 字节兼容。但是一旦使用了其中一个代码页上的字符(例如“智能引号”),它就会中断,因为这些字符在 UTF-8 中由不同的字节表示。

在您的情况下,您可以使用根元素包装旧编码中的文本文件,以将它们转换为格式良好的 XML。因此,您需要自己添加 XML 声明:

'<?xml encoding="Windows-1252"?><article>'.file_get_contents($xmlfile).'</article>'

这样您就可以指示DOMDocument 如何解释字符串中的字节。我为您假设Windows-1252,因为您说ANSI并提到了花引号。

事实上,95% 的时间这是人们真正的意思,即使在 Linux 上,即使他们说 ISO-8859-1(或 latin-1),这几乎是,但不是完全正确同样的事情。

为了更加确定您可以在十六进制编辑器中打开您的文本文件,请找出一些特殊字符并将它们的字节值与可疑编码进行比较。对于Windows-1252。对于花引号,预期的字节值将是:

147 (0x93) 148 (0x94)

一旦声明了字符串中各个字节的含义,DOMDocument 就可以理解它们并做正确的事情。

当涉及到数据库时,我强烈怀疑正在进行一些自动编码转换。我承认我对 PHP/mysql/Unicode 集成的了解不够肯定。

【讨论】:

【参考方案3】:

它们被正确转换,因为 utf8_encode 完全按照它应该做的事情,并将它们从输入流中删除。

utf8_encode 将 ISO-8859-1 中的文本转换为 UTF-8 编码。 ISO-8859-1 不包含 smartquote 字符。您的网页可能在浏览器中被视为 MSWIN1252 编码,其中包含智能引号字符,并且您通过在其上调用 utf8_encode 来强制 PHP 输入 ISO-8859-1。这意味着将进入您的数据库的唯一字符是这些字符:http://en.wikipedia.org/wiki/ISO/IEC_8859-1#Codepage_layout

要解决这个问题,您必须确保网页本身采用 UTF-8 编码,并从您的代码中删除所有 utf8_encode/utf8_decode 语句。本页解释了如何正确使用 UTF-8:http://malevolent.com/weblog/archive/2007/03/12/unicode-utf8-php-mysql/

【讨论】:

十进制 60 处的 latin 1 个字符怎么样?那不是智能引号字符吗? @bvdb 不,这不是一个聪明的报价,而是一个反引号。 Windows-1252 字符集上的智能引号字符为 145 到 148:en.wikipedia.org/wiki/Windows-1252#Character_set

以上是关于智能引号未正确转换为 UTF8的主要内容,如果未能解决你的问题,请参考以下文章

我可以使用 iconv 将多字节智能引号转换为扩展的 ASCII 智能引号吗?

仅针对文本而非 HTML 代码将哑引号转换为智能引号

如何将 utf-8 花式引号转换为中性引号

为啥 Xaringan 幻灯片中没有出现智能引号和破折号?

在树枝中使用逃生和生?

MySQL 因卷曲(智能)引号而窒息