Definimos encoding como uma técnica que define as regras para armazenar os códigos dos caracteres em memória (esses códigos são denominados code points no jargão Unicode). Na prática, existem vários encodings diferentes: UTF-8, ISO-8859-1 (apelidado de Latin-1), UCS-2, Windows-1252, etc. Cada um deles utiliza um algoritmo distinto para fazer a representação dos códigos Unicode.

UTF-8 diferencia-se da maioria dos outros encodings pelo fato de ser completo. Isto é: ele é capaz de armazenar qualquer caractere Unicode. Outros encodings, como ISO-8859-1 e Windows-1252, preferem trabalhar apenas com um subconjunto da tabela Unicode (ex: apenas com caracteres das linguagens ocidentais). A grande maioria dos softwares comerciais e open source (SGBD’s, por exemplo) prefere trabalhar internamente com o formato UTF-8, pois este encoding favorece a questão da internacionalização do produto. Pelo mesmo motivo, as linguagens padrão para troca de informações XML e JSON normalmente trabalham com a codificação UTF-8. Aliás, já existem vários arquivos e bases de dados de domínio público codificadas em UTF-8 na Internet.

Sendo assim, mais cedo ou mais tarde chegará a hora em que precisaremos trabalhar com arquivos UTF-8 (não há como fugir!!!). Neste artigo apresentaremos uma receita segura para trabalhar com UTF-8 na linguagem Java, tanto na leitura como na gravação de arquivos.

Java x UTF-8: O Problema

Na linguagem Java, as classes mais utilizadas para leitura e gravação de arquivos são java.io.FileReader e java.io.FileWriter, respectivamente. Essa situação é bastante justificável, uma vez que trabalhar com essas classes é muito simples na prática. No entanto, existe um problema importante associado a estas classes que precisa ser conhecido por todo desenvolvedor Java. O que ocorre é que, por default, ambas não usam UTF-8. Mas que problema isso pode causar? Muitos! Basicamente, se você tiver que trabalhar com um arquivo texto UTF-8 que possuir qualquer caractere que não seja padrão ASCII (ex: letras acentuadas, cedilha, etc.), o caractere em questão não será lido em nem gravado corretamente.

Demonstraremos isso com um exemplo. Considere o arquivo texto “poema.txt”, apresentado na Figura 1. Este arquivo foi criado utilizando a codificação UTF-8 (veja o círculo em vermelho na figura).

Arquivo texto com encoding UTF-8
Figura 1. Arquivo texto com encoding UTF-8

O exemplo da Listagem 1 mostra um pequeno programa onde a classe “FileReader” é usada para realizar a leitura do arquivo texto.


/**
 * Esta classe lê o arquivo "poema.txt" usando a classe java.io.FileReader. 
 * O programa não trabalha com UTF-8 de forma correta
 *
 */

import java.io.BufferedReader;
import java.io.FileReader;

public class LeituraFileReader {

	public static void main(String[] args) throws Exception {

		// abertura do arquivo
		FileReader arq = new FileReader("c:\\temp\\poema.txt");
		BufferedReader myBuffer = new BufferedReader(arq);

		// loop que lê e imprime todas as linhas do arquivo
		String linha = myBuffer.readLine();

		while (linha != null) {
			System.out.println(linha);
			linha = myBuffer.readLine();
		}

		arq.close();

	}

} 
Listagem 1. Programa que não funciona com arquivos UTF-8

Infelizmente, ao executar o programa, o resultado é o mostrado na Figura 2. Veja que todos os caracteres acentuados foram trocados por símbolos “malucos”.

Leitura com problema – UTF-8 não é processado corretamente
Figura 2. Leitura com problema – UTF-8 não é processado corretamente

Mas por que o problema ocorreu? A resposta pode ser encontrada na própria Java API:

“public class FileReader
Convenience class for reading character files. The constructors of this class assume that the default character encoding and the default byte-buffer size are appropriate. To specify these values yourself, construct an InputStreamReader on a FileInputStream.”

Em resumo: a classe assume que o arquivo texto a ser lido possui o encoding default do computador em que o programa está sendo executado. Para saber qual é a codificação, use o comando abaixo (no caso deste exemplo, o resultado exibido foi “cp1252”):

System.out.println(System.getProperty("file.encoding"));

Java x UTF-8: A Solução

Felizmente, é possível contornar o problema de maneira simples. A solução está descrita no texto da JAVA API: “para especificar o encoding construa um InputStreamReader em um FileInputStream”. O quê??? Bem, não se preocupe, pois a tradução dessa frase para o “computês”, ou seja, para uma linha de código Java é mostrada na Listagem 2 (obs.: substitua “path” pelo caminho do seu arquivo).


BufferedReader arqIn = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));
Listagem 2. Abrir arquivo UTF-8 para leitura

Agora vamos mostrar como seguir essa receita para fazer a leitura de um arquivo UTF-8. Na Listagem 3, apresentamos o programa corrigido para ler o arquivo “poema.txt”.


/**
 * Esta classe lê o arquivo "poema.txt" considerando o encoding UTF-8
 *
 */
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class LeituraUTF8 {

	public static void main(String[] args) throws Exception {

		// abertura do arquivo
		BufferedReader myBuffer = new BufferedReader(new InputStreamReader(
		  new FileInputStream("c:\\temp\\poema.txt"), "UTF-8"));
		
		// loop que lê e imprime todas as linhas do arquivo
		String linha = myBuffer.readLine();

		while (linha != null) {
			System.out.println(linha);
			linha = myBuffer.readLine();
		}

		myBuffer.close();

	}
	
}
Listagem 3. Programa que lê arquivo UTF-8

Dessa vez, os caracteres acentuados serão lidos normalmente, como mostra a Figura 3.

Arquivo UTF-8 é processado corretamente
Figura 3. Arquivo UTF-8 é processado corretamente

Escrita de Arquivos

Para encerrar nosso artigo, abordaremos a questão da escrita de arquivos UTF-8. Uma possível solução é apresentada na Listagem 4. Mais uma vez, seguiremos o conselho dado pela Java API:

“public class FileReader
Convenience class for writing character files. The constructors of this class assume that the default character encoding and the default byte-buffer size are acceptable. To specify these values yourself, construct an OutputStreamWriter on a FileOutputStream.”

Então está bem, para criar um arquivo com encoding UTF-8 vamos construir um “OutputStreamWriter” em um “FileOutputStream”. O exemplo é apresentado na Listagem 4, onde o programa produz um arquivo com a codificação UTF-8 chamado “acentos.txt” e contendo três linhas.


/** Esta classe salva um arquivo com encoding UTF-8 *
 */

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class GravacaoUTF8 {

	public static void main(String[] args) throws Exception {
		
		OutputStreamWriter bufferOut = new OutputStreamWriter(
                               new FileOutputStream("c:\\temp\\acentos.txt"),"UTF-8");
				
		bufferOut.write("acento agudo: áéíóú\n");
		bufferOut.write("acento circunflexo: âêîôû\n");
		bufferOut.write("fim");
		
		bufferOut.close();
	}
	
}
Listagem 4. Programa que grava arquivo UTF-8

Este artigo apresentou a receita básica para trabalhar com UTF-8 em Java. Se desejar conhecer um pouquinho mais sobre Unicode e UTF-8, temos um artigo que pode ajudar.