UTF-8转Unicode代码点

6
有没有一种函数可以将UTF-8转换为Unicode,使非特殊字符保持正常字母和数字的形式?
例如,德语单词“tchüß”将被渲染为类似于“tch\20AC\21AC”的东西(请注意,我是在编造Unicode代码)。
编辑:我正在尝试以下函数,但尽管这个函数对ASCII 32-127有效,但它似乎无法处理双字节字符:
function strToHex ($string)
{
    $hex = '';
    for ($i = 0; $i < mb_strlen ($string, "utf-8"); $i++)
    {
        $id = ord (mb_substr ($string, $i, 1, "utf-8"));
        $hex .= ($id <= 128) ? mb_substr ($string, $i, 1, "utf-8") : "&#" . $id . ";";
}

    return ($hex);
}

你有什么想法吗?

编辑2:找到解决方案:PHP中的ord()函数不能处理双字节字符。改为使用:http://nl.php.net/manual/en/function.ord.php#78032


4
将标题改为更加描述性的内容——UTF-8 Unicode。你可能在寻找“UTF-8转Unicode编码点”。 - Artyom
一个有用的资源:https://dev59.com/nnRC5IYBdhLWcg3wJNmf - Karolis
你如何定义“非特殊字符”? - borrible
1
不,除了在病态情况下通过恒等操作,你不能将UTF-8转换为Unicode。定义“没有特殊字符”和“普通字母和数字”!像“%”和“/”这样的字符是特殊字符吗?那控制字符呢?什么使一个字母或数字正常或异常?ñ U+00F1 和 ð U+00F0 是正常字母吗?如果ñ实际上是n后跟U+0303怎么办?同样,什么使一个字符成为字母或数字?难道¼ U+00BC 和 ² U+00B2 不是数字吗?Unicode 6.0.0有100,520个GC=Letter和1,100个GC=Number代码点,其中456个是GC=Letter_Number,如Ⅷ。(...) - tchrist
4
Adrien说:“我从来没有想到过这个定义。这意味着在Unicode的1,114,112个代码点中,只有94个是特殊的,留下1,114,018个需要被归类为“特殊”?这真的很不符合直觉。我认为出现频率比其余低五个数量级的才是特殊的。否则你就颠倒了特殊性的概念。从我的角度来看,实际上代码点32-126才是特殊的,而不是那些不是特殊的。无法称呼99.99%的东西为“特殊”。正如我所说的,我从来没有想到过这一点。” - tchrist
显示剩余2条评论
8个回答

28

如果需要易读的格式,我会选择JSON。在JSON中不需要转义非ASCII字符,但PHP需要:

echo json_encode("tchüß");

"tch\u00fc\u00df"

1
有趣,从未想过这个! - Adrien Hingert
1
太棒了!运行得像魔法一样.. :) - Anthony
JSON默认要求转义非ASCII字符,每次使用时都应该这样做。 - William R
太棒了! - Basster
@WilliamR, 你为什么这样想?JSON 的定义是 UTF-8,完全支持 Unicode。转义任何 Unicode 字符是不必要的。 - Ulrich Eckhardt
很明显,对于JSON来说使用UTF-8是显而易见的。但是通过ASCII转义Unicode(例如"é"变成\u00e9)是一种保护数据免受HTTP传输头中设置的错误"字符集"、糟糕编写的代码甚至更糟糕的是,在ISO-Latin1 XML文件中的CDATA标签内部的JSON的影响的好方法。 - William R

11

PHP 7引入了一个新的IntlChar::ord()方法,用于从给定的UTF-8字符中查找Unicode代码点:

var_dump(sprintf('U+%04X', IntlChar::ord('ß')));

# Outputs: string(6) "U+00DF"

1
请注意,您需要在PHP.ini中启用extension=php_intl.dll才能使用此类。 - eis

10

对于寻找任何字符的Unicode代码点的人来说,这可能很有用。然后,您可以将字符串编码为任何您想要的内容,使用转义代码替换某些字符,并将其他字符保留在其二进制形式中(例如ASCII可打印字符),具体取决于您想要使用它的上下文。

来自:将代码点映射到Unicode编码形式

UTF-32的映射本质上是恒等映射:用于编码代码点的32位代码单元具有与代码点本身相同的整数值。

/**
 * Convert a string into an array of decimal Unicode code points.
 *
 * @param $string   [string] The string to convert to codepoints
 * @param $encoding [string] The encoding of $string
 * 
 * @return [array] Array of decimal codepoints for every character of $string
 */
function toCodePoint( $string, $encoding )
{
    $utf32  = mb_convert_encoding( $string, 'UTF-32', $encoding );
    $length = mb_strlen( $utf32, 'UTF-32' );
    $result = [];


    for( $i = 0; $i < $length; ++$i )

        $result[] = hexdec( bin2hex( mb_substr( $utf32, $i, 1, 'UTF-32' ) ) );


    return $result;
}

我需要获取UTF-8字符串的代码点值,以检查给定的TTF字体是否支持它们。这个函数完美地获得了代码点值。 - Erik Kalkoken

3

htmlentities 仅转换在 HTML 语言中已定义实体的字符,这仅涵盖了 Unicode 的一小部分。不幸的是它不会为其他字符创建 &#...; 字符引用。 - bobince
我知道,但是iconv也倾向于出现一些问题。并不是每个字符集都能完美转换所有字符。这就是为什么我提到了htmlentities函数。在iconv函数页面的评论中也有人建议使用它:http://nl.php.net/manual/en/function.iconv.php#81494 - Luwe

2

在 PHP 5.6 上测试通过。

/**
 * @param string $utf8char
 * @return string
 */
function toUnicodeCodePoint($utf8char)
{
    return 'U+' . dechex(mb_ord($utf8char));
}

/**
 * @see https://github.com/symfony/polyfill-mbstring
 * @param string $s
 * @return int
 */
function mb_ord($s)
{
    $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
    if (0xF0 <= $code) {
        return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
    }
    if (0xE0 <= $code) {
        return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
    }
    if (0xC0 <= $code) {
        return (($code - 0xC0) << 6) + $s[2] - 0x80;
    }

    return $code;
}

echo toUnicodeCodePoint('');
// U+1f613

2

我猜你要在网站上打印出你的字符串?

我将我的所有数据库存储为uft8,在输出之前使用html_entities($string)。

也许你需要尝试使用 html_entities(utf8_encode($string));


2
我曾经创建了一个名为_convert()的函数,它可以将所有东西安全地编码为UTF-8。

1
你可以在这里添加答案,而不是作为链接。 - eis

0

当我需要将包含 Cyrillic 的字符串(默认为 utf-8)部分地转换为实体时,我遇到了问题。最终我需要得到类似于 JSON 的结果,如下:

<li class="my_class">City - Mocsow (Москва)</li>

转换为:

<li class=\"my_class\">City - Mocsow (\u041c\u043e\u0441\u043a\u0432\u0430)<\/li>

所以,我有一个复杂的(主题作者和Nus的混合)解决方案:
function strToHex($string){
    $enc="utf-8";
    $hex = '';
    for ($i = 0; $i < mb_strlen ($string, $enc); $i++){
        $id = ord (mb_substr ($string, $i, 1, $enc));
        $hex .= ($id <= 128) ? mb_substr ($string, $i, 1, $enc) : toCodePoint(mb_substr ($string, $i, 1, $enc), $enc);
    }
    return $hex;
}
function toCodePoint($string, $encoding){
    $utf32  = mb_convert_encoding( $string, 'UTF-32', $encoding );
    $length = mb_strlen( $utf32, 'UTF-32' );
    $result = Array();
    for( $i = 0; $i < $length; ++$i )$result[] = "\u".substr(bin2hex( mb_substr( $utf32, $i, 1, 'UTF-32' ) ), 4,8);
    return implode("", $result);
}
$output=strToHex(
    str_replace( // this is for json compatible
        array("\"", "\n", "\r", "\t", "/"),
        array('\"', '\n', "", " ", "\/"),
        $text
    )
);
echo $output;

它在 PHP 5.2.17 上进行测试 :)


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