Qual página de codificação / código está usando o cmd.exe?

271

Quando abro o cmd.exe no Windows, que codificação está usando?

Como posso verificar qual codificação está usando atualmente? Depende da minha configuração regional ou existem variáveis ​​de ambiente para verificar?

O que acontece quando você digita um arquivo com uma determinada codificação? Às vezes, recebo caracteres ilegíveis (codificação incorreta usada) e às vezes funciona. No entanto, não confio em nada, desde que não saiba o que está acontecendo. Alguém pode explicar?

danglund
fonte

Respostas:

389

Sim, é frustrante - às vezes typee outros programas imprimem bobagens, e às vezes não.

Primeiro, os caracteres Unicode serão exibidos apenas se a fonte do console atual contiver os caracteres . Portanto, use uma fonte TrueType como Lucida Console, em vez da fonte Raster padrão.

Mas se a fonte do console não contiver o caractere que você está tentando exibir, você verá pontos de interrogação em vez de sem sentido. Quando você fica sem sentido, há mais coisas acontecendo do que apenas configurações de fonte.

Quando os programas usam funções padrão de E / S da biblioteca C printf, a codificação de saída do programa deve corresponder à codificação de saída do console , ou você ficará sem sentido. chcpmostra e define a página de código atual. Toda saída usando funções de E / S da biblioteca C padrão é tratada como se estivesse na página de códigos exibida por chcp.

A correspondência da codificação de saída do programa com a codificação de saída do console pode ser realizada de duas maneiras diferentes:

  • Um programa pode obter a página de código atual do console usando chcpou GetConsoleOutputCPe configurar-se para produzir essa codificação ou

  • Você ou um programa pode definir a página de códigos atual do console usando chcpou SetConsoleOutputCPpara corresponder à codificação de saída padrão do programa.

No entanto, os programas que usam APIs do Win32 podem gravar seqüências de caracteres UTF-16LE diretamente no console WriteConsoleW. Essa é a única maneira de obter a saída correta sem definir páginas de código. E mesmo ao usar essa função, se uma seqüência de caracteres não estiver na codificação UTF-16LE, um programa Win32 deve passar a página de código correta para MultiByteToWideChar. Além disso, WriteConsoleWnão funcionará se a saída do programa for redirecionada; é necessário mais mexer nesse caso.

typefunciona algumas vezes porque verifica o início de cada arquivo em busca de uma BOM ( UTF-16LE Byte Order Mark) , ou seja, os bytes 0xFF 0xFE. Se encontrar essa marca, ele exibirá os caracteres Unicode no arquivo, WriteConsoleW independentemente da página de código atual. Porém, ao typeinserir qualquer arquivo sem uma lista técnica UTF-16LE ou para usar caracteres não ASCII com qualquer comando que não seja chamado WriteConsoleW- será necessário definir a página de código do console e a codificação de saída do programa para coincidirem.


Como podemos descobrir isso?

Aqui está um arquivo de teste contendo caracteres Unicode:

ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好

Aqui está um programa Java para imprimir o arquivo de teste em várias codificações Unicode diferentes. Pode estar em qualquer linguagem de programação; apenas imprime caracteres ASCII ou bytes codificados em stdout.

import java.io.*;

public class Foo {

    private static final String BOM = "\ufeff";
    private static final String TEST_STRING
        = "ASCII     abcde xyz\n"
        + "German    äöü ÄÖÜ ß\n"
        + "Polish    ąęźżńł\n"
        + "Russian   абвгдеж эюя\n"
        + "CJK       你好\n";

    public static void main(String[] args)
        throws Exception
    {
        String[] encodings = new String[] {
            "UTF-8", "UTF-16LE", "UTF-16BE", "UTF-32LE", "UTF-32BE" };

        for (String encoding: encodings) {
            System.out.println("== " + encoding);

            for (boolean writeBom: new Boolean[] {false, true}) {
                System.out.println(writeBom ? "= bom" : "= no bom");

                String output = (writeBom ? BOM : "") + TEST_STRING;
                byte[] bytes = output.getBytes(encoding);
                System.out.write(bytes);
                FileOutputStream out = new FileOutputStream("uc-test-"
                    + encoding + (writeBom ? "-bom.txt" : "-nobom.txt"));
                out.write(bytes);
                out.close();
            }
        }
    }
}

A saída na página de códigos padrão? Lixo total!

Z:\andrew\projects\sx\1259084>chcp
Active code page: 850

Z:\andrew\projects\sx\1259084>java Foo
== UTF-8
= no bom
ASCII     abcde xyz
German    ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish    ąęźżńł
Russian   ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK       õ¢áÕÑ¢
= bom
´╗┐ASCII     abcde xyz
German    ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish    ąęźżńł
Russian   ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK       õ¢áÕÑ¢
== UTF-16LE
= no bom
A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h         ♣☺↓☺z☺|☺D☺B☺
 R u s s i a n       0♦1♦2♦3♦4♦5♦6♦  M♦N♦O♦
 C J K               `O}Y
 = bom
 ■A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h         ♣☺↓☺z☺|☺D☺B☺
 R u s s i a n       0♦1♦2♦3♦4♦5♦6♦  M♦N♦O♦
 C J K               `O}Y
 == UTF-16BE
= no bom
 A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h        ☺♣☺↓☺z☺|☺D☺B
 R u s s i a n      ♦0♦1♦2♦3♦4♦5♦6  ♦M♦N♦O
 C J K              O`Y}
= bom
■  A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h        ☺♣☺↓☺z☺|☺D☺B
 R u s s i a n      ♦0♦1♦2♦3♦4♦5♦6  ♦M♦N♦O
 C J K              O`Y}
== UTF-32LE
= no bom
A   S   C   I   I                       a   b   c   d   e       x   y   z
   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                   ♣☺  ↓☺  z☺  |☺  D☺  B☺
   R   u   s   s   i   a   n               0♦  1♦  2♦  3♦  4♦  5♦  6♦      M♦  N
♦  O♦
   C   J   K                               `O  }Y
   = bom
 ■  A   S   C   I   I                       a   b   c   d   e       x   y   z

   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                   ♣☺  ↓☺  z☺  |☺  D☺  B☺
   R   u   s   s   i   a   n               0♦  1♦  2♦  3♦  4♦  5♦  6♦      M♦  N
♦  O♦
   C   J   K                               `O  }Y
   == UTF-32BE
= no bom
   A   S   C   I   I                       a   b   c   d   e       x   y   z
   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                  ☺♣  ☺↓  ☺z  ☺|  ☺D  ☺B
   R   u   s   s   i   a   n              ♦0  ♦1  ♦2  ♦3  ♦4  ♦5  ♦6      ♦M  ♦N
  ♦O
   C   J   K                              O`  Y}
= bom
  ■    A   S   C   I   I                       a   b   c   d   e       x   y   z

   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                  ☺♣  ☺↓  ☺z  ☺|  ☺D  ☺B
   R   u   s   s   i   a   n              ♦0  ♦1  ♦2  ♦3  ♦4  ♦5  ♦6      ♦M  ♦N
  ♦O
   C   J   K                              O`  Y}

No entanto, e se typeos arquivos que foram salvos? Eles contêm exatamente os mesmos bytes que foram impressos no console.

Z:\andrew\projects\sx\1259084>type *.txt

uc-test-UTF-16BE-bom.txt


■  A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h        ☺♣☺↓☺z☺|☺D☺B
 R u s s i a n      ♦0♦1♦2♦3♦4♦5♦6  ♦M♦N♦O
 C J K              O`Y}

uc-test-UTF-16BE-nobom.txt


 A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h        ☺♣☺↓☺z☺|☺D☺B
 R u s s i a n      ♦0♦1♦2♦3♦4♦5♦6  ♦M♦N♦O
 C J K              O`Y}

uc-test-UTF-16LE-bom.txt


ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好

uc-test-UTF-16LE-nobom.txt


A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h         ♣☺↓☺z☺|☺D☺B☺
 R u s s i a n       0♦1♦2♦3♦4♦5♦6♦  M♦N♦O♦
 C J K               `O}Y

uc-test-UTF-32BE-bom.txt


  ■    A   S   C   I   I                       a   b   c   d   e       x   y   z

   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                  ☺♣  ☺↓  ☺z  ☺|  ☺D  ☺B
   R   u   s   s   i   a   n              ♦0  ♦1  ♦2  ♦3  ♦4  ♦5  ♦6      ♦M  ♦N
  ♦O
   C   J   K                              O`  Y}

uc-test-UTF-32BE-nobom.txt


   A   S   C   I   I                       a   b   c   d   e       x   y   z
   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                  ☺♣  ☺↓  ☺z  ☺|  ☺D  ☺B
   R   u   s   s   i   a   n              ♦0  ♦1  ♦2  ♦3  ♦4  ♦5  ♦6      ♦M  ♦N
  ♦O
   C   J   K                              O`  Y}

uc-test-UTF-32LE-bom.txt


 A S C I I           a b c d e   x y z
 G e r m a n         ä ö ü   Ä Ö Ü   ß
 P o l i s h         ą ę ź ż ń ł
 R u s s i a n       а б в г д е ж   э ю я
 C J K               你 好

uc-test-UTF-32LE-nobom.txt


A   S   C   I   I                       a   b   c   d   e       x   y   z
   G   e   r   m   a   n                   õ   ÷   ³       ─   Í   ▄       ▀
   P   o   l   i   s   h                   ♣☺  ↓☺  z☺  |☺  D☺  B☺
   R   u   s   s   i   a   n               0♦  1♦  2♦  3♦  4♦  5♦  6♦      M♦  N
♦  O♦
   C   J   K                               `O  }Y

uc-test-UTF-8-bom.txt


´╗┐ASCII     abcde xyz
German    ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish    ąęźżńł
Russian   ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK       õ¢áÕÑ¢

uc-test-UTF-8-nobom.txt


ASCII     abcde xyz
German    ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish    ąęźżńł
Russian   ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK       õ¢áÕÑ¢

A única coisa que funciona é o arquivo UTF-16LE, com uma BOM, impresso no console via type.

Se usarmos outra coisa que não seja typepara imprimir o arquivo, obteremos lixo:

Z:\andrew\projects\sx\1259084>copy uc-test-UTF-16LE-bom.txt CON
 ■A S C I I           a b c d e   x y z
 G e r m a n         õ ÷ ³   ─ Í ▄   ▀
 P o l i s h         ♣☺↓☺z☺|☺D☺B☺
 R u s s i a n       0♦1♦2♦3♦4♦5♦6♦  M♦N♦O♦
 C J K               `O}Y
         1 file(s) copied.

Pelo fato de copy CONnão exibir corretamente o Unicode, podemos concluir que o typecomando possui lógica para detectar uma BOM UTF-16LE no início do arquivo e usar APIs especiais do Windows para imprimi-lo.

Podemos ver isso abrindo cmd.exeem um depurador quando ele sai de type um arquivo:

insira a descrição da imagem aqui

Após typeabrir um arquivo, ele procura uma lista técnica de - 0xFEFFou seja, os bytes 0xFF 0xFEem little endian - e, se houver uma lista técnica, typedefine um fOutputUnicodesinalizador interno . Este sinalizador é marcado mais tarde para decidir se deseja ligar WriteConsoleW.

Mas essa é a única maneira de obter typesaída Unicode, e apenas para arquivos que possuem BOMs e estão em UTF-16LE. Para todos os outros arquivos e para programas que não possuem código especial para lidar com a saída do console, seus arquivos serão interpretados de acordo com a página de códigos atual e provavelmente serão exibidos como sem sentido.

Você pode emular como typegera Unicode para o console em seus próprios programas, da seguinte maneira:

#include <stdio.h>
#define UNICODE
#include <windows.h>

static LPCSTR lpcsTest =
    "ASCII     abcde xyz\n"
    "German    äöü ÄÖÜ ß\n"
    "Polish    ąęźżńł\n"
    "Russian   абвгдеж эюя\n"
    "CJK       你好\n";

int main() {
    int n;
    wchar_t buf[1024];

    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    n = MultiByteToWideChar(CP_UTF8, 0,
            lpcsTest, strlen(lpcsTest),
            buf, sizeof(buf));

    WriteConsole(hConsole, buf, n, &n, NULL);

    return 0;
}

Este programa funciona para imprimir Unicode no console do Windows usando a página de código padrão.


Para o programa Java de amostra, podemos obter um pouco da saída correta configurando a página de código manualmente, embora a saída seja confusa de maneiras estranhas:

Z:\andrew\projects\sx\1259084>chcp 65001
Active code page: 65001

Z:\andrew\projects\sx\1259084>java Foo
== UTF-8
= no bom
ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好
ж эюя
CJK       你好
 你好
好
�
= bom
ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好
еж эюя
CJK       你好
  你好
好
�
== UTF-16LE
= no bom
A S C I I           a b c d e   x y z
…

No entanto, um programa C que define uma página de código UTF-8 Unicode:

#include <stdio.h>
#include <windows.h>

int main() {
    int c, n;
    UINT oldCodePage;
    char buf[1024];

    oldCodePage = GetConsoleOutputCP();
    if (!SetConsoleOutputCP(65001)) {
        printf("error\n");
    }

    freopen("uc-test-UTF-8-nobom.txt", "rb", stdin);
    n = fread(buf, sizeof(buf[0]), sizeof(buf), stdin);
    fwrite(buf, sizeof(buf[0]), n, stdout);

    SetConsoleOutputCP(oldCodePage);

    return 0;
}

tem saída correta:

Z:\andrew\projects\sx\1259084>.\test
ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好

A moral da história?

  • type pode imprimir arquivos UTF-16LE com uma lista técnica, independentemente da sua página de código atual
  • Os programas Win32 podem ser programados para gerar Unicode no console, usando WriteConsoleW.
  • Outros programas que definem a página de códigos e ajustam sua codificação de saída de acordo podem imprimir Unicode no console, independentemente da página de código quando o programa foi iniciado.
  • Para todo o resto, você terá que mexer chcpe provavelmente ainda terá uma saída estranha.
andrewdotn
fonte
73
Uau, essa deve ser a resposta mais detalhada que eu já vi no SO. Crédito extra pelas impressões de desmontagem e habilidades em vários idiomas! Apenas lindo, senhor!
ataque aéreo
2
Pode-se também estudar a extensão específica da Microsoft _setmode (_fileno (stdout), _O_U16TEXT), introduzida no VS2008. Consulte stackoverflow.com/a/9051543 e stackoverflow.com/a/12015918 e msdn.microsoft.com/en-us/library/tw4k6df8(v=vs.90).aspx Além das óbvias diferenças de portabilidade entre _setmode () e SetConsoleOutputCP (), também pode haver outras sutilezas e efeitos colaterais ocultos nas duas abordagens que não são totalmente compreendidos à primeira vista. Se andrewdotn pudesse atualizar sua resposta com quaisquer observações sobre _setmode (fd, _O_U16TEXT), isso seria ótimo.
JasDev 7/09/14
13
Embora essa seja uma excelente resposta, é enganoso dizer que o console suporta UTF-16. É limitado ao UCS-2, ou seja, limitado a caracteres no plano multilíngue básico (BMP). Quando o servidor do console Win32 (conhost.exe, atualmente) foi projetado por volta de 1990, o Unicode era um padrão de 16 bits, portanto, o buffer da tela do console usa um WCHAR de 16 bits por célula de caractere. Um par substituto UTF-16 é impresso como dois caracteres de caixa.
Eryk Sun
3
@ user200783, o formulário decomposto não é suportado; geralmente é possível transformar em um equivalente NFC. Além disso, o console em locais ocidentais não permite misturar glifos de largura total e meia largura. Além disso, ao usar a página de código 65001 (UTF-8), anterior ao Windows 8 WriteFile, o número de caracteres gravados é substituído pelo número de bytes, portanto, os gravadores em buffer tentam os bytes 'restantes' várias vezes na proporção do número de caracteres não ASCII . Também em 65001, a leitura de caracteres não ASCII falha no conhost.exe porque ele assume 1 byte ANSI por código UTF-16 ao chamar WideCharToMultiByte.
Eryk Sun
2
Os programas de demonstração simples nesta resposta assumem que GetStdHandle(STD_OUTPUT_HANDLE)e C stdoutsão identificadores de console. Na prática, para testar um console, verifique se foi GetConsoleModebem-sucedido. Além disso, não use a _isattyfunção de tempo de execução C para verificar se um descritor de arquivo de E / S baixa é um console; que apenas verifica se há um dispositivo no modo de caractere, que inclui NULentre outros. Em vez disso, chame _get_osfhandlee verifique o identificador diretamente.
Eryk Sun
29

Tipo

chcp

para ver sua página de código atual (como Dewfy já disse).

Usar

nlsinfo

para ver todas as páginas de código instaladas e descobrir o que significa o número da página de código.

Você precisa ter o kit de recursos do Windows Server 2003 instalado (funciona no Windows XP) para usar nlsinfo.

Cagdas Altinkaya
fonte
19
Curiosamente, nlsinfonão parece existir no meu Windows 7.
Joey
2
nlsinfotambém não existe na minha máquina com Windows XP SP3.
9338 Thomas Owens
2
Oh, me desculpe. Eu acho que ele vem com as ferramentas do Windows Server Resource Kit. Eu o usei algumas vezes na minha máquina com Windows XP SP3 anteriormente e não sabia que não estava instalado por padrão.
Cagdas Altinkaya 11/08/09
Ah, isso explica por que está lá na minha máquina Vista, onde eu as instalei.
11119 Joey
4
nlsinfotambém não existe na máquina Windows 10E.
Yousha Aleayoub 8/03/19
21

Para responder sua segunda consulta, re. como a codificação funciona, Joel Spolsky escreveu um ótimo artigo introdutório sobre isso . Fortemente recomendado.

Brian Agnew
fonte
13
Eu li e sei. No entanto, no Windows, sempre me sinto perdido porque o sistema operacional e a maioria dos aplicativos parecem totalmente ignorantes quanto à codificação.
danglund
5

O comando CHCP mostra a página de código atual. Ele tem três dígitos: 8xx e é diferente do Windows 12xx. Portanto, digitando um texto somente em inglês, você não verá nenhuma diferença, mas uma página de código estendida (como cirílico) será impressa incorretamente.

Dewfy
fonte
5
O CHCP não mostra apenas 3 dígitos nem está no formato 8 ##. 437 é, por exemplo, uma codificação americana e é o padrão padrão nos sistemas ingleses. - 65001 é uma codificação Unicode (se bem me lembro, é UTF-8 e 65000 é UTF-7) e pode ser escolhido. O CMD também permite alternar para a página de código 1250, por exemplo, mas não sei desde quando essas páginas de código são selecionáveis. (É sob Win7.)
Adam LS
4

Há muito tempo fico frustrado com os problemas da página de códigos do Windows e com os problemas de portabilidade e localização dos programas C que eles causam. As postagens anteriores detalharam detalhadamente os problemas, por isso não adicionarei nada a esse respeito.

Para resumir uma longa história, acabei escrevendo minha própria camada de biblioteca de compatibilidade UTF-8 na biblioteca C padrão do Visual C ++. Basicamente, esta biblioteca garante que um programa C padrão funcione corretamente, em qualquer página de código, usando UTF-8 internamente.

Esta biblioteca, chamada MsvcLibX, está disponível como código aberto em https://github.com/JFLarvoire/SysToolsLib . Principais características:

  • Origens C codificadas em UTF-8, usando seqüências de caracteres char [] C normais e APIs de biblioteca C padrão.
  • Em qualquer página de código, tudo é processado internamente como UTF-8 no seu código, incluindo a rotina principal () argv [], com entrada e saída padrão convertidas automaticamente para a página de código correta.
  • Todas as funções do arquivo stdio.h suportam nomes de caminho UTF-8> 260 caracteres, até 64 KBytes.
  • As mesmas fontes podem compilar e vincular com êxito no Windows usando as bibliotecas Visual C ++ e MsvcLibX e Visual C ++ C, e no Linux usando a biblioteca C padrão gcc e Linux, sem a necessidade de blocos #ifdef ... #endif.
  • As inclusões incluem arquivos comuns no Linux, mas ausentes no Visual C ++. Ex: unistd.h
  • Adiciona funções ausentes, como as de E / S do diretório, gerenciamento de link simbólico etc., tudo com suporte a UTF-8, é claro :-).

Mais detalhes no arquivo MsvcLibX README no GitHub , incluindo como criar a biblioteca e usá-la em seus próprios programas.

A seção de liberação no repositório GitHub acima fornece vários programas usando esta biblioteca MsvcLibX, que mostram seus recursos. Ex: Experimente minha ferramenta which.exe com diretórios com nomes não ASCII no PATH, procurando programas com nomes não ASCII e alterando as páginas de código.

Outra ferramenta útil é o programa conv.exe. Este programa pode converter facilmente um fluxo de dados de qualquer página de código para outra. Seu padrão é entrada na página de códigos do Windows e saída na página de códigos atual do console. Isso permite exibir corretamente os dados gerados pelos aplicativos da GUI do Windows (por exemplo: Bloco de notas) em um console de comando, com um comando simples como:type WINFILE.txt | conv

Esta biblioteca MsvcLibX não é de forma alguma completa, e contribuições para melhorá-la são bem-vindas!

Jean-François Larvoire
fonte
2

Em Java, usei a codificação "IBM850" para gravar o arquivo. Isso resolveu o problema.

Neumi
fonte