Problemas com codificação: acentos não interpretados

Problemas com codificação: acentos não interpretados

Um dúvida muito recorrente em fóruns concerne a erros com acentuação, quando palavras como “atenção” são exibidas como “aten??o”. Ou ficam com caracteres estranhos, como na imagem acima.

A solução geralmente é muito simples: padronizar as codificações de arquivos, do banco de dados e do HTML.


A melhor codificação a ser usada é a UTF-8, que suporta diversos idiomas, e tem sido adotada como codificação padrão em diversas aplicações.

E importante manter todas as codificações iguais, ou seja, todas em UTF-8. Para um site, é preciso atentar para as seguintes codificações:

Codificação do arquivo propriamente dito

É a codificação do arquivo dentro do sistema operacional. Ela pode ser modificada por meio do seu editor de textos (Sublime, Notepad++, Eclipse, Netbeans e outros). Também é possível alterar a codificação de um arquivo com o comando iconv, em ambientes Linux ou Mac OS X, da seguinte forma:

$ iconv -f codificacao_original -t utf-8 arquivo > arquivo_utf8

Para mais detalhes sobre como usar o comando iconv, veja este tutorial.

UTF-8 Sem BOM

O BOM (Byte Order Mark, ou Marca de Ordem de Byte) é uma sequência de
caracteres que é inserida no início de um arquivo para definir a ordem dos bytes. Isso é uma razão de um dos principais problemas com o uso de sessões e cookies, como descrevo aqui.

Por isso use sempre UTF-8 sem BOM e não terá problemas.

Codificação do HTML

A codificação do HTML é definida por meio de meta tag ou da função header(), do PHP.

Definição por meio de meta tag, para HTML 5:

<meta charset="utf-8" />

Definição por meio de meta tag, para HTML 4 e XHTML:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Definição por meio da função header(), do PHP:

1
header( 'Content-Type: text/html; charset=utf-8' );

Codificação do banco de dados e das tabelas

Se a sua aplicação usa um banco de dados, ele precisa, também, estar na codificação usada – UTF-8 no nosso caso. Ela é definida quando se criam o banco de dados e as tabelas. Por exemplo, com MySQL, podemos definir as codificação assim:

Codificação do banco de dados:

CREATE DATABASE bancodedados DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

Codificação das tabelas:

CREATE TABLE tabela(
	id SMALLINT(5) UNSIGNED NOT NULL auto_incre3ment,
	nome VARCHAR(20) NOT NULL,
	sobrenome VARCHAR(50) NOT NULL,
	PRIMARY KEY (id)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

Assim, todos os campos do tipo string estarão na codificação utf8.

Migrando Dados Não-UTF-8 Para UTF-8

Muitas vezes, precisamos migrar os dados de um banco de dados que não está em UTF-8 para outro que está em UTF-8.

Se apenas fizermos um SELECT em um e um INSERT no outro, erros de codificação vão aparecer para todo lado. É necessário remover os caracteres não-utf-8. E a melhor forma que encontrei para fazer isso é utilizando a seguinte rotina:

$regex = <<<'END'
/
  (
    (?: [\x00-\x7F]               # single-byte sequences   0xxxxxxx
    |   [\xC0-\xDF][\x80-\xBF]    # double-byte sequences   110xxxxx 10xxxxxx
    |   [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences   1110xxxx 10xxxxxx * 2
    |   [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3 
    ){1,100}                      # ...one or more times
  )
| ( [\x80-\xBF] )                 # invalid byte in range 10000000 - 10111111
| ( [\xC0-\xFF] )                 # invalid byte in range 11000000 - 11111111
/x
END;
 
function utf8replacer($captures) {
  if ($captures[1] != "") {
    // Valid byte sequence. Return unmodified.
    return $captures[1];
  }
  elseif ($captures[2] != "") {
    // Invalid byte of the form 10xxxxxx.
    // Encode as 11000010 10xxxxxx.
    return "\xC2".$captures[2];
  }
  else {
    // Invalid byte of the form 11xxxxxx.
    // Encode as 11000011 10xxxxxx.
    return "\xC3".chr(ord($captures[3])-64);
  }
}
preg_replace_callback($regex, "utf8replacer", $text);

Esse código eu retirei deste post no StackOverflow.

Problemas persistentes

Se você usava codificações diferentes, e, depois, modificou apenas a codificação do banco de dados, as acentuações, provavelmente, continuarão erradas. Não basta apenas mudar a codificação, nesse caso; será necessário passar os dados para outra tabela, que esteja, inicialmente, na codificação correta. É possível fazer essa transferência de dados por meio de apenas uma consulta. Para MySQWL, por exemplo, é possível usar um INSERT INTO… SELECT.

No meu Guia Gratuito de 15 Dicas e Boas Práticas de PHP falo mais sobre isso. Além dessa, são outras 14 dicas que vão deixar seus sistemas PHP mis estáveis, seguros e eficientes. Clique aqui para baixar o guia gratuito agora mesmo.

Essa e inúmeras outras dicas e técnicas eu abordo com mais detalhes no meu Curso ULTIMATE PHP. Estude PHP desde o básico, de forma 100% prática. Aprenda como se tornar um excelente programador, reconhecido e valorizado. Clique Aqui e Conheça o Curso Agora Mesmo!

 

Aprenda Ainda Mais

15 Dicas, Boas Práticas e Fundamentos do PHP

Conheça Dicas FUNDAMENTAIS para programar em PHP de forma Profissional

Não se considere Programador PHP sem antes ler este guia e adotar estas práticas!

Baixe gratuitamente este guia com 15 Dicas de PHP

The following two tabs change content below.
Graduado em Ciência da Computação, pela Universidade Federal do Paraná (UFPR), é desenvolvedor de software desde 2008, com foco em Desenvolvimento Web com PHP.
  • Rui Afonso

    blz? cara vc poderia me ajudar,gravei um comando com um chip que grava rs232 entre a cpu e o periferico de destino do comando,mas entre os dois tem uma placa multiserial que estão conectados outros perifericos,tem como eu manda este comando atraves de um outro periferico sem que ele volte na cpu ou se passar mesmo que o sistema operacional der erro de leitura(é o que vai acontecer)esse comando va para o periferico de destino? ou seja usando outro periferico que esteja conectado a essa placa,é possivel?se puder me tire essa duvida.Obrigado.

  • Muito interessante e útil o post. Vou usar nos projetos futuros este padrão.

  • Gabriel Haase

    Olá Beraldo, boa tarde.
    Sempre utilizei em meus projetos, bases de dados com charser latin1, a Collation latin1_swedish_ci e as páginas charset ISO-8859-1 e os processo de gravação, exibição e pesquisa sempre funcionaram corretamente. Iniciei um novo projeto, e nele pretendo passar a utilizar o UTF-8 como padrão. Criei a base de dados e as tabelas utilizando o charset utf-8 e collation utf8_general_ci. Se efetuo testes diretamente na base de dados para gravar ou buscar registros na base utilizando o phpmyadmin ou do mysql query browser, tudo funciona corretamente. Porém, quando executo uma inserção através de um formulário html (onde o charset da página e do header do php estão definidos para utf-8) as palavras acentuadas são gravadas no banco com caracteres estranhos. Ex: ‘acentuação’ -> ‘acentuação’.

    O mais estranho é que visualizo o erro apenas quando efetuo a query diretamente no banco através do phpmyadmin ou do mysql query browser, porém, quando ela é executada através do browser os dados são exibidos corretamente.

    Surge então um problema na pesquisa. Se o registro foi inserido pelo browser, sendo gravado ‘acentuação’, e caso haja uma busca onde o usuário do sistema procure a palavra ‘acentuacao’ sem utilizar os caracteres especiais o mySQL não retorna nada, mas caso ele busque ‘acentuação’, como foi gravado no banco, ele consegue retornar o registro.

    Mas…. Caso eu grave o registro diretamente no banco utilizando o phpmyadmin ou o mysql query browser ele será gravado corretamente, da mesma forma como foi inserido, aí sim a busca funciona perfeitamente. No caso de termos gravado no banco a palavra ‘acentuação’ e buscarmos ‘acentuacao’, ‘acao’, ‘cão’ ele me retorna o registro corretamente.

    Com este quadro pergunto??? Onde estou errando??? Tendo o arquivo, o browser e o banco completamente em utf-8, pode o PHP estar influenciando este problema??

    Desde já agradeço.
    Gabriel Haase

    • Elvis

      2 Anos depois do seu Post e estou exatamente com o mesmo problema, poderia me ajudar?

      Você conseguiu solucionar este seu problema ?

  • @Gabriel Haase

    Verifique a codificação do arquivo, como mostrei postando imagens do GEdit e do Eclipse. Se a mata tag/header() está correto, o problema está na codificação do arquivo (definida no editor de textos)

  • Fábio Camargo

    Olá Caro Beraldo,
    gostei de suas explicações e por isso venho contacta-lo,

    pois estou com estes problemas, e fiz conforme tudo o que vc expos, mas não estou conseguindo solucionar…

    refiz minha base baseada na sua…
    reeditei as pages tudo esta com UTF-8 como desde o principio estou trabalhando.

    preciso de sua ajuda por gentileza!

    forte abraço!

  • Fábio Camargo

    consegui resolver com esse exemplo a maior parte dos problemas…

    $testo =utf8_decode( $_POST[‘testo’]);

    $sql = (“INSERT INTO tbl_latim (testo) VALUES (‘$testo’)”);
    mysql_query($sql) or die (mysql_error());

    para exbir

    $query = mysql_query(“SELECT * FROM tbl_latim”);
    while($array = mysql_fetch_array($query,MYSQL_ASSOC)){

    echo “id:“.$array[‘id’].””;
    echo “Testo:“.htmlentities($array[‘testo’]).””;
    echo “”;
    }

    porém a recepção com o htmlentities fica, por exemplo [á – ã] e não gosto disso devido o SEO…
    sem o htmlentities, o retorno do banco de dado vem “�”…

    enviar para o banco de dados esta blz… ele recebe com acentuação, porém a recepção na pagina (q esta UTF-8) não é agradável…

    pode me ajudar?

  • Fábio Camargo

    Olá mais uma vez…
    achei um jeito de resolver… se vc souber de algo melhor me avisa por favor….

    meu jeito:

    function translate_Html($str) {
    $str = htmlentities($str);

    $quote[] = "Á";
    $translate[] = "Á";
    $quote[] = "á";
    $translate[] = "á";
    $quote[] = "Â";
    $translate[] = "Â";
    $quote[] = "â";
    $translate[] = "â";
    $quote[] = "À";
    $translate[] = "À";
    $quote[] = "à";
    $translate[] = "à";
    $quote[] = "Å";
    $translate[] = "Å";
    $quote[] = "å";
    $translate[] = "å";
    $quote[] = "Ã";
    $translate[] = "Ã";
    $quote[] = "ã";
    $translate[] = "ã";
    $quote[] = "Ä";
    $translate[] = "Ä";
    $quote[] = "ä";
    $translate[] = "ä";
    $quote[] = "Æ";
    $translate[] = "Æ";
    $quote[] = "æ";
    $translate[] = "æ";
    $quote[] = "É";
    $translate[] = "É";
    $quote[] = "é";
    $translate[] = "é";
    $quote[] = "Ê";
    $translate[] = "Ê";
    $quote[] = "ê";
    $translate[] = "ê";
    $quote[] = "È";
    $translate[] = "È";
    $quote[] = "è";
    $translate[] = "è";
    $quote[] = "Ë";
    $translate[] = "Ë";
    $quote[] = "ë";
    $translate[] = "ë";
    $quote[] = "Ð";
    $translate[] = "Ð";
    $quote[] = "ð";
    $translate[] = "ð";
    $quote[] = "Í";
    $translate[] = "Í";
    $quote[] = "í";
    $translate[] = "í";
    $quote[] = "Î";
    $translate[] = "Î";
    $quote[] = "î";
    $translate[] = "î";
    $quote[] = "Ì";
    $translate[] = "Ì";
    $quote[] = "ì";
    $translate[] = "ì";
    $quote[] = "Ï";
    $translate[] = "Ï";
    $quote[] = "ï";
    $translate[] = "ï";
    $quote[] = "Ó";
    $translate[] = "Ó";
    $quote[] = "ó";
    $translate[] = "ó";
    $quote[] = "Ô";
    $translate[] = "Ô";
    $quote[] = "ô";
    $translate[] = "ô";
    $quote[] = "Ò";
    $translate[] = "Ò";
    $quote[] = "ò";
    $translate[] = "ò";
    $quote[] = "Ø";
    $translate[] = "Ø";
    $quote[] = "ø";
    $translate[] = "ø";
    $quote[] = "Õ";
    $translate[] = "Õ";
    $quote[] = "õ";
    $translate[] = "õ";
    $quote[] = "Ö";
    $translate[] = "Ö";
    $quote[] = "ö";
    $translate[] = "ö";
    $quote[] = "Ú";
    $translate[] = "Ú";
    $quote[] = "ú";
    $translate[] = "ú";
    $quote[] = "Û";
    $translate[] = "Û";
    $quote[] = "û";
    $translate[] = "û";
    $quote[] = "Ù";
    $translate[] = "Ù";
    $quote[] = "ù";
    $translate[] = "ù";
    $quote[] = "Ü";
    $translate[] = "Ü";
    $quote[] = "ü";
    $translate[] = "ü";
    $quote[] = "Ç";
    $translate[] = "Ç";
    $quote[] = "ç";
    $translate[] = "ç";
    $quote[] = "Ñ";
    $translate[] = "Ñ";
    $quote[] = "ñ";
    $translate[] = "ñ";
    $quote[] = "<";
    $translate[] = "";
    $quote[] = "&";
    $translate[] = "&";
    $quote[] = """;
    $translate[] = """;
    $quote[] = "®";
    $translate[] = "®";
    $quote[] = "©";
    $translate[] = "©";
    $quote[] = "Ý";
    $translate[] = "Ý";
    $quote[] = "ý";
    $translate[] = "ý";
    $quote[] = "Þ";
    $translate[] = "Þ";
    $quote[] = "þ";
    $translate[] = "þ";
    $quote[] = "ß";
    $translate[] = "ß";
    $quote[] = " ";
    $translate[] = " ";
    /*

    */
    $i = 0;
    while(isset($quote[$i])){
    $str = str_replace($quote[$i],$translate[$i],$str);
    $i = $i + 1;
    }
    return $str;
    }

    assim… qndo tiver algum erro de caracteres a função resolve…
    como tinha dito… No banco de dados as acentuações, cecedilhas e etc, estão perfeitos, porem qndo vai para a página ficar com o formato NULO… �

    Porém com o que vc passou não foi possivel resolver, criei esta função q resolveu o problema que eu estava tendo…
    converte a string “$str” para htmlentities e o “while” efetua a troca dos caracteres htmlentities para os normais…

    espero ter ajudado alguém, pois mesmo com tudo que procurei na net, um dos mais próximos que encontrei para resolver foi o do R Beraldo…

    Abraço!

  • Olá, Fábio Camargo.

    Algo está com codificação errada. Eu uso UTF-8 em tudo e nunca preciso de htmlentities. Aliás, htmlentities gera mais problemas com UTF-8.

    Verifique a codificação do arquivo (aquela modificada pelo editor de textos)

  • Fábio Camargo

    Então Beraldo, eu fiz estas verificações e estão perfeitas…
    por isso não estou entendendo o que aconteceu…

    PS.: apague meus dois últimos comentários [além deste], não ficaram como eu quis

  • Fábio Camargo

    Repassando…
    a base e as tabelas no MySql esta com a codificação “utf8_general_ci”…
    as páginas estão salvas em UTF-8 e os heads:

    entendeu?

  • Manoel Filho

    Fábio

    Tive esse problema aqui com um sistema onde o banco atualizado tinha textos de planilhas do excel. Percebi que ao selecionar o formato texto nessa planilha, o próprio programa não reconhecia como texto. Os dados no Mysql ficaram perfeitos, já no html ficavam todos errados. Mesmo com a padronização do banco e das páginas para UTF-8. Sinistro isso. Sua função salvou minha vida. Abraço

  • Esse é o tipo de problema que não interessa aos criadores de códigos e afins da internet, mesmo porque a lingua deles (inglês), não tem esse tipo de acentuação. Imagina-se então como os chineses, japoneses, russos, etc. Na minha opinião não tem jeito. Deve-se criar uma sub onde se resolve caso por caso, letra por letra. É isso aí amigo! Até na informática existe monopólio.

  • Luiz Araujo

    Oi Pessoal,

    Estava com o mesmo problema.
    Meu banco, minhas tabelas e minhas páginas estavam usando utf8 mas mesmo assim as tabelas no MySQL tinham os caracteres estranhos…
    Resolvi mantendo o banco, tabelas e paginas com utf8 mas usei a função utf8_decode() para enviar os campos para o banco e a função utf8_encode() para puxar os campos de lá.
    Meus registros nas tabelas do MySQL estão com os acentos da língua portuguesa.
    Espero ter ajudado.
    []s,

    • Ronaldo

      Muito obrigado Luiz Araujo. Em páginas que eu enviava pelo submit do form estava gravando no BD certinho. Mas nas que eu enviava o post via javascript, sem submit do form dava esse problema com acentuadas. Segui o seu conselho e deu certinho.
      antes de executar o UPDATE em sql eu fiz o tratamento do valor da variável assim:
      $text = utf8_decode($texto);
      E depois fiz o UPDATE normalmente
      UPDATE tabela
      SET comentario = ‘$text’, …
      Valeu mesmo!!

    • Lucas

      Muito obrigada cara, salvou minha vida. Tinha tentado de tudo !

  • heu

    Para quem mexe com cakePhp e está tendo problemas com a codificação dos caracteres, basta acrescentar (ou descomentar) ‘encoding’ => ‘UTF8′ no arquivo ‘app/config/database.php’

    • Alisson

      Show, valeu cara!

  • Evandro

    Pessoal,

    No meu celular (sony experia m2), tenho uma conto do gmail, e quando alguém me envia um email, não consigo visualizar os caracteres corretamente, conforme vejo nos comentários acima, ex: atenç5?9.

    Alguém poderia me ajudar a corrigir?

    • Olá, Evandro.

      Procure um fórum sobre celulares. Seu problema é configuração do celular. Aqui neste post eu falo de codificação de aplicações PHP. Não é o melhor lugar para buscar a solução pro seu problema.

      Abraço,
      Beraldo

  • berg

    Estava com esse problema nesses dias a solução passou por duas etapas a primeira foi trocar a meta tag para utf8 e depois converter a codificação do arquivo de ansi para utf8 sem bom.

  • Josiel de Assis

    Nomes acentuados na pasta do Shell do Linux, como copiar?

    Quero copiar a pasta “Músicas” para outra parte do site usando o Cron

    Mas a programação só aceita “Musica” (sem acento). Porém o acento tem que ser mantido para o acesso ao site.

    Como lidar com isso?

  • Hugo

    Mano, toda vez que publico um texto em meu site esta aparecendo o caractere “null” antes dos textos..n consigo modificar isso. Oq pode ser?
    Vlw, obg

    • Olá, Hugo.
      “null” não tem a ver com codificação. Isso deve ser o retorno de alguma consulta do banco de dados, ou até algo ligado ao Javascript

  • sergiomaia

    Poderia me ajudar;…. no sistema de suporte q instalei subi nao está funcionando a tradução da erro na acentuação.

    ‘charset’ => ‘utf8’,
    ‘collation’ => ‘utf8_unicode_ci’,
    ‘prefix’ => ”,

    Já tentei subir o banco de dados como iso8859-1 e mudar acima mas da erro;

    http://www.lojavirtualopen.com.br/demo/opensuporte/guest-ticket

    Pode dar uma força?

    abraços

    • Olá. Verifique atentamente todos os pontos que apresentei no artigo. Os arquivos devem estar em UTF-8 sem BOM, todas as codificações devem estar em UTF-8. Se já tiver registros no banco em ISO, não basta alterar a codificação para UTF-8; é preciso migrar os dados para uma nova tabela e remover caracteres não-utf-8

  • Guilherme Paes Weber

    Bom dia, a muito tempo trabalho com iso, e nunca houve a necessidade de migrar para o utf8, porem nesse momento estou em um projeto que preciso mudar para utf8 e estou com duvidas.

    meus arquivos html estão salvos com encoding utf8 sem bom…
    minhas tabelas do banco foram recriadas e salvas como utf8, charset utf8 general ci…

    qd eu escrevoalgum caracter especial, não faço nenhum tratamento de encoding decoding ou html entities, então no banco de dados fica com caracteres estranhos, mas qd eu faço o select e mostro no html, ele me mostra os caracteres normalmente…

    minha dúvida é, se é normal q fique com caracteres estranhos no banco, creio que se tud estivesse certo não… então eu preciso fazer algum tratamento antes de salvar no banco? ou meu banco é q ainda nao está configurado corretamente?

    desde já agradeço a atenção e todo o material disponibilizado.

    • Olá
      Depende também do programa que você está usando para ver os dados do banco. Se ele não usar UTF-8, vai aparecer de forma errada, mesmo.
      A melhor forma de verificar isso é pelo Terminal/Prompt, desde que ele (e o seu SO) também esteja em UTF-8

      • Fernando Santana

        Olá Roberto e Guilherme, estou tendo o mesmo problema, testei no phpMyAdmin, HeidiSql e no Mysql Workbench. E fiz um teste a mais, se eu altero o texto usando qualquer um desses programas ele aparece com o caracter errado no sistema.

        • Guilherme Paes Weber

          vlw Fernando, eu acabei de corrigir o problema meio que por acaso, acho até q ja tinha corrigido o problema mas nao tinha percebido, pois estou usando ajax em um lugar e estava testando com ajax as alterações…

          bom nos inserts normais ja estava funcionando, no ajax eu só precisei dar um utf8_encode() e qd busco via ajax utf8_decode()

          o segredo aqui foi usar tb o q vc comentou acima, setar via php o charset utf8

          mysql_query(“SET NAMES ‘utf8′”);
          mysql_query(‘SET character_set_connection=utf8’);
          mysql_query(‘SET character_set_client=utf8’);
          mysql_query(‘SET character_set_results=utf8’);

          minhas tabelas ja estavam funcionando, certinho, só o ajax precisou de tratamento antes de inserir…

          agradeço a ajuda a todos.

          • Ae guilherme vlw pela dica do utf8 encode e decode(). Estava passando um dados aqui pelo jquery file upload e ele tava indo assim começar. Quando coloquei o utf8_decode() normalizou. Claro antes passa tudo pra utf-8 até o cabeçãlho. Vlw mesmo Deus te abençoe

    • Fernando Santana

      Olá Guilherme acho que achei a resposta para nós, na verdade conteúdo não está sendo gravado como utf8 pois a conexão do banco não está sendo em utf8.
      Exemplo no php, só para ilustar:
      $mysqli = new mysqli(“localhost”, “my_user”, “my_password”, “test”);
      $mysqli->set_charset(“utf8”);