¿Cómo decodificar secuencias de escape Unicode como "\ u00ed" en caracteres codificados en UTF-8 adecuados?

97

¿Existe una función en PHP que pueda decodificar secuencias de escape Unicode como " \u00ed" a " í" y todas las demás ocurrencias similares?

Encontré una pregunta similar aquí, pero parece que no funciona.

Docstero
fuente

Respuestas:

169

Prueba esto:

$str = preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function ($match) {
    return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
}, $str);

En caso de que sea C / C ++ / Java / Json-style basado en UTF-16:

$str = preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function ($match) {
    return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UTF-16BE');
}, $str);
Gumbo
fuente
1
¿Dónde pongo "\ u00ed"?
Docstero
2
@Docstero: La expresión regular coincidirá con cualquier secuencia \useguida de cuatro dígitos hexadecimales.
Gumbo
9
Esta función no puede tratar con caracteres suplementarios ya que no se pueden representar en UCS-2.
Artefacto
3
@gumbo ¿Cómo se llama o usa esta función?
Demodave
2
Encontré mi camino aquí como lo había hecho \ u00ed en mi salida, pero estaba mirando la salida con json_encode () y, curiosamente, el json_encode () predeterminado eliminará la salida, así que use json_encode ($ theDict, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
Tom Andersen
71
print_r(json_decode('{"t":"\u00ed"}')); // -> stdClass Object ( [t] => í )
2BJ
fuente
44
Ni siquiera necesita la envoltura de objetos:json_decode('"' . $text . '"')
deceze
3
Gracias. Esta parece ser una forma ESTÁNDAR , en lugar de una respuesta aceptada.
T.Todua
Curiosamente, esto también funciona para entidades complejas como caras sonrientes ... json_decode('{"t":"\uD83D\uDE0A"}')es 😊
DynamicDan
2
@deceze debe incluir el hecho de que $textpuede incluir comillas dobles. Por lo que una versión revisada sería: json_decode('"'.str_replace('"', '\\"', $text).'"'). Gracias por tu ayuda :-)
Yvan
10
$str = '\u0063\u0061\u0074'.'\ud83d\ude38';
$str2 = '\u0063\u0061\u0074'.'\ud83d';

// U+1F638
var_dump(
    "cat\xF0\x9F\x98\xB8" === escape_sequence_decode($str),
    "cat\xEF\xBF\xBD" === escape_sequence_decode($str2)
);

function escape_sequence_decode($str) {

    // [U+D800 - U+DBFF][U+DC00 - U+DFFF]|[U+0000 - U+FFFF]
    $regex = '/\\\u([dD][89abAB][\da-fA-F]{2})\\\u([dD][c-fC-F][\da-fA-F]{2})
              |\\\u([\da-fA-F]{4})/sx';

    return preg_replace_callback($regex, function($matches) {

        if (isset($matches[3])) {
            $cp = hexdec($matches[3]);
        } else {
            $lead = hexdec($matches[1]);
            $trail = hexdec($matches[2]);

            // http://unicode.org/faq/utf_bom.html#utf16-4
            $cp = ($lead << 10) + $trail + 0x10000 - (0xD800 << 10) - 0xDC00;
        }

        // https://tools.ietf.org/html/rfc3629#section-3
        // Characters between U+D800 and U+DFFF are not allowed in UTF-8
        if ($cp > 0xD7FF && 0xE000 > $cp) {
            $cp = 0xFFFD;
        }

        // https://github.com/php/php-src/blob/php-5.6.4/ext/standard/html.c#L471
        // php_utf32_utf8(unsigned char *buf, unsigned k)

        if ($cp < 0x80) {
            return chr($cp);
        } else if ($cp < 0xA0) {
            return chr(0xC0 | $cp >> 6).chr(0x80 | $cp & 0x3F);
        }

        return html_entity_decode('&#'.$cp.';');
    }, $str);
}
masakielástica
fuente
Gracias. Esto parece funcionar con un carácter complementario, como😍
c00000fd
3

Este es un enfoque de mazo para reemplazar UNICODE sin formato con HTML. No he visto ningún otro lugar para poner esta solución, pero supongo que otros han tenido este problema.

Aplique esta función str_replace al RAW JSON , antes de hacer cualquier otra cosa.

function unicode2html($str){
    $i=65535;
    while($i>0){
        $hex=dechex($i);
        $str=str_replace("\u$hex","&#$i;",$str);
        $i--;
     }
     return $str;
}

Esto no tomará tanto tiempo como cree, y reemplazará CUALQUIER unicode con HTML.

Por supuesto, esto se puede reducir si conoce los tipos Unicode que se devuelven en JSON.

Por ejemplo, mi código estaba recibiendo muchas flechas y unicode dingbat. Estos están entre 8448 y 11263. Entonces, mi código de producción se ve así:

$i=11263;
while($i>08448){
    ...etc...

Puede buscar los bloques de Unicode por tipo aquí: http://unicode-table.com/en/ Si sabe que está traduciendo árabe o Telegu o lo que sea, puede simplemente reemplazar esos códigos, no todos los 65,000.

Puede aplicar este mismo mazo a una codificación simple:

 $str=str_replace("\u$hex",chr($i),$str);
Nemo Noman
fuente
1

También hay una solución:
http://www.welefen.com/php-unicode-to-utf8.html

function entity2utf8onechar($unicode_c){
    $unicode_c_val = intval($unicode_c);
    $f=0x80; // 10000000
    $str = "";
    // U-00000000 - U-0000007F:   0xxxxxxx
    if($unicode_c_val <= 0x7F){         $str = chr($unicode_c_val);     }     //U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
    else if($unicode_c_val >= 0x80 && $unicode_c_val <= 0x7FF){         $h=0xC0; // 11000000
        $c1 = $unicode_c_val >> 6 | $h;
        $c2 = ($unicode_c_val & 0x3F) | $f;
        $str = chr($c1).chr($c2);
    } else if($unicode_c_val >= 0x800 && $unicode_c_val <= 0xFFFF){         $h=0xE0; // 11100000
        $c1 = $unicode_c_val >> 12 | $h;
        $c2 = (($unicode_c_val & 0xFC0) >> 6) | $f;
        $c3 = ($unicode_c_val & 0x3F) | $f;
        $str=chr($c1).chr($c2).chr($c3);
    }
    //U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    else if($unicode_c_val >= 0x10000 && $unicode_c_val <= 0x1FFFFF){         $h=0xF0; // 11110000
        $c1 = $unicode_c_val >> 18 | $h;
        $c2 = (($unicode_c_val & 0x3F000) >>12) | $f;
        $c3 = (($unicode_c_val & 0xFC0) >>6) | $f;
        $c4 = ($unicode_c_val & 0x3F) | $f;
        $str = chr($c1).chr($c2).chr($c3).chr($c4);
    }
    //U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    else if($unicode_c_val >= 0x200000 && $unicode_c_val <= 0x3FFFFFF){         $h=0xF8; // 11111000
        $c1 = $unicode_c_val >> 24 | $h;
        $c2 = (($unicode_c_val & 0xFC0000)>>18) | $f;
        $c3 = (($unicode_c_val & 0x3F000) >>12) | $f;
        $c4 = (($unicode_c_val & 0xFC0) >>6) | $f;
        $c5 = ($unicode_c_val & 0x3F) | $f;
        $str = chr($c1).chr($c2).chr($c3).chr($c4).chr($c5);
    }
    //U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    else if($unicode_c_val >= 0x4000000 && $unicode_c_val <= 0x7FFFFFFF){         $h=0xFC; // 11111100
        $c1 = $unicode_c_val >> 30 | $h;
        $c2 = (($unicode_c_val & 0x3F000000)>>24) | $f;
        $c3 = (($unicode_c_val & 0xFC0000)>>18) | $f;
        $c4 = (($unicode_c_val & 0x3F000) >>12) | $f;
        $c5 = (($unicode_c_val & 0xFC0) >>6) | $f;
        $c6 = ($unicode_c_val & 0x3F) | $f;
        $str = chr($c1).chr($c2).chr($c3).chr($c4).chr($c5).chr($c6);
    }
    return $str;
}
function entities2utf8($unicode_c){
    $unicode_c = preg_replace("/\&\#([\da-f]{5})\;/es", "entity2utf8onechar('\\1')", $unicode_c);
    return $unicode_c;
}
jianyong
fuente
1

corrige los valores json, se agrega \ antes de u {xxx} a todos + ""

  $item = preg_replace_callback('/"(.+?)":"(u.+?)",/', function ($matches) {
        $matches[2] = preg_replace('/(u)/', '\u', $matches[2]);
            $matches[2] = preg_replace('/(")/', '&quot;', $matches[2]); 
            $matches[2] = json_decode('"' . $matches[2] . '"'); 
            return '"' . $matches[1] . '":"' . $matches[2] . '",';
        }, $item);
orel
fuente