如何在PHP UTF-8字符串中检测代理对?求实现hasSurrogate函数
我来帮你搞定这个代理对检测的问题!
首先得理清核心逻辑:你看到json_encode输出的\ud840\udd18是UTF-16编码下的代理对,但你的原PHP字符串"abc𠄘"里的“𠄘”是完整的4字节UTF-8字符,对应单个Unicode码点U+20118——这也是为什么你用utf8_to_unicode只拿到一个码点,以及\p{Cs}匹配不到的原因:\p{Cs}是匹配单独存在的代理字符(比如孤立的高代理/低代理),但你的字符串里根本没有这种情况,只有完整的辅助平面字符。
要实现hasSurrogate函数,本质是检测字符串中是否存在Unicode辅助平面字符(码点范围U+10000到U+10FFFF)——因为这类字符在转成UTF-16时必然会被拆分成代理对,这正是json_encode输出代理对的根源。
方案一:PHP 7.2+ 简洁实现
利用PHP内置的mb_ord函数直接获取UTF-8字符的码点:
function hasSurrogate(string $string): bool { // 按UTF-8规则拆分字符串为单个字符 $chars = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY); foreach ($chars as $char) { $codePoint = mb_ord($char, 'UTF-8'); // 辅助平面字符的码点范围:U+10000 ~ U+10FFFF if ($codePoint >= 0x10000 && $codePoint <= 0x10FFFF) { return true; } } return false; } // 测试 $string = "abc𠄘"; var_dump(hasSurrogate($string)); // 输出 bool(true)
方案二:兼容PHP 7.2以下版本
如果你的PHP版本不支持mb_ord,可以自己实现UTF-8字符到码点的转换:
// 辅助函数:将单个UTF-8字符转为Unicode码点 function utf8ToCodePoint(string $char): int { $bytes = unpack('C*', $char); $byteCount = count($bytes); switch ($byteCount) { case 1: return $bytes[1]; case 2: return (($bytes[1] & 0x1F) << 6) | ($bytes[2] & 0x3F); case 3: return (($bytes[1] & 0x0F) << 12) | (($bytes[2] & 0x3F) << 6) | ($bytes[3] & 0x3F); case 4: return (($bytes[1] & 0x07) << 18) | (($bytes[2] & 0x3F) << 12) | (($bytes[3] & 0x3F) << 6) | ($bytes[4] & 0x3F); default: throw new InvalidArgumentException('输入不是有效的单个UTF-8字符'); } } // 实现hasSurrogate函数 function hasSurrogate(string $string): bool { $chars = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY); foreach ($chars as $char) { $codePoint = utf8ToCodePoint($char); if ($codePoint >= 0x10000 && $codePoint <= 0x10FFFF) { return true; } } return false; }
补充说明
- 为什么之前的
preg_match_all("/\p{Cs}/", ...)无效?\p{Cs}属于Unicode属性类,匹配的是无配对的孤立代理字符(比如单独出现的U+D800),但你的字符串里的“𠄘”是完整的辅助平面字符,不存在孤立代理,所以匹配不到。 - 为什么
json_encode会输出代理对?json_encode默认会将UTF-8字符串转成UTF-16编码的JSON格式(符合JSON标准),辅助平面字符在UTF-16中必须用一对代理码元表示,所以你看到了\ud840\udd18。
内容的提问来源于stack exchange,提问作者coderhwz
相关产品推荐
相关产品推荐

