Sí, es frustrante, a veces type
y otros programas imprimen galimatías, y a veces no lo hacen.
En primer lugar, los caracteres Unicode solo mostrarán if the current console font contains the characters. Por lo tanto, use una fuente TrueType como Lucida Console en lugar de la fuente predeterminada de Raster.
Pero si la fuente de la consola no contiene el carácter que está tratando de mostrar, verá signos de interrogación en lugar de galimatías. Cuando se cometen galimatías, , hay más cosas en juego que la configuración de fuentes.
Cuando programas utilizan funciones estándar C-biblioteca de E/S como printf
, codificación de salida el programa de debe coincidir con codificación de salida de la consola o obtendrá un galimatías. chcp
muestra y establece la página de códigos actual. Todos los resultados de que utilizan funciones de E/S de bibliotecas C estándar se tratan como si estuvieran en la página de códigos mostrada por chcp
.
Coincidencia de codificación de salida del programa con codificación de salida de la consola se puede lograr de dos maneras diferentes:
Un programa puede obtener la página de códigos actual de la consola con chcp
o GetConsoleOutputCP
y configurarse a sí mismo a la producción en que la codificación, o
Usted o un programa puede establecer la página de códigos actual de la consola con chcp
o SetConsoleOutputCP
para que coincida la codificación de salida predeterminada del programa.
Sin embargo, los programas que utilizan API Win32 puede escribir cadenas UTF-16LE directamente a la consola con WriteConsoleW
. Esta es la única forma de obtener una salida correcta sin configurar páginas de códigos. Y incluso cuando se utiliza esa función, si una cadena no está en la codificación UTF-16LE , un programa Win32 debe pasar la página de códigos correcta al MultiByteToWideChar
. Además, WriteConsoleW
no funcionará si se redirige la salida del programa; se necesita algo más de violín en ese caso.
type
funciona parte del tiempo porque comprueba al principio de cada archivo para un UTF-16LE Byte Order Mark (BOM), es decir, los bytes 0xFF 0xFE
. Si encuentra dicha marca , muestra los caracteres Unicode en el archivo usando WriteConsoleW
independientemente de la página de códigos actual. Pero cuando type
ing cualquier archivo sin una BOM UTF-16LE , o para usar caracteres no ASCII con cualquier comando que no llame al WriteConsoleW
, deberá configurar la página de códigos de la consola y la codificación de salida del programa para que coincidan entre sí.
¿Cómo podemos descubrir esto?
Aquí es un archivo de prueba que contiene caracteres Unicode:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
He aquí un programa Java para imprimir el archivo de prueba en un montón de diferentes codificaciones Unicode. Podría estar en cualquier lenguaje de programación; solo imprime caracteres ASCII o bytes codificados en 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();
}
}
}
}
¿La salida en la página de códigos predeterminada? ¡Basura 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}
Sin embargo, ¿qué pasaría si type
los archivos que se salvó? Contienen los mismos bytes exactos que se imprimieron en la consola.
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 õ¢áÕÑ¢
La única cosa que funciona es el archivo UTF-16LE, con una lista de materiales, impresa a través de la consola type
.
Si utilizamos distintos de type
nada para imprimir el archivo, obtenemos la basura:
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.
Del hecho de que copy CON
no muestra Unicode correctamente, podemos la conclusión de que el comando type
tiene lógica para detectar una lista de materiales UTF-16LE en el inicio del archivo y use API especiales de Windows para imprimirla.
Podemos ver esto abriendo cmd.exe
en un depurador cuando se va a type
un archivo:
Después type
abre un archivo, se comprueba si hay una lista de materiales de 0xFEFF
-es decir, la bytes 0xFF 0xFE
en little-endian-y si hay tal BOM, type
establece un indicador interno fOutputUnicode
. Esta marca se marca más adelante para decidir si se llama al WriteConsoleW
.
Pero esa es la única manera de obtener type
para generar Unicode, y solo para los archivos que tienen listas de materiales y están en UTF-16LE. Para todos los demás archivos y para los programas que no tengan un código especial para manejar la salida de la consola, sus archivos serán interpretados de acuerdo con la página de códigos actual, y es probable que aparezcan como galimatías.
puede emular cómo type
salidas Unicode a la consola en sus propios programas, así:
#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 trabaja para la impresión Unicode en la consola de Windows utilizando la página de códigos defecto.
Para el programa Java de ejemplo, podemos conseguir un poco de la salida correcta por establecer la página de códigos manualmente, aunque la salida se cometa un error de maneras extrañas:
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
…
Sin embargo, un C programa que establece un Unicode códigos UTF-8:
#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;
}
tiene salida correcta:
Z:\andrew\projects\sx\1259084>.\test
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
¿La moraleja de la historia?
type
puede imprimir archivos UTF-16LE con una lista de materiales, independientemente de su página de códigos activa programas
- Win32 pueden ser programados para la producción de Unicode para la consola, usando
WriteConsoleW
.
- Otros programas que establezcan el código de página y ajustar su codificación de salida en consecuencia puede imprimir Unicode en la consola, independientemente de lo que la página de códigos fue cuando se inició el programa
- Para todo lo demás tendrá que perder el tiempo con
chcp
, y probablemente todavía obtener resultados raros.
Whoa, esta debe ser la respuesta más detallada que he visto en SO. ¡Crédito adicional para las impresiones de dissembly y skillz multilenguaje! Simplemente hermoso, señor! –
También es posible que desee estudiar la extensión específica de Microsoft _setmode (_fileno (stdout), _O_U16TEXT) que se introdujo en VS2008. Vea http://stackoverflow.com/a/9051543, y http://stackoverflow.com/a/12015918, y http://msdn.microsoft.com/en-us/library/tw4k6df8(v=vs. 90) .aspx Además de las obvias diferencias de portabilidad entre _setmode() y SetConsoleOutputCP(), también puede haber otras sutilezas y efectos secundarios ocultos en ambos enfoques que no se entienden completamente a primera vista. Si andrewdotn pudiera actualizar su respuesta con cualquier observación sobre _setmode (fd, _O_U16TEXT), sería genial. – JasDev
Si bien esta es una respuesta excelente, es engañoso decir que la consola admite UTF-16. Está limitado a UCS-2, es decir, limitado a caracteres en el plano multilingüe básico (BMP). Cuando el servidor de consola Win32 (conhost.exe, hoy en día) se diseñó alrededor de 1990, Unicode era un estándar de 16 bits, por lo que el búfer de pantalla de la consola usa un WCHAR de 16 bits por celda de caracteres. Un par suplente UTF-16 se imprime como dos caracteres de cuadro. – eryksun