PHP:Unicode重音字符和变音符号

9
在我们的网站上,一些Mac用户在从PDF文件复制粘贴文本到由TinyMCE处理的TextArea时遇到麻烦。所有带重音符号的字符都会变得损坏,例如将é变为e?,将î变为i?等等。我无法在Windows电脑上重现这个问题。
当我将TextArea的内容写入文件(在插入数据库之前),我发现初始的在视觉上与传统的é不同(在Vim中查看如下)。
实际上:
// the corrupted é - first line of the screenshot
echo bin2hex($char); // display 65cc81

// traditionnal é
echo bin2hex('é');   // display c3a9

在大量搜索之后,我发现:Mac OS会将Unicode重音字符复制为两个字符的组合。在我们的例子中,就是e + ́。到目前为止,我还没有找到解决方案来替换损坏的é以避免在数据库中出现e?。我有些绝望了。

4
http://unicode.org/reports/tr15/ - hakre
3个回答

8
将表示法规范化为一种形式或另一种形式的过程称为规范化。在PHP中,有一个叫做{{link2:Normalizer类}}的工具可用于此操作。建议将所有输入都通过该工具进行处理。
$input = Normalizer::normalize($input);

你可能希望将其规范化为C形式,即规范分解后紧随规范组合。
如果你的系统上没有该类可用,则有 Patchwork UTF-8库

1
需要注意的是,实际上并没有损坏。分解的字符是完全有效的。 - Tino Didriksen
@Tino 确实。 应该调查为什么数据库或一些导致其破坏解析字符的进程。 - deceze
好的,所以我刚刚安装了intl,并且使用Normalizer::normalize()函数得到了这个:é。但是我添加了一个utf8_decode()函数,它给了我é!! 我很兴奋,我会再次测试并告诉你结果。 - 4wk_
好的。当我在我的文件中写入utf8_decode(Normalizer::normalize($input);时,我得到了我想要的结果。常规的é等字符。然而,在我的数据库中仍然是e?。我会检查一下@hakre所说的。 - 4wk_
1
@Ash 不要随意使用 utf8_decode,而是要正确处理编码。我相信数据库的问题是由于类似的编码错误造成的。请参考 http://kunststube.net/encoding 和 http://kunststube.net/frontback。 - deceze
你是对的。它可以在没有 utf8_decode(); 的情况下工作,这是我的错误。 - 4wk_

4
这只是对@deceze已经回答的内容的补充。在Unicode中,有多种方式可以指定相同(在等价意义上)的字符。
你在这里有一个常见的例子:
65cc81

这是Utf-8编码中的两个Unicode代码点。 65e LATIN SMALL LETTER E (U+0065),cc81́ COMBINING ACUTE ACCENT (U+0301)(不能单独显示,因此我使用了HTML实体)。
在Unicode中,它被称为组合序列。但由于某种原因,您的数据库不支持它。可能是因为列的编码不是UTF-8或者数据库连接存在问题。
它可以被规范等效地表示为:
c3a9

那是Utf-8编码中的一个Unicode代码点。c3a9é,即LATIN SMALL LETTER E WITH ACUTE(U+00E9)。看起来你的数据库处理它没有问题,可能是因为它被重新编码为Latin-1 / ISO-8859-1,并成功连接到了数据库。
所以有两种处理数据的方式。要么是数据重新编码的问题,要么是数据存储的问题。
只要您对组合的Unicode代码点序列进行分解感兴趣,您应该采用Deceze's answer中概述的标准化程序。
您也可以允许UTF-8存储到数据库中,然后您也不应该有问题。
此外,您可能应该规范化一下,这样在数据库或程序中排序和比较数据会更好。正如您所看到的,二进制序列不同,这可能会导致所有在二进制级别上进行比较的操作出现问题。
当然,您也可以节省一些流量 :)

谢谢您的回答。非常有用的演示,我因为您学到了很多! :) - 4wk_
1
很愉快的阅读体验。我还发现了这篇博客文章,它在我的眼中非常有趣:Unicode规范化 - 它还有一些更多的链接,如果你想深入挖掘Unicode部分,其中一些链接仍然有效。 - hakre
谢谢链接。我刚刚加入了 Poket,稍后会在地铁上阅读它 :) - 4wk_

0

有一个tinymce配置参数,可以让您定义一个函数,在将内容粘贴到编辑器中之前处理粘贴的内容:paste_preprocessing

使用该函数,您可以将特殊字符替换为所需的形式。

tinyMCE.init({
        ...
        paste_preprocess : function(pl, o) {
            // Content string containing the HTML from the clipboard
            o.content = o.content.replace(/\u2020/, 'x'); // example
        },
        paste_postprocess : function(pl, o) {
            ...
        },
        ...
});

1
期望的形式应该是Singleton而不是Combining sequence。你手头上有处理这个问题的javascript库吗? - hakre
是的,这是TinyMCE API:http://tinymce.moxiecode.com/js/tinymce/docs/api/index.html。但是JavaScript字符转换需要在paste_preprocess函数中由网站管理员定义。 - Thariama
有趣,但是 Normalizer 类的名称是什么?我找不到它。 - hakre
1
有趣,但是Normalizer类的名称是什么?我找不到它。- 不过谷歌很快就显示了这个:walling / unorm - hakre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接