【问题标题】:Convert unicode characters above 127 to decimal [duplicate]将127以上的unicode字符转换为十进制[重复]
【发布时间】:2012-10-11 20:34:49
【问题描述】:

可能重复:
How to convert text to unicode code point like \u0054\u0068\u0069\u0073 using php?

我正在尝试将所有不适合 7 位 ANSI 字符的字符转换为转义形式 \uN,其中 N 是它的十进制值。这是我想出的:

private static function escape($str) {
    return preg_replace_callback('~[\\x{007F}-\\x{FFFF}]~u',function($m){return '\\u'.ord($m[0]);},$str);
}

我已经尝试过使用 Gamma 等字符,

echo self::escape('Γ');

但是我得到了\u206 而不是\u915。我不知道我哪里出错了......想法?

实际上,ord() 函数似乎没有给我或我想要的值,或者我的 .php 文件上的编码错误?

【问题讨论】:

  • 应该已经阅读了ord页面上的第一条评论; ca3.php.net/manual/en/function.ord.php
  • 这是一种一种的方法,但它是一种可怕的方法,就在我挖掘出正确方法的时候去做。
  • @DaveRandom:从 2004 年开始,页面底部还有另一个似乎有效。
  • @mario:我认为我的解决方案看起来比该页面上的解决方案更干净、更高效 :)
  • 一直在使用类似的东西:return preg_replace("/[^\\x{0020}-\\x{007F}]/ue", "'\\u'.current(unpack('H*', iconv('UTF-8', 'UCS-2BE', '$0')))", $var);

标签: php unicode multibyte


【解决方案1】:

我不得不回忆一下 UTF-8 的工作原理,但这里有一个 utf8_ord() 函数和一个补充 utf8_chr()chr() 几乎是从我的回答 here 中逐字提取的。

function utf8_ord ($chr)
{
    $bytes = array_values(unpack('C*', $chr));

    switch (count($bytes)) {
        case 1:
            return $bytes[0] < 0x80
                ? $bytes[0]
                : false;
        case 2:
            return ($bytes[0] & 0xE0) === 0xC0 && ($bytes[1] & 0xC0) === 0x80
                ? (($bytes[0] & 0x1F) << 6) | ($bytes[1] & 0x3F)
                : false;
        case 3:
            return ($bytes[0] & 0xF0) === 0xE0 && ($bytes[1] & 0xC0) === 0x80 && ($bytes[2] & 0xC0) === 0x80 
                ? (($bytes[0] & 0x0F) << 12) | (($bytes[1] & 0x3F) << 6) | ($bytes[2] & 0x3F)
                : false;
        case 4:
            return ($bytes[0] & 0xF8) === 0xF0 && ($bytes[1] & 0xC0) === 0x80 && ($bytes[2] & 0xC0) === 0x80 && ($bytes[3] & 0xC0) === 0x80
                ? (($bytes[0] & 0x07) << 18) | (($bytes[1] & 0x3F) << 12) | (($bytes[2] & 0x3F) << 6) | ($bytes[3] & 0x3F)
                : false;
    }

    return false;
}

function utf8_chr ($ord)
{
    switch (true) {
        case $ord < 0x80:
            return pack('C*', $ord & 0x7F);
        case $ord < 0x0800:
            return pack('C*', (($ord & 0x07C0) >> 6) | 0xC0, ($ord & 0x3F) | 0x80);
        case $ord < 0x010000:
            return pack('C*', (($ord & 0xF000) >> 12) | 0xE0, (($ord & 0x0FC0) >> 6) | 0x80, ($ord & 0x3F) | 0x80);
        case $ord < 0x110000:
            return pack('C*', (($ord & 0x1C0000) >> 18) | 0xF0, (($ord & 0x03F000) >> 12) | 0x80, (($ord & 0x0FC0) >> 6) | 0x80, ($ord & 0x3F) | 0x80);
    }

    return false;
}

【讨论】:

  • 我第一次看到switch(true);整洁的。关于您的utf8_ord——当您可以使用n* 直接解压缩成小数时,为什么要解压缩成字符?
  • @Mark 因为您需要检查每个字符。您需要从第一个字节的右侧提取可变数量的位(5、4 或 3),并从每个后续字节中提取 6 个尾随位。一次处理这 1 个字节要简单得多。除非我错过了什么。话虽如此,我可能会将其转换为 N*(您需要一个 long 值,因为它可能是 4 个字节),因为这样更容易验证它确实是一个 UTF-8 字符。 switch (TRUE) 就像一个 elseif 树,你将每个表达式评估为布尔值,在这种特定情况下,我认为它更具可读性,YMMV。
  • @Mark 事实上,this 是你必须使用字符而不是短裤的原因。因为任何不适合特定多字节序列的内容都会被unpack() 忽略
  • @Mark 验证现已添加到utf8_ord()
猜你喜欢
  • 1970-01-01
  • 2019-01-17
  • 2014-12-22
  • 2013-10-09
  • 2019-09-26
  • 2018-07-03
  • 2013-12-16
  • 2020-11-06
  • 2017-05-16
相关资源
最近更新 更多