CodeGym /Cursos /JAVA 25 SELF /Codificações principais: UTF-8, UTF-16, ISO-8859-1

Codificações principais: UTF-8, UTF-16, ISO-8859-1

JAVA 25 SELF
Nível 37 , Lição 1
Disponível

1. UTF-8 — o rei das codificações modernas

No trabalho de programador, você vai lidar com arquivos, protocolos de rede, bancos de dados, serviços web, onde são usadas codificações diferentes. Se você não sabe em que UTF-8 difere de ISO-8859-1, pode cair numa armadilha: seu código vai funcionar “na minha máquina”, mas quebrará no colega da França ou na nuvem, onde o sistema padrão é outro.

Hoje vamos analisar três codificações principais que aparecem na vida real de um desenvolvedor Java:

  • UTF-8 — padrão universal moderno.
  • UTF-16 — formato interno das strings no Java e muito comum no Windows.
  • ISO-8859-1 (Latin-1) — codificação de um byte mais antiga, mas ainda encontrada para idiomas da Europa Ocidental.

Vamos conhecê-las mais de perto!

O que é?

UTF-8 (Unicode Transformation Format, 8-bit) é uma codificação capaz de representar qualquer caractere da enorme tabela Unicode (ou seja, inclui cirílico, ideogramas chineses, emojis e até sistemas de escrita antigos raros). E faz isso de maneira esperta: para os caracteres mais populares (letras inglesas, dígitos, sinais de pontuação) usa apenas 1 byte. Para os demais — 2, 3 ou até 4 bytes.

Fato: Todos os sites modernos, a maioria dos arquivos JSON e XML, e até os fontes em Java, por padrão, usam UTF-8.

Quantos bytes são necessários para um caractere?

  • Caracteres ASCII (letras inglesas, dígitos, pontuação) — 1 byte.
  • Caracteres cirílicos e europeus com diacríticos2 bytes.
  • Ideogramas chineses, japoneses e coreanos3 bytes.
  • Super exóticos, emojis4 bytes.

Exemplo:

Símbolo Código no Unicode UTF-8 (em bytes)
A
U+0041
41
Ya
U+042F
D0 AF
U+20AC
E2 82 AC
😀
U+1F600
F0 9F 98 80

Por que o UTF-8 é tão bom?

  • Retrocompatível com ASCII: se o arquivo contiver apenas caracteres ingleses, ele será exatamente igual ao ASCII.
  • Compacidade: para textos curtos em inglês, o arquivo em UTF-8 terá o menor tamanho.
  • Abrangência internacional: suporta todos os idiomas do mundo, incluindo emojis.
  • Padrão para a web: HTML, CSS, JSON, XML, JavaScript — tudo isso é UTF-8 por padrão.

Exemplo de uso em Java

import java.nio.charset.StandardCharsets;
import java.nio.file.*;

public class Utf8Demo {
    public static void main(String[] args) throws Exception {
        // Gravação de string em arquivo em UTF-8
        String text = "Olá, mundo! 😀";
        Files.write(Paths.get("utf8.txt"), text.getBytes(StandardCharsets.UTF_8));

        // Leitura de arquivo em UTF-8
        String read = Files.readString(Paths.get("utf8.txt"), StandardCharsets.UTF_8);
        System.out.println(read); // Vai imprimir: Olá, mundo! 😀
    }
}

Aqui primeiro codificamos explicitamente a string em UTF-8 antes de gravar e, depois, indicamos explicitamente a codificação na leitura. Graças a isso, o texto é preservado e restaurado sem distorções, mesmo que contenha emojis ou caracteres de vários idiomas.

Quando usar UTF-8?

Sempre, a menos que você tenha um motivo muito forte para escolher outra coisa. É a opção universal para todos os aplicativos modernos.

2. UTF-16 — o formato interno do Java

UTF-16 é outra codificação da família Unicode. Diferente do UTF-8, ela geralmente usa 2 bytes por caractere. É assim que o Java armazena strings (String) e caracteres (char) dentro da JVM. Mas há um detalhe: alguns caracteres (por exemplo, ideogramas raros ou emojis) exigem 4 bytes (dois “pares substitutos”).

Onde o UTF-16 é usado?

  • Dentro do Java: todas as strings e caracteres são UTF-16.
  • Windows: muitos arquivos do sistema no Windows usam UTF-16 (por exemplo, o Bloco de Notas salva em UTF-16 LE por padrão).
  • Alguns protocolos e formatos: por exemplo, XML pode estar em UTF-16.

Exemplo: como um caractere fica em UTF-16

Símbolo Código no Unicode UTF-16 (bytes) Comentário
A
U+0041
00 41
2 bytes
Ya
U+042F
04 2F
2 bytes
U+20AC
20 AC
2 bytes
😀
U+1F600
D8 3D DE 00
4 bytes (par substituto)

Exemplo de uso em Java

import java.nio.charset.StandardCharsets;
import java.nio.file.*;

public class Utf16Demo {
    public static void main(String[] args) throws Exception {
        String text = "Olá, mundo! 😀";

        // Gravação da string em arquivo usando a codificação UTF-16
        Files.write(Paths.get("utf16.txt"), text.getBytes(StandardCharsets.UTF_16));

        // Leitura do conteúdo do arquivo informando a codificação UTF-16
        String read = Files.readString(Paths.get("utf16.txt"), StandardCharsets.UTF_16);
        System.out.println(read); // Saída correta da string original
    }
}

Ponto-chave: na gravação e na leitura, é preciso informar a mesma codificação. Se você gravar em UTF-16 e ler no padrão (por exemplo, em UTF-8), surgirá uma “bagunça” de caracteres.

Particularidades do UTF-16

  • Comprimento fixo para a maioria dos caracteres: 2 bytes.
  • Pares substitutos: parte dos caracteres (por exemplo, emojis) requer 4 bytes.
  • Byte Order Mark (BOM): no início do arquivo, muitas vezes, é adicionado um “cabeçalho” especial indicando a ordem dos bytes (LE — little endian, BE — big endian). O Java geralmente entende o BOM automaticamente.
  • Não é compatível com ASCII: se você abrir um arquivo UTF-16 em um editor que espera ASCII ou UTF-8, verá muitos zeros e símbolos estranhos.

Quando usar UTF-16?

  • Se você trabalha com sistemas ou arquivos muito específicos que exigem UTF-16 (por exemplo, integração com programas do Windows).
  • Para aplicativos e arquivos comuns — prefira UTF-8.

4. ISO-8859-1 (Latin-1) — um aceno do passado

ISO-8859-1, ou Latin-1, é uma codificação de um byte criada lá na década de 1980. Ela suporta 256 caracteres: alfabeto inglês, letras da Europa Ocidental com diacríticos (é, ü, ç etc.), pontuação e símbolos especiais.

Fato: Essa codificação não possui cirílico, grego, árabe, chinês e outros caracteres não latinos.

Onde ISO-8859-1 aparece?

  • Em arquivos e programas antigos (especialmente na Europa).
  • Em alguns bancos de dados e protocolos, como “padrão”.
  • Em cabeçalhos HTTP (pelo padrão, dados de texto sem indicação de codificação são considerados ISO-8859-1, embora na prática isso seja raro).

Exemplo: como um caractere fica em ISO-8859-1

Símbolo Código no Unicode ISO-8859-1 (byte) Comentário
A
U+0041
41
Igual ao ASCII
é
U+00E9
E9
Francês
ü
U+00FC
FC
Alemão
Ya
U+042F
Esse caractere não existe!

Exemplo de uso em Java

import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.charset.Charset;

public class Latin1Demo {
    public static void main(String[] args) throws Exception {
        String text = "Bonjour, ça va? Café!"; // Texto em francês
        Files.write(Paths.get("latin1.txt"), text.getBytes(StandardCharsets.ISO_8859_1));

        // Leitura do arquivo em ISO-8859-1
        String read = Files.readString(Paths.get("latin1.txt"), StandardCharsets.ISO_8859_1);
        System.out.println(read); // Tudo é lido corretamente!

        // Vamos tentar gravar cirílico
        String cyrillic = "Olá, mundo!";
        try {
            Files.write(Paths.get("badlatin1.txt"), cyrillic.getBytes(StandardCharsets.ISO_8859_1));
        } catch (Exception e) {
            System.out.println("Erro: não é possível gravar cirílico em ISO-8859-1!");
        }
    }
}

A codificação ISO-8859-1 (Latin-1) suporta apenas caracteres da Europa Ocidental — por exemplo, letras com acento do francês, alemão ou espanhol. Por isso, texto em francês é gravado e lido sem problemas.

Entretanto, o cirílico não existe nessa codificação e, ao tentar gravá-lo, ocorre erro ou perda de caracteres. Por isso, para textos multilíngues, é melhor usar UTF-8 ou UTF-16, que cobrem um conjunto muito maior de caracteres.

Particularidades do ISO-8859-1

  • Limitação por idiomas: apenas idiomas da Europa Ocidental.
  • 1 byte por caractere: compacto, porém muito limitado.
  • Gravar “caracteres de fora” causa distorção: se você tentar salvar cirílico ou ideogramas, eles virarão pontos de interrogação ou lixo.

Quando usar ISO-8859-1?

  • Apenas se você estiver integrando com um sistema muito antigo ou um banco de dados que exija exatamente essa codificação.
  • Para tarefas modernas — não é recomendado.

5. Nuances úteis

Tabela comparativa de codificações

Codificação Bytes por caractere Quais idiomas Compatibilidade com ASCII Onde é usada
UTF-8
1-4 Todos Total Web, Java, JSON, XML, Linux
UTF-16
2 ou 4 Todos Não Dentro do Java, Windows, XML
ISO-8859-1
1 Europa Ocidental Total Programas antigos, bancos de dados, HTTP

Como escolher a codificação?

  • UTF-8 — sua escolha para 99%. Compatível com ASCII, suporta todos os idiomas, menor tamanho para texto em inglês, padrão para a web e para Java.
  • UTF-16 — use apenas se a especificação exigir ou se houver integração com programas do Windows.
  • ISO-8859-1 — apenas para compatibilidade retroativa com sistemas antigos. Nunca use para armazenar cirílico, grego, árabe etc.

Como saber em qual codificação está um arquivo?

  • Abra o arquivo em um editor que mostre a codificação (por exemplo, Notepad++, VS Code).
  • Se vir “caracteres estranhos”, tente abrir o arquivo em outra codificação.
  • No terminal Linux você pode usar o comando file nome_do_arquivo — às vezes ele indica a codificação.

Como definir a codificação no Java?

Use as classes do pacote java.nio.charset:

  • StandardCharsets.UTF_8
  • StandardCharsets.UTF_16
  • StandardCharsets.ISO_8859_1

Ou pelo nome da codificação:

Charset windows1251 = Charset.forName("Windows-1251");
Charset utf8 = Charset.forName("UTF-8");

6. Prática: o que acontece se você confundir a codificação?

Vamos fazer um pequeno experimento. Vamos gravar uma string em UTF-8 e depois tentar lê-la como ISO-8859-1:

import java.nio.file.*;
import java.nio.charset.*;

public class EncodingMismatchDemo {
    public static void main(String[] args) throws Exception {
        String text = "Olá, mundo!"; // Acentos

        // Gravamos como UTF-8
        Files.write(Paths.get("utf8demo.txt"), text.getBytes(StandardCharsets.UTF_8));

        // Lemos como ISO-8859-1 (INCORRETO!)
        String wrong = Files.readString(Paths.get("utf8demo.txt"), StandardCharsets.ISO_8859_1);
        System.out.println("Leitura incorreta: " + wrong);

        // Agora lemos corretamente
        String correct = Files.readString(Paths.get("utf8demo.txt"), StandardCharsets.UTF_8);
        System.out.println("Leitura correta: " + correct);
    }
}

Resultado:

  • No primeiro caso, você verá “caracteres estranhos” — um conjunto de símbolos esquisitos.
  • No segundo caso — texto correto.

7. Erros típicos ao trabalhar com codificações

Erro №1: Ler o arquivo na codificação errada. Se o arquivo foi gravado em UTF-8 e você o lê como ISO-8859-1 ou Windows-1251, todos os caracteres não ASCII virarão lixo. É o clássico: “na minha máquina funciona, no colega não”.

Erro №2: Tentar gravar “caracteres de fora” em uma codificação de 1 byte. Se você tentar salvar cirílico ou ideogramas em ISO-8859-1, o Java os substituirá por pontos de interrogação ou lançará erro. Os dados serão perdidos irreversivelmente.

Erro №3: Uso da codificação do sistema “padrão”. Em alguns métodos do Java (por exemplo, new FileReader("file.txt")), a codificação não é informada explicitamente — usa-se a do sistema. Em um computador pode ser UTF-8, em outro — Windows-1251, em um terceiro — algo exótico. Portanto, sempre use métodos em que seja possível informar a codificação explicitamente.

Erro №4: Abrir um arquivo UTF-16 como UTF-8. Um arquivo gravado em UTF-16, se aberto como UTF-8, conterá muitos zeros e símbolos estranhos, pois os bytes serão interpretados de forma incorreta.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION