Tag Archives: definição de unicode

Convertendo ISO-8859-1 para UTF-8 de forma segura

Introdução

Já falamos várias vezes sobre Unicode, desde a definição de unicodefunções para manupular unicodecomo utilizar unicode em todas camadas do sistema e como representar os símbolos na forma de html entities.

Sabemos que ISO-8859-1 foi uma codificação muito utilizada antes do surgimento do Unicode, mas que a tendência é que tudo seja migrado para Unicode e, no caso do ISO-8859-1, preferencialmente migrado para UTF-8. Porém, aplicações web estão sujeitas a situações das mais adversas, quando se trata de dados enviados pelo usuário. Existem casos em que um usuário consegue colar um texto em que parte dele deveria ser ISO-8859-1 e parte dele deveria ser UTF-8. Isso pode ocorrer ao copiar e colar trechos de aplicações para aplicações em sistemas de “origem duvidosa”, que acabam gerando uma bagunça de bytes que, por acaso, acabam sendo enviadas para nossa aplicação.

Para resolver este problema, elaborei uma função parecida com utf8_encode, ou seja, converte os caracteres de ISO-8859-1 para UTF-8, porém, caso a função identifique um caractere UTF-8 no texto, ela o mantem intacto. Portanto, ela é útil para garantir que o texto final seja 100% UTF-8 válido.

Copyright 2012 Rubens Takiguti Ribeiro

Licença: LGPL 3 ou superior

/**
 * Função que converte caracteres ISO-8859-1 para UTF-8, mantendo os caracteres UTF-8 intactos.
 * @param string $texto
 * @return string
 */
function sanitizar_utf8($texto) {
    $saida = '';

    $i = 0;
    $len = strlen($texto);
    while ($i < $len) {
        $char = $texto[$i++];
        $ord  = ord($char);

        // Primeiro byte 0xxxxxxx: simbolo ascii possui 1 byte
        if (($ord & 0x80) == 0x00) {

            // Se e' um caractere de controle
            if (($ord >= 0 && $ord <= 31) || $ord == 127) {

                // Incluir se for: tab, retorno de carro ou quebra de linha
                if ($ord == 9 || $ord == 10 || $ord == 13) {
                    $saida .= $char;
                }

            // Simbolo ASCII
            } else {
                $saida .= $char;
            }

        // Primeiro byte 110xxxxx ou 1110xxxx ou 11110xxx: simbolo possui 2, 3 ou 4 bytes
        } else {

            // Determinar quantidade de bytes analisando os bits da esquerda para direita
            $bytes = 0;
            for ($b = 7; $b >= 0; $b--) {
                $bit = $ord & (1 << $b);
                if ($bit) {
                    $bytes += 1;
                } else {
                    break;
                }
            }

            switch ($bytes) {
            case 2: // 110xxxxx 10xxxxxx
            case 3: // 1110xxxx 10xxxxxx 10xxxxxx
            case 4: // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
                $valido = true;
                $saida_padrao = $char;
                $i_inicial = $i;
                for ($b = 1; $b < $bytes; $b++) {
                    if (!isset($texto[$i])) {
                        $valido = false;
                        break;
                    }
                    $char_extra = $texto[$i++];
                    $ord_extra  = ord($char_extra);

                    if (($ord_extra & 0xC0) == 0x80) {
                        $saida_padrao .= $char_extra;
                    } else {
                        $valido = false;
                        break;
                    }
                }
                if ($valido) {
                    $saida .= $saida_padrao;
                } else {
                    $saida .= ($ord < 0x7F || $ord > 0x9F) ? utf8_encode($char) : '';
                    $i = $i_inicial;
                }
                break;
            case 1:  // 10xxxxxx: ISO-8859-1
            default: // 11111xxx: ISO-8859-1
                $saida .= ($ord < 0x7F || $ord > 0x9F) ? utf8_encode($char) : '';
                break;
            }
        }
    }
    return $saida;
}

Fonte, Convertendo ISO-8859-1 para UTF-8 de forma segura.