Problemas com Charset? Nunca mais

Artigo que apresenta os principais problemas com codificação de caracteres e mostra o passo a passo de como definir o charset (encoding) UTF-8 para todas as áreas dos seus projetos PHP.

Hommer assustado com um caractere exibido incorretamente
Introdução

Vira e mexe existe alguém aparecendo com problemas com apresentação de caracteres por conta de codificação de caracteres errada. Já escrevi sobre Unicode, Códigos e Símbolos Unicode e sobre HTML entities. Mas neste artigo, veremos como utilizar UTF-8 em tudo e nunca mais ver caracteres sendo exibidos errado.


1º Salve o código-fonte em UTF-8

Antes de mais nada, escolha um bom editor de código-fonte, que lhe permita definir qual a codificação utilizada nos arquivos salvos. Normalmente isso fica nas configurações do editor ou nas opções do momento de salvar.

Se você usa editores em modo texto (vi, nano, pico, etc), talvez seja necessário configurar a codificação nas configurações do próprio emulador de terminal de comandos (gnome-terminal, xterm, etc). E também pode ser necessário definir a variável de ambiente LANG com valor "pt_BR.UTF8" no terminal, antes de abrir o editor, como mostrado a seguir:

$ export LANG=pt_BR.UTF8

Observação: alguns editores tem opção de salvar o arquivo com o BOM. Recomenda-se que não salve o arquivo com estes bytes de marcação, pois eles podem causar comportamento inesperado no PHP. Por exemplo, você não vai conseguir chamar funções como header ou utilizar o recurso de namespaces, que obriga que a declaração de namespace seja a primeira coisa no script.


2º Informe ao navegador que você usa UTF-8

Quando um arquivo PHP gera um HTML e é enviado para o navegador, junto com o arquivo vai um cabeçalho (do protocolo HTTP), onde é especificado o tipo de arquivo e a codificação. Caso você não informe ela explicitamente no seu código, o seu servidor HTTP (por exemplo, o Apache) irá enviar tal arquivo com um mime-type padrão (normalmente "text/html") e uma codificação padrão (normalmente "ISO-8859-1").

Para mudar este cabeçalho explicitamente, e informar corretamente o mime-type e a codificação do documento que você está gerando, utilize a função header, passando a diretiva "Content-type", conforme exemplo:

<?php
header('Content-Type: text/html; charset=UTF-8');
echo '<html>';
...

Caso o arquivo seja de outro tipo, basta mudar o mime-type para o tipo correspondente (por exemplo, "text/css", "text/xml", "application/xhtml+xml", etc).

Porém, nem sempre os arquivos são gerados via PHP. Existem HTML estáticos que precisam informar o cabeçalho HTTP com o mime-type e codificação corretos. Neste caso, existe uma alternativa que é usando a tag meta com o atributo "http-equiv" (equivalente HTTP). Com ela, é possível "simular" cabeçalho do HTTP pelo próprio conteúdo do documento HTML. Isso é feito da seguinte forma:

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

No HTML 5, isso foi simplificado:

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="UTF-8" >
...

Nos arquivos CSS, você pode opcionalmente informar a codificação na primeira linha do arquivo:

@charset "UTF-8";
...

Caso você utilize XML ou XHTML, lembre-se de informar a codificação UTF-8 no cabeçalho XML:

<?xml version="1.0" encoding="UTF-8" ?>
<html>
...

Ao incluir CSS o JS no seu HTML, também é possível especificar o charset destes arquivos:

...
<link rel="stylesheet" type="text/css" href="estilo.css" charset="UTF-8" />
<script src="exemplo.js" type="text/javascript" charset="UTF-8" ></script>
...

3º Comunique-se com o BD via UTF-8

Para que as informações sejam trafegadas entre o PHP e o Banco de Dados usando UTF-8, é preciso declarar esta codificação logo que se conecta ao banco. Isso varia de banco para banco, mas vamos ver alguns exemplos comuns:

MySQL (PDO):

$dsn = "mysql:host=localhost;dbname=world;charset=utf8";
$opcoes = array(
    PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'
);
$pdo = new PDO($dsn, $usuario, $senha, $opcoes);
...

MySQLi:

$mysqli = new mysqli(...);
$mysqli->set_charset('utf8'));

MySQL (funções):

$conexao = mysql_connect(...);
mysql_select_db(...);
mysql_set_charset('UTF8', $conexao);
...

Observação: as funções de conexão com MySQL estão depreciadas. Prefira usar PDO ou MySQLi.

PostgreSQL (PDO):

$pdo = new PDO(...);
$pdo->query('SET NAMES UNICODE');
...

PostgreSQL (funções):

$conexao = pg_connect(...);
pg_set_client_encoding($conexao, 'UNICODE');
...

4º Crie seu Banco de Dados em UTF-8

Os campos de texto armazenados em bancos de dados também precisam de uma codificação de caracteres. Caso não seja definida no momento que se cria o campo, a codificação é obtida do padrão da tabela ou do bando de dados. Para definir um valor padrão de codificação de um banco de dados, use os comandos:

MySQL:

CREATE DATABASE nome_bd CHARACTER SET UTF8;

PostgreSQL:

CREATE DATABASE nome_bd ENCODING UNICODE;

5º Lembre-se de especificar a codificação UTF-8 onde puder

Algumas funções em PHP recebem como parâmetro a codificação a ser considerada. Algumas das mais importantes que devem ser ressaltadas são: htmlentities e htmlspecialchars.

Além disso, ao realizar operações com expressões regulares PCRE, lembre-se de utilizar o modificador "u" ao final da expressão, indicando que ela é UTF-8, conforme o exemplo:

preg_match('/^[a-z](.*)/u', $str, $matches);

Um conjunto importante de funções, leva em consideração a localidade (com uma codificação) para funcionar. Então, também é importante definir a localidade adequadamente com a localidade UTF-8:

setlocale(LC_ALL, 'pt_BR.utf8');

Lembre-se que a localidade depende do servidor e o nome utilizado pode variar.

A partir do PHP 5.3, é possível especificar a codificação de um arquivo PHP com uma chamada declare logo no início do arquivo PHP:

<?php
declare(encoding='UTF-8');
...

Conclusão

Tomando as devidas medidas, é possível utilizar UTF-8 sem grandes problemas em todas as camadas do seu sistema: no HTML, no PHP e no Banco de Dados. Erro de codificação? Nunca mais!

110 comentários

xico disse...

Letras pequenas no menu parecem propagandas.
Muitas artigos interessantes no seu blog.
Parabéns!!!11!

rubS (autor do blog) disse...

Obrigado, xico.
As letras pequenas são aquelas abaixo do título do blog? Realmente imaginei que poderiam parecer propaganda, mas, de qualquer forma, são apenas âncoras para itens da barra lateral.

Luiz Henrique disse...

Concordo plenamente com você, num adianta fazer uma salada de codificação e depois tentar corrigir com (funções), o negocio tem de vir padronizado da raiz. mais é isso ai..
Meus parabéns seus artigos são ótimos, sempre acrescentam algo.

Abraço

Anônimo disse...

Cara, e os arquivos xslt?!
eu tenho um arquivo xsl codificado com utf8.
usado por um xml que tbem eh utf8.
mas ele acusa erro qndo encontra um caracter com acento (estando esse caracter no arquivo xsl).
Tem alguma dica?!

rubS (autor do blog) disse...

Olá Anônimo.

Se você salvar os dois arquivos em UTF-8 e declalar que são UTF-8 (no cabeçalho do arquivo XML e do XSLT), provavelmente vai dar certo. É só usar o atributo "encoding".

Fiz um exemplo rápido aqui e funcionou. Se quiser, mando para você ver.

Anônimo disse...

Amigo, estou com um probleminha..
Tentei passar meu código para vc da uma olha mais não é aceito códigos de programação.

Não faz sentido, pois o blog é de programação web.

rubS (autor do blog) disse...

Olá, Anônimo,

Infelizmente o blogger é uma plataforma compartilhada para vários blogs, por isso ele não permite inclusão de código. Porém, se você quiser, pode colocar o código em algum site próprio para isso como o pastebin.com e depois indicar o link com a pergunta aqui nos comentários.

Régis disse...

bah, q luz foi esse artigo; sempre combati o utf-8 achando que isso era charset de gringo e nunca refleti realmente; eu saia setando locale latin1 e iso-8859-1 em tudo q podia (plataforma, so, php, mysql); sendo ainda mais sincero me considerava o máximo q configurava um editor de texto com apenas um caracter para quebra de linha. Ainda, notava q a codificação utf-8 numa folha de estilos aceitava + símbolos do q uma em iso-8859-1. Minha humilde contribuição a este artigo: acrescentar na primeira linha da folha de estilo '@charset "utf-8";' pra indicar a codificação... parabéns pelo blog

Roy disse...

Sei que ja faz um tempo esse post mas tenho uma duvida, como faço:

$conexao = mysql_connect(...);
mysql_select_db(...);
mysql_set_charset('UTF8', $conexao);

no doctrine?
se alguém souber e puder ajudar agradeço.

Anônimo disse...

Parabens!
Muito bom, ótimo conteúdo, bem explicado e otimos exemplos
Valeu!

Anônimo disse...

ainda estou me acustumando com o uso do utf-8 porem é a codificação padrão em todo mundo, assim sendo se você vai desenvolver um sistema nôvo salve os cod fontes em utf8 pra depois não ficar semanas ou mais como eu fiquei para converter do latin para utf.

Paulo disse...

Rubens, obrigado pela ajuda!
Resolveu meu problema por aqui.
Muito bom o texto!

Abraço,
Paulo

Anônimo disse...

Muito obrigado pelos posts! Eles hoje me adiantaram e muito em alguns problemas que estava quebrando a cabeça pra resolver!

Anônimo disse...

Poderia da uma luz?
Uso a conexão dessa maneira.
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e){
echo $e->getMessage();
exit(" Erro ao conectar.");
}
?>

na página o html está tudo certo nas no db sai aqueles acaracteres estranhos no lugar da acentuação..
Grato.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo

Talvez você esteja visualizando o BD de forma incorreta. Ao conectar no MySQL via terminal, por exemplo, você precisa executar o comando:

mysql> SET NAMES utf8;

Além disso, precisa garantir que o terminal esteja exibindo em UTF-8 também.

Anônimo disse...

No Charset já tive muitos problemas com ele, porque antigamente utilizava o ISO-8859-1, hoje com o novo UTF-8 eu posso utiliza-lo, a Solução que eu encontrei foi adicionar uma chatset diretamento no .htaccess e manter o padrão de definição em todo o projeto.

Diego Figueiró Dias disse...

Tive problemas com strtoupper com os caracteres especiais, para o caso de alguém também ter o mesmo problema, podem ser usadas as funções mbstring do PHP (http://php.net/manual/en/book.mbstring.php). No caso do strtoupper, a sintaxe é mb_strtoupper($string, 'UTF-8').

Para habilitar no Windows (PHP 5.4) é só descomentar a linha extension=php_mbstring.dll no php.ini. Acredito que no Linux o procedimento seja similar.

Anônimo disse...

Luiz:
Obrigado pelas dicas!
Segui sua orientação passei tudo pra UTF-8, tive de trocar meu editor de texto que nao escrevia em (UTF-8 sem BOM), mas agora esta tudo melhor. :)
Obrigado pelo artigo. Sucesso!

Bernardo disse...

Olá,

Eu tenho um código que faz a conexão no mysql por javascript. Como faço para comunicar com o BD em utf-8?

Segue o código:

ConectaBase();
}

// Conexao com banco
function ConectaBase()
{
$this->link = @mysql_pconnect( $this->servidor, $this->usuario, $this->senha);
if (!$this->link) {
die("Error na conexao: ".mysql_error()." - ".mysql_errno());
} elseif (!mysql_select_db($this->banco, $this->link)) {
die("Error na conexao: ".mysql_error()." - ".mysql_errno());
//mysql_errno(): numero do erro
//mysql_error(): descrição do erro
}
}

// Execução da query
function sql($query)
{
$this->query = $query;
//echo $this->query;
if ($result = mysql_query($this->query,$this->link)) {
return $result;
mysql_free_result($result);
} else {
return 0;
}
}

function tabelas()
{
if ($result = mysql_list_tables($this->banco)) {
return $result;
} else {
return 0;
}
}
}

?>

Obrigado!

Rubens Takiguti Ribeiro (autor do blog) disse...

Bernardo, este código que passou é PHP, não JavaScript.

Sobre a sua pergunta, para se comunicar com o BD em UTF-8, leia atentamente a sessão "3º Comunique-se com o BD via UTF-8" neste post, que lá estará a resposta.

Aproveito para alertá-lo de que as funções de conexão com MySQL já estão depreciadas. Se possível, utilize PDO ou MySQLi. Tem um artigo aqui no blog sobre PDO:
http://rubsphp.blogspot.com.br/2010/09/pdo.html

Bernardo disse...

Olá Rubens,

Obrigado pela resposta.
Eu consegui fazer o mysql puxar as informações em utf-8!
O problema agora está só na parte do HTML, que continua mostrando os acentos com símbolos.

No php.ini está setado default_charset = "UTF-8".

Eu inclui no index.php do meu site:

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

e



Mas ele continua mostrando tudo com símbolos.

Sabe o que pode ser?
Obrigado!

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Bernardo

Pelo visto, você já fez tudo certo. Sugiro que faça um teste: consulte um registro do seu BD que contenha algum acento. Depois, no PHP, imprima o tamanho da string. Se o número for maior que a quantidade real de símbolos, provavelmente está chegando UTF-8 mesmo (então o problema está na exibição pelo navegador). Se a quantidade for igual a de símbolos, provavelmente você está obtendo o conteúdo em ISO-8859-1.

Sugiro a leitura deste outro artigo para saber exatamente o que é o UTF-8, Unicode, ISO-8859-1, codificação de caracteres, etc:
http://rubsphp.blogspot.com.br/2010/10/unicode.html

Rubens Takiguti Ribeiro (autor do blog) disse...

Mariane, creio que este link tenha a resposta:
http://msdn.microsoft.com/en-us/library/ms191200.aspx

Você precisa criar os campos do BD com os tipos nchar, nvarchar e ntext (ao invés de char, varchar e text).

Não sei dizer se precisa de algum tratamento especial no lado do PHP, pois nunca usei o Sql Server.

Mauro Cuy disse...

Muito bom Rubens, gostaria de colocar aqui uma experiência que tive que me arrancou os cabelos.Eu trazia o conteúdo de um formulário para inserir no banco. Tudo já estava em UTF-8, á página, o Database as tabelas e os campos, e visualizava corretamente, com acento. Porém ao incluir no banco apareciam caracteres estranhos. A solução foi usar isso --->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');
Dessa forma a variável acentuada foi inserida no banco corretamente e com acento. É isso...


Rubens Takiguti Ribeiro (autor do blog) disse...

Olá Mauro,

Obrigado por compartilhar. Embora eu ache estranho não ter funcionado com o que você diz ter usado. Talvez você pode ter se enganado no momento da verificação.

A documentação do PHP diz que a forma ideal de informar o charset de comunicação com o BD (usando a biblioteca de funções do MySQL) é com a função mysql_set_charset:
http://www.php.net/manual/en/function.mysql-set-charset.php

Neste link diz que deve-se dar preferência à função ao invés de usar a query "SET NAMES".

Note que quando você vai conferir o dado no MySQL usando um cliente no terminal, também precisa informar o charset:
$ mysql --default-character-set=utf8 ...

A forma mais simples que eu vejo de detectar se o banco está armazenando utf8 ou latin1 é inserindo um campo texto com valor "ç". Depois, faço a consulta:
SELECT LENGTH(campo) FROM tabela;

Se a consulta retornar "1", significa que está em latin1 (1 byte), e se retornar "2" significa que está em utf8 (2 bytes).

Mais detalhes sobre o UTF-8 estão no link: http://rubsphp.blogspot.com/2010/10/unicode.html

Mauro Cuy disse...

Exatamente, essa diretiva --> mysql_set_charset('utf8'); substitui a query "SET NAMES" com êxito. Valeu Rubens, grande abraço...

Marco Túlio Nogueira Silva disse...

Boa tarde Rubens! Ótimo post. Mas estou com o seguinte problema. Quando salvo caracteres acentuados no banco, eles ficam salvos todo errado. E quando recupero caracteres acentuados no banco, que eu inseri diretamente pelo SGBD eles sao exibidos todos errados também. Estou utilizando o zendserver e conecto ao SQL server pelo pdo_mssql. Tenho que utilizar o ISO-8859-1. Você sabe o que pode estar acontecendo de errado?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Marco Túlio

Se você conecta diretamente ao SGBD, precisa garantir que está trafegando os dados na codificação correta. Não conheço de mssql, mas no MySQL, por exemplo, precisa ajustar o terminal para usar ISO-8859-1 e executar "SET NAMES latin1" assim que se conecta.

Anônimo disse...

Gostei muito, mas faltou mencionar o SQLite...
Parabéns pelo teor das informações

Anônimo disse...

Rubens, parabéns pelo blog, esta excelente. Referente a UTF-8 tenho uma dúvida, vejamos se consegue me ajudar, em um site que montei, os links criados, funcionam perfeitamente, porem na URL, leva por %20 ao invés do espaço, e isto não contribui para as estatísticas dos sites de pesquisa, de forma que eu iria trocar por '-', como imagino que quem esteja postando o %20, seja o CHARSET, pergunto, se você teria alguma ideia para que isto não ocorresse.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo
As URLs utilizam um outro tipo de codificação para os caracteres especiais. As funções para codificar e decodificar valores em uma url são: "urlencode" e "urldecode". Além delas, talvez seja útil você conhecer a função http_build_query. Veja este artigo para entender:
http://rubsphp.blogspot.com.br/2012/04/manipulacao-de-url-uri-e-links.html

A questão de utilização do "-" é útil se você está querendo montar URLs amigáveis. O Google recomenda o uso do "-" para separar palavras na URL, em oposição ao "_" (underscore). Veja as recomendações que ele faz neste link:
https://support.google.com/webmasters/answer/76329

Quando se trabalhar com URLs amigáveis ao invés de utilizar query string (parâmetros na URL, colocados após o símbolo "?"), é preciso tomar alguns cuidados:
* se você pretende distinguir o símbolo de hífen dos símbolos de espaço.
* se você pretende usar símbolos especiais para URLs, como "&", "?", ":", "@" e "/".

avdvdfotosefilmagens disse...

Ola Rubens excelente artigo e veja configurando o configbd ou a conexao como vc demonstra -

$dsn = "mysql:host=localhost;dbname=world;charset=utf8";
$opcoes = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'
);
$pdo = new PDO($dsn, $usuario, $senha, $opcoes);

realmente ja inseriu no banco com acento e no internet explorer ele é visualizado com acento,porém no google chrome aparecem simbolos - como resolvo isto, haja visto que a pagina que retorna dados no INTERNET EXPLORER DEMONSTRA COM ACENTUAÇÃO COMO A TABLE DO BD JA NO GOOGLE CHROME NÃO - COMO RESOLVERIA ISTO - FICO NO AGUARDO E DESDE JA AGRADEÇO

avdvdfotosefilmagens disse...

Ola Rubens excelente artigo e veja configurando o configbd ou a conexao como vc demonstra -

$dsn = "mysql:host=localhost;dbname=world;charset=utf8";
$opcoes = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'
);
$pdo = new PDO($dsn, $usuario, $senha, $opcoes);

realmente ja inseriu no banco com acento e no internet explorer ele é visualizado com acento,porém no google chrome aparecem simbolos - como resolvo isto, haja visto que a pagina que retorna dados no INTERNET EXPLORER DEMONSTRA COM ACENTUAÇÃO COMO A TABLE DO BD JA NO GOOGLE CHROME NÃO - COMO RESOLVERIA ISTO - FICO NO AGUARDO E DESDE JA AGRADEÇO

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, avdvdfotosefilmagens
Aparentemente isso que você citou é resolvido com o item 2 do artigo, ou seja, informar ao navegador que o conteúdo apresentado tem codificação UTF-8.

avdvdfotosefilmagens disse...

Olá Rubens primeiramente obrigado - bem eu já tinha feito isto - o que ocorre - O HTML e os campos da base já estão gravando acentuados veja como está setada a minha base de dados em relação ao collation e caracter set

+ Opções
Variable_name
Value
character_set_client
utf8
character_set_connection
utf8
character_set_database
utf8
character_set_filesystem
binary
character_set_results
utf8
character_set_server
latin1
character_set_system
utf8
character_sets_dir
/usr/share/mysql/charsets/


na base os campos já estão sendo salvos na minha table acentuados

mas na hora de mostrar na pagina de teste agora tb no internet explorer alem do google - aparece desta forma - a primeira linha é a do html da pagina a segunda em diante é o retorno de dados do banco de dados

no google chrome aparece desta forma

Produto, Marca (Nome do anunciante) Ação
sampa, S�o Paulo (S�o Paulo) Enviar e-mail para pgto Alterar Excluir
sampa, S�o Paulo (S�o Paulo) Enviar e-mail para pgto Alterar Excluir
sampa, S�o Paulo (S�o Paulo) Enviar e-mail para pgto Alterar Excluir
sampa, S�o Paulo (S�o Paulo) Enviar e-mail para pgto Alterar Excluir
sampa, S�o Paulo (S�o Paulo) Enviar e-mail para pgto Alterar Excluir

no internet explorer desta forma


Produto, Marca (Nome do anunciante)

Ação



sampa, S�o Paulo (S�o Paulo)






mas agora consegui resolver o problema mudando as linhas do header da pagina e no include colocando o include correto e alterando toda a base de dados para utf8-generalci

header da pagina

'SET NAMES UTF8'
);
$conn = new PDO($dsn, $usuario_banco, $senha_banco, $opcoes);

agora o novo resultado no internet explorer e google chrome

google

Produto, Marca (Nome do anunciante) Ação
sampa, São Paulo (São Paulo) Enviar e-mail para pgto Alterar Excluir
sampa, São Paulo (São Paulo) Enviar e-mail para pgto Alterar Excluir
sampa, São Paulo (São Paulo) Enviar e-mail para pgto Alterar Excluir
sampa, São Paulo (São Paulo) Enviar e-mail para pgto Alterar Excluir
sampa, São Paulo (São Paulo) Enviar e-mail para pgto Alterar Excluir

internet explorer


Produto, Marca (Nome do anunciante)

Ação



sampa, São Paulo (São Paulo)









sampa, São Paulo (São Paulo)









sampa, São Paulo (São Paulo)









sampa, São Paulo (São Paulo)









sampa, São Paulo (São Paulo)


obrigado desde ja pela ajuda e espero tb ter compartilhado esta mesma ajuda e este lance da acentuação para os colegas

detalhe - tb solicitei ao meu provedor host - mudar de latin1 para utf-8 general ci a seguinte linha do server do bd -

Variable_name
Value
character_set_client
utf8
character_set_connection
utf8
character_set_database
utf8 // esta linha antes no provedor cpanel estava como Latin1 e pos solicitacao foi para utf8
character_set_filesystem
binary
character_set_results
utf8
character_set_server
latin1
character_set_system
utf8
character_sets_dir
/usr/share/mysql/charsets/

para verificar estas linhas no SQL DA BASE NO PHPMYADMIN EXECUTAR ESTE COMANDO SQL

SHOW VARIABLES LIKE '%char%';

obrigado á todos e desde já agradeço

Rubens Takiguti Ribeiro (autor do blog) disse...

Agradeço os elogios. Aliás, Joaquim, vi seu site e achei muito bonito suas obras de arte. Também sou entusiasta de arte realista, mas fico só no desenho como hobby.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Guilherme

Creio que não exista algo tão direto pra fazer isso. Veja um tutorial que explica como fazer isso:
http://docs.moodle.org/23/en/Converting_your_MySQL_database_to_UTF8

Anônimo disse...

Legal, mas... vejam a seguinte query:

SELECT * FROM Pessoa WHERE nome LIKE '%Assunção%'

Essa query em LATIN1, ou WIN-1252, casa com qualquer nome que contenha 'Assuncao', 'Assunçao', 'Assuncão' e 'Assunção'.

Mas em UNICODE ou UTF-8 só casa com nomes que contenha exatamente o termo 'Assunção'.

E isso faz toda a diferença numa tela de busca de cliente, por exemplo. Esse é um dos motivos porquê ainda não abandonei o LATIN1 no Banco de Dados.

Se alguém souber como fazer a mesma coisa em UNICODE, me avise...

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo
Acabei de fazer um teste aqui, montando uma tabela em UTF8 e consultando um termo com acento. Funcionou perfeitamente.

Talvez você tenha feito algo errado. Experimente acessar o client de MySQL e executar este exemplo:
SET NAMES utf8;
CREATE DATABASE teste DEFAULT CHARACTER SET utf8;
USE teste;
CREATE TABLE usuarios (nome VARCHAR(128)) CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO usuarios VALUES ('assuncao'), ('assunçao'), ('assunção');
SELECT nome FROM usuarios;

Anônimo disse...

Refiz o teste, e realmente, funciona no MySQL. Mas...

Não funfou no MSSQL e no SQLite, então, testem bem no SGBD do projeto antes de implementar, pra não dar zica.

No comentário anterior, eu joguei essa questão baseado numa experiência feita com MSSQL, onde tínhamos que buscar dados em webservices UTF-8, e o banco era WIN-1252. Pensa no sofrimento para conversão de charset.

A dica é válida: SEMPRE que puderem, usem UNICODE.

R.E.C Comunicação disse...

Simplesmente incrível esse post, explicou de uma vez por todas as melhores maneiras de fazer com que o sistema não nos dê surpresas na hora de ir para o ar! Obrigado e parabéns!

Anônimo disse...

Nobre amigo:

1) Fiz exatamente o que indicou. UTF-8 para todo mundo;
2) Meu banco no MySQL estava LATIN1, aproveitei e converti para UTF-8 alterando em propriedades.
3) Percebi que os meus arquivos eram gerados no formato ANSI. Converti-os para UTF-8 também.

Um SELECT está trazendo quadradinhos em vez dos caracteres especiais ou acentuados.
Porque será?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo

Experimente inserir um registro com acento, consultá-lo e ver se é exibido corretamente. Se for, é provável que o erro foi durante a conversão do banco. Neste artigo mostra como fazer:
http://blogs.law.harvard.edu/djcp/2010/01/convert-mysql-database-from-latin1-to-utf8-the-right-way/

Anônimo disse...

Olá Rubens, gostaria de tua ajuda, se estiver ao teu alcance. Eu usava o php com iso-8859-1 e com ele gravava imagens acentuadas num servidor brasileiro e funconava tudo ok. Migrei de servidor, para um servidor inglês, onde me falaram que usam o utf-8 e o servidor deles não reconhece o iso-8859-1, as imagens não abrem mais nos sites, da erro por causa dos acentos, vou ter que enviar as milhares de imagens que tenho, tudo de novo sem acento ? ou tem alguma solução ?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo
Explique melhor essa situação. São imagens estáticas ou imagens dinâmicas geradas pelo PHP sob demanda? (utilizando a biblioteca GD ou outra do gênero)

Anônimo disse...

prezado
estou com um problema no zend. faço as consultas no banco de dados e até a classe do form tudo é mostrado ok, mas quando envio para o zend enviar para o browser, desta forma: $this->addElement('Select', 'Category',array(
'label' => 'Região de origem:',
'AutoComplete'=> true,

'MultiOptions' => $this->_paramregiao,
'required' => false ));

o multioptions quando o array tem acentos, ele não reconhece os caracteres, aparece por exemplo an&aacute,polis para simbolizar anápolis. o engraçado é que o label "Região de origem" aparece ok.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo
Creio que você já esteja passando as strings codificadas com entities HTML. Daí o zend codifica algo que já está codificado, e acaba exibindo as entities literalmente. Verifique a origem do seu array de opções e confira que lá as strings já estão codificadas.

Anônimo disse...

obrigado Rubens

Não resolveu mas o problema mudou. antes trocava os caracteres pelos nomes das entidades. eu estava codificando um json com htmlentites, realmente. tirei o htmlentities e aí fiz apenas $sRetorno = json_encode($arr). aí simplesmente trunca. por exemplo onde é Goiânia, fica Go.

Anônimo disse...

olá Rubens
na verdade resolveu. tinha outras funções antes que estavam atrapalhando. fiz a decodificação e deu certo. muito obrigado.

Anônimo disse...

Bem legal mesmo. Mas como eu faço com os javascript, tanto os que estão com os códigos na própria página como também aqueles que são chamados via src, tipo:
script src="caminho/nomearquivo.js" /script /head - Eles também precisam ser salvos como utf8?

Anônimo disse...

Até onde entendi, convém salvar o html(php), css e js com codificação UTF8 sem BOM, pois eu tinha feito tudo no Bloco de Notas e salvo em UTF8, porem o Scrubtheweb deu aviso de erro pois estava com BOM. então:
Salvar todos no Notepad++ "Codificação em UTF8 (Sem BOM)".
Fiz isso até com o htaccess, mas deu zebra. Lá tem que ser ANSI mesmo.
Por favor, se eu estiver errado me corrija.
Desde já muito obrigado e parabéns pela iniciativa.
Walter

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Walter
Creio que esteja tudo certo. De fato, o BOM é problemático, pois é considerado código indevidamente.
Quanto ao htaccess, creio que também possa ser salvo como UTF-8. Qual o problema que você teve?

Anônimo disse...

Bloqueou o site, tanto para os motores de busca como para o acesso normal. Então assim que eu percebi rapidinho voltei para ANSI. Pode ser que eu tenha errado alguma coisa.
Walter.

Athos Rafael disse...

Boa noite Rubens,
Estou com um problema desses, já fiz muitos testes e não consigo resolver.
Quando gravo uma tabela, os caracteres com acentos são gravados acrescidos daqueles caracteres "estranhos". Por exemplo, "Ações" fica gravado na tabela como "Ações".
Ao acessar a tabela para mostrar essas informações não ocorrem problemas, Os problemas ocorrem na inserção ou alteração pois, pelo fato de acrescentar caracteres, ultrapassa o tamenho definido para o campo da tabela dando erro.
Meu HTML:
!DOCTYPE html
html lang="pt-BR"
head
meta charset="utf-8"
Meu Banco de Dados:
Postgres 9.3.2
Encoding UTF8
Collation Portuguese_Brazil.1252
Character type Portuguese_Brazil.1252

Minha conexão com BD:
$this->objPDO = new \PDO($dsn, $user, $pass);
$this->objPDO->query('SET NAMES UNICODE');

Podendo me ajudar agradeço muito.
Obrigado,
Athos

Athos Rafael disse...

No meu comentário anteriror a palavra Ações que deveria aparecer com erro, do exemplo, acabou aparecendo escrita de forma correta.
Na tabela ela fica errada. Fica dessa forma, sem os espaços em branco que digitei aqui para poderem aparecer:
A & cedi l ; &o tild e ; es

Athos

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Athos Rafael
Não entendi direito. Você diz que está inserindo errado, mas por onde você viu os dados? Se foi consultando os dados e exibindo no HTML, você precisa se certificar que a página esteja sendo exibida em UTF-8 também (passo 2 do artigo).

De fato, a codificação UTF-8 exige mais bytes do que ISO-8859-1 para armazenar alguns símbolos. Para entender detalhes sobre o unicode, recomendo que veja o artigo:
http://rubsphp.blogspot.com.br/2010/10/entendendo-o-unicode-utf8.html

Para depurar, veja o tamanho do texto antes de inserí-lo no banco (use a função strlen). A palavra "Ação" precisa retornar tamanho 6 para ser UTF-8. Se retornar 4 é porque está em outra codificação. O mesmo vale na hora da consulta.

Athos Rafael disse...

Rubens,
Obrigado pelo retorno.
Vejo os dados na opção view da tabela no pgAdmin3 e também num log que gravo num arquivo txt.
Mas hoje, pela manhã, descobri o problema. Ao obter os dados do $_POST executo htmlentities.
Trata-se de um bug do PHP 5 na utilização do htmlentities. Bastou retirá-lo que funcionou.
Seguem uns links:
http://www.simplemachines.org/community/index.php?topic=497799.0
https://wiki.php.net/rfc/default_encoding
Agora o problema é como fazer para não deixar de usar o htmlentities ou atualizar a versão para o PHP 5.6 e verificar se está mesmo resolvido o bug.
Obrigado,
Athos

Anônimo disse...

Nossa mano, tentei em tudo que é site resolver um problema q eu tava tendo com charset!! Fazia tudo o que era pedido, nada resolvia!! Até que adicionei uma única linha de código, que faltava e encontrei nesse site, a utilização do mysql_set_charset('UTF8', $conexao); e aí funcionou! Já tava perdendo os cabelos cara, muito obrigadoooo mesmooooo!!!!!!!!

A Torres disse...

Como declarar o Charset no CSS ?

tentando mudar

.tovar_sale {position:relative;}
.tovar_sale:before {
content:'Sale';

para

.tovar_sale {position:relative;}
.tovar_sale:before {
content:'PROMOÇÃO'; Quebra a codificaçao css e o layout do site

Anônimo disse...

Bom dia Rubens,

Fazem dias que estou tentando resolver esse problema de caracteres. Tenho um form no site que chega perfeito, mas o do contato que é básico não. Preciso de ajuda... fazem 3 dias que estou em cima disso somente...

O código é o send-email.php que está abaixo. No html está contato.html está tudo certo também, com o
meta charset="utf-8" no cabeçalho.

Ver codigo:

http://pastebin.com/DwL064DX

O que está errado?

Preciso fazer um .htacess? O que está invalidando o envio deste form codificado em UTF8?

Desde já agradeço muito,
Marcos

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Marcos
Segue o link com algumas correções que encontrei:
http://pastebin.com/XhyrFRpp

Os erros que vi foram estes:
* A expressão regular precisa do modificador "u" para tratar caracteres em UTF-8 (linha 34);
* O assunto do e-mail precisa ser codificado de acordo com a RFC 2047 http://www.faqs.org/rfcs/rfc2047.html (linha 89);
* O tipo de conteúdo enviado no e-mail precisa ser especificado, juntamente com seu charset (linha 90);
* As diretivas "From" e "Reply-To" são do protocolo HTTP e não podem ser traduzidas (linhas 91 e 92).

O ideal é utilizar alguma classe para abstrair o processo de envio de e-mail, pois existem vários detalhes para serem tratados. Uma sugestão: use o phpmailer.

Anônimo disse...

Rubens....
Não sei como lhe agradecer.
Seus conhecimentos foram um bálsamo e tudo simplesmente funciona.
Realmente, estou profundamente agradecido, e inspirado, por ter recebido uma ajuda tão fundamental de alguém que não conheço pessoalmente.
MUITO OBRIGADO!
Abs!

Karl - Senpai disse...

Eu estou com um problema parecido, mais no meu caso eu preciso inverte!!
Eu preciso que o texto apareça assim: Hércules
E esta aparecendo assim: Hércules
Alguem sabe me ajudar?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Karl

Em que situação você precisa exibir o texto desta forma?

Para fazer isso numa página é relativamente simples: você salva um arquivo HTML com o texto "Hércules" e salva em UTF-8, mas indica que a página é ISO-8859-1. Veja o 2º item do artigo, mas troque a codificação por ISO-8859-1.

Para mais detalhes, recomendo que leia este artigo:
http://rubsphp.blogspot.com.br/2010/10/entendendo-o-unicode-utf8.html

José Marcos disse...

Olá Rubens. Obrigado por compartilhar conosco seu conhecimento, porém atualmente aqui na empresa estamos passando por um obstáculo do qual não estamos conseguindo uma solução.

Estamos com um projeto do qual já constava em uma determinada tabela dados cadastrados em utf-8 e iso-8859-1. Quando aparece no site, os caracteres ficam dessa forma Can��o ou Canção. Já usamos o utf8_encode() e o utf8_decode() do PHP, porém algumas palavras tem solução e outras não.

Já usamos também a própria codificação do HTML, mas nada resolveu. Pensamos em fazer manualmente, porém só nessa tabela, tem mais de 30 mil registros.

Como poderíamos agir nesse caso?

Desde já agradecemos muito pela ajuda.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, José Marcos
Veja este artigo:
http://rubsphp.blogspot.com.br/2012/08/convertendo-iso88591-para-utf8-de-forma-segura.html

Lá eu coloquei uma função que converte ISO-8859-1 para UTF-8, mas o que já está em UTF-8 é mantido. Ela será útil se um mesmo texto tiver caracteres ISO-8859-1 e UTF-8.

Caso existam alguns registros em ISO e outros em UTF-8, você pode converter os registros que são ISO-8859-1 para UTF-8 checando primeiro se ele não é UTF-8, desta forma:
if (!preg_match('/./u', $texto)) {
$texto = utf8_encode($texto);
}
E então salvando o texto final no BD, para ficar tudo UTF-8.

José Marcos disse...

Bom dia Rubens. Estaremos implantando a sua função em nosso projeto e colocando os créditos para o seu blog.

Muito obrigado pela ajuda.

Um abraço.

Anônimo disse...

Parabéns pelo seu blog, resolveu um problema que estava tendo com PHP, continue assim..

CIPRIANO disse...

Muito obrigado meu amigo. Perfeita sua explicação. Agora entendi. No meu caso solucionou apenas modificando o arquivo de conexão com o banco.
Sucesso!!!

Luiz Neto disse...

Preciso de sua dica...Instalei um script no hostinger e estou lutando para corrigir a codificação ...http://www.simuladodeconcurso.com.br/simulado
quando importo o BD e visualizo no phpMyadmin está tudo correto , na colação ele vem original como latin1, alterei para utf8 e mesmo assim continua com problema na acentuação ...como devo proceder??? Obrigado.

Anônimo disse...

Notei que na página
http://www.simuladodeconcurso.com.br/simulado
que você citou, está codificado utf8 mas você fêz algumas codificações de caracteres em algumas palavras.
Eu acho que uma vez que você codificou utf8 não precisa codificar mais nada. Use a acentuação normal do teclado e seja feliz.

Spencer Martin Camaño Ekroth disse...

Eu uso php mysql e tentando usar uma lib criada para exibir os registro me deparei com esse problema, após tentar resolve-lo de todas as formas propostas, sem exito para essa lib, voltei a minha forma de trabalhar com isso! somente para complementar o post do seu blog que foi muito útil (li todos os comentário na esperança de achar o que precisava):
-->>

$link = mysql_connect("meuservidor", "meuusuario", "minhasenha");
mysql_select_db(bd", $link);
$result = mysql_query("SELECT * FROM tabela", $link);
$num_rows = mysql_num_rows($result);
echo "Exibindo $num_rows Registro(s)\n";
echo "Profissionais em Atendimento:"; ?>
echo "br>"; ?>
$con = mysql_connect('meuservidor', 'meuusuario', 'minhasenha') or
die('Não foi possível conectar');
@mysql_query("SET CHARACTER SET 'utf8'", $con);
mysql_select_db("bd", $con);
$result = mysql_query('SELECT * FROM tabela');
while($row = mysql_fetch_array($result)){
echo 't border="0">';echo 'tr>';echo 'td>';
echo $row['campo1'];
echo '  -  ';
echo $row['campo2'];
echo '  -  ';
echo $row['campo3'];
echo '  -  ';
echo $row['data'];
echo "br />";


echo 'td>';echo 'tr>';echo 'tag';echo 'r>r>';
}
mysql_close($con);

com isso tenho o número de registros gravados na tabela e os dados aparecem corretamente na página.

Exibindo 11 Registro(s)
...manipulação de fármacos, produção. etc.

o outro código é esse: (não funcionou)
xmlns="http://www.w3.org/1999/xhtml" lang="pt-br" xml:lang="pt-br"

header('SET NAMES UTF8');
echo 'tml>';
//eta http-equiv="Content-Type" content="text/html; charset=utf-8" />
//echo 'tml>';
// seleciona o grupo de caracteres utilizados na pagina
ini_set('default_charset','utf8');
require ("../Process.php");
$p = new Process();
require ("../T ab le.php");
$table = new Table ( $p-> RecoverHeader("Select * tabela") ,$p-> RecoverInfos("Select * from tabela"));
@mysql_query("SET CHARACTER SET 'utf8'", $this);
$ta ble ->setLink("favicon.ico");
$ta ble ->mount();
// seu código que usei para ver se funcionava
function sanitizar_utf8($texto) {
$saida = '';

$i = 0;
... }
}
return $saida;
}..
link rel = "stylesheet" href="../estilos/GraphicsStyles.css" >
mas aqui o resultado ainda é esse: "produ��o"
se puder ajudar ou precisar de mais detalhes do projeto é só falar, abraços

Rubens Takiguti Ribeiro (autor do blog) disse...

Vinícius, depende. Cada software pode optar por ter alguma codificação padrão ou nem mesmo aceitar codificações diversas (aceitando apenas alguma específica). Porém, UTF-8 tem se tornado a mais comum para ser a padrão.

Anônimo disse...

Estava com problema do Json retornando null com caracteres especiais e seu exemplo MySQL (PDO) funcionou perfeito. Muito bom

Fernando disse...

Fiz questão de comentar KKK TOP! muito bom! eu já estava aqui impaciente e já tinha adicionado UTF-8 no cabeçalho mas ainda não tinha definido na classe de conexão com o banco...
ótimo!

Anônimo disse...

Boa tarde! Amigo poderia me dar uma ajuda urgente? Tenho uma loja virtual de oscommerce e o db está todo em utf-8, o site todo em utf-8. Mas quando adiciono os nomes às categorias, ele gera o link pras categorias com acentuação, o que, claro, faz dar errado no browser. Como transformar as palavras em palavras sem acento para os links? Obrigada, desculpe o incômodo! E parabésn pelas ótimas matérias! Lucy

Rubens Takiguti Ribeiro (autor do blog) disse...

Oi, Lucy
Em teoria, as URIs são UTF-8, portanto não é problema a URL ter acentos.
Em todo caso, dê uma olhada na função iconv:

setlocale(LC_CTYPE, 'pt_BR.UTF8');
$texto = iconv('UTF-8', 'ASCII//TRANSLIT', 'texto com áçêñtõ');
var_dump($texto);

Anônimo disse...

Vou fazer os testes aqui, muito obrigada pela ajuda! Abraço! ~Lucy

Cpd Salbego disse...

Opa!
Depois de quebrar a cabeça, consegui corrigir a bagaça!
Já havia feito tudo que tem na net: banco UTF-8 general-ci, arquivo com formato utf8, colocar no header o , colocar aqueles baratos de mysql_query("SET NAMES 'utf8'")...
e mesmo assim nada!
então li teu post e adicionei à minha conexão a expressão: $this->mysqli->set_charset('utf8');
BINGO!!!
valew!!

Ana Gauna disse...

Tive esse problema desde a semana passada, consegui resolver criando um arquivo com o nome de paginacao.cgi contendo o seguinte, e os acentos funcionaram.
#!/bin/sh
Content-type: text/html
Language: pt-br
Charset: UTF-8-pt-br