PHP过滤JavaScript存储字符串中的Emoji失效,忽略了什么?
问题核心:Unicode代理对的编码差异
你的示例字符串"foo \ud83d\ude08 bar"在PHP中并不是一个完整的Emoji字符,而是两个独立的UTF-16代理对字符(\ud83d是高代理项,\ude08是低代理项)。Emoji属于Unicode补充平面(代码点大于0xFFFF),在UTF-16中需要用两个代理项组合表示,但PHP的preg_replace加/u修饰符时,是按UTF-8单字符解析的,这两个代理项会被当成独立的无效UTF-8序列,所以你的正则(匹配单个补充平面字符\x{1F600}-\x{1F64F})根本匹配不到。
另外还要确认你数据库中存储的字符串格式,分两种情况解决:
情况1:数据库存的是JavaScript风格的Unicode转义文本
如果数据库里的内容是foo \ud83d\ude08 bar(即\ud83d\ude08是作为\u开头的转义字符串存储,而非实际的Emoji字符),首先需要把这些转义序列解码成真正的UTF-8字符,再过滤:
// 用json_decode快速解码JavaScript的Unicode转义序列 $decoded_text = json_decode('"' . addcslashes($text, '"\\') . '"'); // 过滤Emoji(覆盖主流Emoji Unicode区块) $result = preg_replace('/[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F1E0}-\x{1F1FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}]/u', '', $decoded_text);
情况2:数据库存的是UTF-16代理对的UTF-8编码
如果数据库里的内容是代理对转成UTF-8后的字节序列(比如\ud83d转成UTF-8是\xed\xa0\xbd,\ude08转成UTF-8是\xb8\x88),则需要直接匹配代理对的组合,或者同时匹配代理对和单平面Emoji:
// 方法1:直接匹配所有UTF-16代理对(对应所有补充平面字符,包括Emoji) $result = preg_replace('/[\x{D800}-\x{DBFF}][\x{DC00}-\x{DFFF}]/u', '', $text); // 方法2:精确匹配Emoji,同时覆盖代理对和基础平面的Emoji $result = preg_replace('/(?:[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F1E0}-\x{1F1FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}]|[\x{D800}-\x{DBFF}][\x{DC00}-\x{DFFF}])/u', '', $text);
验证字符串格式的方法
可以用bin2hex($text)查看字符串的实际字节:
- 如果输出包含
5c75643833645c7564653038,说明是转义后的字符串(对应\ud83d\ude08的ASCII编码),对应情况1。 - 如果输出包含
eda0bdb888,说明是代理对的UTF-8编码,对应情况2。
内容的提问来源于stack exchange,提问作者Insomnia88
相关产品推荐
相关产品推荐

