Sim, é frustrante - às vezes type
e 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. chcp
mostra 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 chcp
ou
GetConsoleOutputCP
e configurar-se para produzir essa codificação ou
Você ou um programa pode definir a página de códigos atual do console usando chcp
ou
SetConsoleOutputCP
para 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, WriteConsoleW
não funcionará se a saída do programa for redirecionada; é necessário mais mexer nesse caso.
type
funciona 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 type
inserir 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 type
os 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 type
para 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 CON
não exibir corretamente o Unicode, podemos concluir que o type
comando 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.exe
em um depurador quando ele sai de type
um arquivo:
Após type
abrir um arquivo, ele procura uma lista técnica de - 0xFEFF
ou seja, os bytes
0xFF 0xFE
em little endian - e, se houver uma lista técnica, type
define um fOutputUnicode
sinalizador interno . Este sinalizador é marcado mais tarde para decidir se deseja ligar WriteConsoleW
.
Mas essa é a única maneira de obter type
saí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 type
gera 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
chcp
e provavelmente ainda terá uma saída estranha.
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 chamarWideCharToMultiByte
.GetStdHandle(STD_OUTPUT_HANDLE)
e Cstdout
são identificadores de console. Na prática, para testar um console, verifique se foiGetConsoleMode
bem-sucedido. Além disso, não use a_isatty
funçã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 incluiNUL
entre outros. Em vez disso, chame_get_osfhandle
e verifique o identificador diretamente.Tipo
para ver sua página de código atual (como Dewfy já disse).
Usar
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
.fonte
nlsinfo
não parece existir no meu Windows 7.nlsinfo
também não existe na minha máquina com Windows XP SP3.nlsinfo
também não existe na máquina Windows 10E.Para responder sua segunda consulta, re. como a codificação funciona, Joel Spolsky escreveu um ótimo artigo introdutório sobre isso . Fortemente recomendado.
fonte
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.
fonte
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:
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!
fonte
Em Java, usei a codificação "IBM850" para gravar o arquivo. Isso resolveu o problema.
fonte