Codificação de arquivos e UTF-8 como conjunto de caracteres padrão

A partir do JDK 18, UTF-8 é o conjunto de caracteres padrão em todos os sistemas operacionais.

JEP 400 - UTF-8 por padrão

A Java Platform Enhancement Proposal (JEP) 400 introduziu UTF-8 como o conjunto de caracteres padrão para APIs Java padrão em todos os sistemas operacionais, a partir do Java 18, exceto para a codificação de entrada e saída do console.

Em IBMSemeru Runtime Certified Edition for z/OS 21 ( Semeru 21), UTF-8 é o valor padrão da file.encoding propriedade e o conjunto de caracteres retornado pelo método java.nio.charsets.Charset.defaultCharset(). As versões anteriores usavam como padrão um conjunto de caracteres EBCDIC que dependia das configurações de localidade.

Essa alteração afeta várias APIs do Java SE, que usam o conjunto de caracteres padrão, como
  • aPIs InputStreamReader, ' FileReader, ' OutputStreamWriter, ' FileWriter e ' PrintStream do pacote java.io e
  • aPIs URLEncoder e ' URLDecoder do pacote java.net.
O padrão UTF-8 afeta tanto a interpretação da codificação dos arquivos de entrada quanto a codificação dos arquivos de saída. A propriedade " file.encoding influencia esse comportamento.

A propriedade ' file.encoding

A propriedade " file.encoding pode ser usada para influenciar o comportamento padrão do charset. Especificamente, foi introduzido um novo valor de propriedade COMPAT, que reverte o comportamento do conjunto de caracteres padrão para o do Java 17 e versões anteriores. Como alternativa, um conjunto de caracteres específico pode ser especificado para a propriedade usando a linha de comando, se necessário. O exemplo a seguir ilustra o uso da propriedade.

# The COMPAT value reverts the default charset selection to Java 17 behaviour, 
and will select a native EBCDIC encoding dependent on locale settings.
java -Dfile.encoding=COMPAT MyApplication    

# This specifies file.encoding to be in IBM-1047 charset.
java -Dfile.encoding=IBM-1047 MyApplication
Observação: a configuração da propriedade do sistema ' file.encoding usando a opção java -D na linha de comando não é compatível com o JEP. IBM oferece suporte a esse comportamento no z/OS. A configuração desta propriedade deve ser feita antes da inicialização do JVM.
Comportamento UTF-8 padrão e conversão automática de arquivos de entrada
Se nenhum valor específico da propriedade ' file.encoding for definido, o conjunto de caracteres padrão será UTF-8. A compatibilidade aprimorada do Semeru 21 com UTF-8 sobre EBCDIC em z/OS sistemas é obtida através da implementação de um recurso de conversão automática de conjunto de caracteres que converte arquivos EBCDIC para UTF-8. Este recurso utiliza o suporte à z/OS marcação de arquivos, usando o chtag utilitário, para identificar o conjunto de caracteres do texto dentro do arquivo. Quando ativado, o conteúdo de texto do arquivo marcado é automaticamente convertido nas operações de leitura e apresentado como UTF-8 caracteres para o aplicativo Java, sujeito às limitações documentadas na seção seguinte. Apenas conversões para UTF-8 o conjunto de caracteres são suportadas. Somente arquivos com ' txtflag=ON (definido com o uso de ' chtag -t) e um código de conjunto de caracteres válido são considerados candidatos à conversão automática. O atributo " txtflag indica se o arquivo contém dados de texto codificados uniformemente. Arquivos de texto e arquivos binários não marcados não são convertidos.

O trecho de código a seguir mostra como marcar arquivos no z/OS com o utilitário ' chtag.


chtag -t -c IBM-1047 EBCDIC-1047.txt          # Tag as text in IBM-1047 encoding
chtag -t -c ISO8859-1 ASCII-ISO8859-1.txt     # Tag as text in ISO8859-1 encoding
chtag -t -c UTF-8 UTF-8.txt                   # Tag as text in UTF-8 encoding

Esse recurso de conversão automática é controlado por meio de uma nova propriedade ' com.ibm.autocvt. O valor padrão da propriedade " com.ibm.autocvt é " true somente se a propriedade " file.encoding não estiver definida. Se um charset ou COMPAT específico for especificado para a propriedade ' file.encoding, a conversão automática será desativada por padrão.

Observação: a propriedade ' com.ibm.autocvt é totalmente independente do suporte a ASCII aprimorado z/OS e não interage com nenhuma configuração _BPXK_AUTOCVT.

A tabela a seguir resume as interações entre a propriedade " file.encoding e a conversão automática.

Tabela 1. Interações entre a propriedade ' file.encoding e a conversão automática.
valor file.encoding Configuração de conversão automáticacom.ibm.autocvt) Comportamento
Desconfigurado Ativado Comportamento padrão que converte automaticamente arquivos marcados para UTF-8. Presume-se que os arquivos de texto não marcados estejam em UTF-8.
file.encoding=COMPAT Desativado COMPAT reverte para o comportamento do Java 17, ' file.encoding tem como padrão a codificação EBCDIC nativa dependente da localidade, a conversão automática está desativada.
file.encoding=UTF-8 Desativado (pode ser ativado opcionalmente) Essa configuração impõe que ' file.encoding seja UTF-8. A conversão automática é desativada para preservar a compatibilidade com o JDK17 quando ' file.encoding=UTF-8 é definido. Se a conversão automática for necessária, ela poderá ser definida usando ' -Dcom.ibm.autocvt=true
file.encoding=<non UTF-8 charset> Desativado Essa configuração impõe o ' file.encoding ao conjunto de caracteres especificado. A conversão automática está desativada (e não pode ser ativada definindo a propriedade ' com.ibm.autocvt como verdadeira) nesse cenário. O comportamento é compatível com o JDK17 com a mesma configuração ' file.encoding aplicada.
Diagnóstico para conversão automática
Foi adicionado um registrador que monitora as atividades relacionadas à conversão automática. O registrador imprime informações, avisos ou erros encontrados durante o processo de conversão automática em um fluxo de erro padrão. O método e a ID do thread, juntamente com o tipo de registro e o registro real, são impressos. O recurso de registro de conversão automática é controlado por meio de uma nova propriedade com.ibm.autocvt.trace.

O registro de conversão automática é desativado por padrão. Para ativá-lo, defina a propriedade com.ibm.autocvt.trace como true ao iniciar o aplicativo Java usando o comando java , conforme mostrado no exemplo a seguir.

java -Dcom.ibm.autocvt.trace=true MyApplication

Um exemplo de registros de informações de conversão automática é fornecido aqui.

[AUTOCVT.INFO] [ThreadID:2] [jdk.internal.util.AutoConversion.<clinit>] Autoconversion is true
[AUTOCVT.INFO] [ThreadID:2] [jdk.internal.util.AutoConversion.enable] Enabling auto-conversion for: myFile.txt

Um exemplo de logs de aviso de conversão automática é fornecido aqui.

[AUTOCVT.WARNING] [ThreadID:2] [jdk.internal.util.AutoConversion.<clinit>] Autoconversion will not be performed since file.encoding (IBM-1047) is not UTF-8.
[AUTOCVT.INFO] [ThreadID:2] [jdk.internal.util.AutoConversion.<clinit>] Autoconversion is false
Limitações da conversão automática
1. APIs ' FileChannel e ' RandomAccessFile
Embora a conversão automática permita uma abordagem perfeita para o suporte a UTF-8, a conversão para UTF-8 pode introduzir caracteres de vários bytes que resultam em um número diferente de bytes na codificação UTF-8 em relação aos dados brutos. Para classes Java, como ' java.nio.channels.FileChannel e ' java.io.RandomAccessFile, que permitem que o conteúdo do arquivo seja lido em deslocamentos específicos, não há suporte para a conversão automática. O uso das APIs com arquivos de texto marcados com ' non-UTF-8 resulta em um código ' errno2 de ' 0x05350651 e a seguinte exceção é lançada.
java.io.IOException: EDC5121I Invalid argument

O exemplo a seguir mostra a exceção que é vista quando a API ' FileChannel.read() é chamada em um arquivo de texto marcado com a conversão automática ativada.

Caused by: java.io.IOException: EDC5121I Invalid argument.
        at java.base/sun.nio.ch.UnixFileDispatcherImpl.pread0(Native Method)
        at java.base/sun.nio.ch.UnixFileDispatcherImpl.pread(UnixFileDispatcherImpl.java:57)
        at java.base/sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:338)
        at java.base/sun.nio.ch.IOUtil.read(IOUtil.java:306)
        at java.base/sun.nio.ch.IOUtil.read(IOUtil.java:283)
        at java.base/sun.nio.ch.FileChannelImpl.readInternal(FileChannelImpl.java:984)
        at java.base/sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:967)

Para resolver esse problema, o arquivo de entrada deve permanecer sem marcação ou marcado como binário, o que for mais apropriado.

2. Implicações com charsets explícitos
O recurso de conversão automática pode converter de forma transparente o conteúdo de arquivos de texto marcados para UTF-8. Esse suporte permite que os aplicativos assumam que o conteúdo do arquivo está em UTF-8, alcançando forte compatibilidade com as metas da JEP 400 na criação de uma experiência uniforme UTF-8-based. No entanto, quando o código do aplicativo define programaticamente uma codificação de caracteres específica, a funcionalidade pode ser comprometida ao operar em um ambiente UTF-8. Considere o seguinte exemplo " FileReader:
FileReader reader = new FileReader("EBCDIC.txt", Charset.forName("IBM-1047"));

Se o arquivo de entrada ' EBCDIC.txt for marcado como texto IBM-1047 e nenhuma propriedade ' file.encoding for definida, a conversão automática converterá automaticamente o conteúdo do arquivo ' EBCDIC.txt para UTF-8 no ' FileInputStream subjacente. O conjunto de caracteres IBM-1047 especificado causa uma interpretação incorreta do conteúdo UTF-8 como texto IBM-1047, resultando em conteúdo distorcido.

Para resolver esse problema, escolha uma das opções a seguir com base nos requisitos de seu aplicativo.

  • Desative a conversão automática definindo ' com.ibm.autocvt=false. Isso desativa todas as conversões automáticas. Com essa opção, a conversão automática é totalmente desativada no contexto da execução do programa aplicativo Java.
  • Use o modo de compatibilidade usando ' file.encoding=COMPAT. Essa configuração força o conjunto de caracteres padrão a reverter para o comportamento do Java 17, em que a codificação nativa é determinada com base na localidade. A conversão automática está desativada nesse cenário.
  • Deixe os arquivos de entrada sem marcação. Isso impede que o recurso de conversão automática converta o conteúdo do arquivo de entrada em UTF-8, e os bytes brutos do arquivo de entrada são apresentados.
  • Remova o charset específico de seu código-fonte, conforme mostrado no exemplo a seguir:
    FileReader reader = new FileReader("EBCDIC.txt");
    Nesse caso, o texto é interpretado como UTF-8, e o suporte subjacente à conversão automática de arquivos marcados o converte em UTF-8.
3. A função ' pread() pode falhar após a conversão automática UTF-8
Todos os arquivos marcados que não estiverem em UTF-8 e que usarem a classe pública ' FileChannel para abrir e ler ou gravar conteúdo falharão. Essa falha ocorre porque a função subjacente ' pread() não é suportada em arquivos que seguem a conversão automática entre codificações que não são um único byte para um único byte.

A mensagem de erro de exemplo mostra qual arquivo ' pread() causou o problema.


A imagem mostra qual pread() do arquivo causou o problema.

Resolva esse problema aplicando as seguintes etapas.

  1. Desmarque o arquivo usado pela classe ' FileChannel.
  2. Converta o arquivo em UTF-8 e marque-o como UTF-8.
Impacto do JEP 400 no comando ' javac

Com o JEP 400, o comando ' javac, usado para compilar arquivos de código-fonte Java em arquivos de classe, espera arquivos de código-fonte de entrada codificados em UTF-8. Se os arquivos de origem Java estiverem codificados em EBCDIC, você poderá usar uma das opções a seguir para alterar ou definir a codificação e compilar com êxito os arquivos de origem Java:

  • Especifique o conjunto de caracteres do arquivo de origem usando a opção ' -encoding do comando ' javac, conforme mostrado no exemplo a seguir.
    javac -encoding IBM-1047 MyJavaSource.java
  • Marque seus arquivos fonte Java codificados em EBCDIC com a codificação correta usando o chtag utilitário, conforme mostrado no exemplo a seguir.
    chtag -tc IBM-1047 MyJavaSource.java
Outros problemas conhecidos com o suporte a UTF-8
Substituir ' Charset no construtor da classe ' java.io.InputStreamReader
No construtor InputStreamReader(Inputstream in, Charset cs), ao ler do fluxo de entrada padrão em ` z/OS `, console.encoding o conjunto de caracteres substitui o conjunto de caracteres especificado, caso o `` não ConsoleInputStream esteja vinculado a um arquivo regular. Isso ajuda a garantir que a entrada seja interpretada corretamente, evitando dados corrompidos.
No construtor InputStreamReader(Inputstream in, Charset cs), ao ler do fluxo de entrada padrão em ` z/OS `, se a propriedade do console.encoding sistema `charset` for explicitamente fornecida, ela substitui o conjunto de caracteres especificado. Se não console.encoding for explicitamente fornecido, então ibm.system.encoding(que, por padrão, é IBM-1047 ) substitui o conjunto de caracteres especificado, desde que não haja um arquivo comum por trás do ConsoleInputStream. Isso pode garantir que a entrada seja interpretada corretamente, evitando dados ilegíveis.
Observação: A substituição do console.encoding charset para fluxos que são suportados por arquivos regulares pode ser desativada definindo a opção -Dcom.ibm.consoleEncoding.useForFileBackedConsoleStreams=false.
Substituir ' Charset no construtor da classe ' java.io.OutputStreamWriter
No construtor OutputStreamWriter(OutputStream out, Charset cs), console.encoding `charset` substitui o conjunto de caracteres fornecido ao gravar na saída padrão ou no erro padrão em ` z/OS `, desde que esses fluxos não sejam direcionados para arquivos comuns. Isso evita que a saída fique distorcida, pois ajuda a garantir que o conjunto de caracteres correto seja aplicado à saída de texto.
No construtor OutputStreamWriter(OutputStream out, Charset cs), console.encoding `charset` substitui o conjunto de caracteres fornecido ao gravar na saída padrão ou no erro padrão em ` z/OS `. Se não console.encoding for explicitamente fornecido, então ibm.system.encoding(que por padrão é IBM-1047 ) o conjunto de caracteres substitui o conjunto de caracteres fornecido ao escrever na saída padrão ou no erro padrão em z/OS, desde que esses fluxos não sejam direcionados para arquivos comuns. Isso evita que a saída fique distorcida, pois ajuda a garantir que o conjunto de caracteres correto seja aplicado à saída de texto.
Observação: A substituição do console.encoding charset para fluxos baseados em arquivos regulares pode ser desativada definindo a opção -Dcom.ibm.consoleEncoding.useForFileBackedConsoleStreams=false.
Migração de versões anteriores
A mudança para adotar UTF-8 pode causar desafios de migração para aplicativos Java existentes que interagem com arquivos non-UTF-8. A seguir estão as possíveis soluções que permitem que seus aplicativos sejam executados após a migração para Semeru a versão 21.
  • Converta e marque seus arquivos para a codificação UTF-8. Espera-se que essa solução seja a melhor em termos de desempenho, pois minimiza o uso do processador necessário para a conversão de charset.
  • Marque todos os arquivos de entrada com a codificação apropriada usando o utilitário ' chtag. A conversão automática é ativada por padrão se ' file.encoding não estiver definido e converte o conteúdo do arquivo de texto marcado para UTF-8.
    Observação: os arquivos de saída que usam o modo padrão permanecem codificados em UTF-8.
  • Especifique um conjunto de caracteres explícito usando ' file.encoding=<charset>. Isso influencia o conjunto de caracteres padrão dos arquivos de entrada e saída para o conjunto de caracteres especificado.
  • Use o modo de compatibilidade especificando ' file.encoding=COMPAT. Esse modo reverte a configuração do conjunto de caracteres padrão para o comportamento " 17’s do Java, em que a codificação nativa é determinada com base na localidade.