Sí, es frustrante, a veces typey otros programas imprimen galimatías, y otras no.
En primer lugar, los caracteres Unicode solo se mostrarán si la fuente de la consola actual contiene los caracteres . Por lo tanto, use una fuente TrueType como Lucida Console en lugar de la fuente Raster predeterminada.
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 obtienes galimatías, hay más cosas que solo configuraciones de fuente.
Cuando los programas usan funciones estándar de E / S de la biblioteca C printf, como , la codificación de salida del programa debe coincidir con la codificación de salida de la consola , o obtendrá galimatías. chcpmuestra y establece la página de códigos actual. Todos los resultados que utilizan las funciones estándar de E / S de la biblioteca C se tratan como si estuvieran en la página de códigos que muestra chcp.
La coincidencia de la codificación de salida del programa con la 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 usando chcpo
GetConsoleOutputCP, y configurarse para generar esa codificación, o
Usted o un programa pueden configurar la página de códigos actual de la consola usando chcpo
SetConsoleOutputCPpara que coincida con la codificación de salida predeterminada del programa.
Sin embargo, los programas que usan las API de Win32 pueden escribir cadenas UTF-16LE directamente en la consola con
WriteConsoleW. Esta es la única forma de obtener una salida correcta sin establecer páginas de códigos. E incluso cuando se usa esa función, si una cadena no está en la codificación UTF-16LE para empezar, un programa Win32 debe pasar la página de códigos correcta
MultiByteToWideChar. También,WriteConsoleW no funcionará si la salida del programa se redirige; se necesita más violín en ese caso.
typefunciona algunas veces porque comprueba el inicio de cada archivo para una marca de orden de bytes (BOM) UTF-16LE , es decir, los bytes 0xFF 0xFE. Si encuentra dicha marca, muestra los caracteres Unicode en el archivo, WriteConsoleW
independientemente de la página de códigos actual. Pero al typecrear cualquier archivo sin una lista de materiales UTF-16LE, o para usar caracteres no ASCII con cualquier comando que no llame, WriteConsoleWdeberá 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í hay un archivo de prueba que contiene caracteres Unicode:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Aquí hay 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 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é pasa si tenemos typelos archivos que se guardaron? Contienen exactamente los mismos bytes 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 õ¢áÕÑ¢
Lo único que funciona es el archivo UTF-16LE, con una lista de materiales, impreso a través de la consola type.
Si usamos otra cosa que no sea typepara imprimir el archivo, obtenemos 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.
Por el hecho de que copy CONno muestra Unicode correctamente, podemos concluir que el typecomando tiene lógica para detectar una lista de materiales UTF-16LE al comienzo del archivo y usar API especiales de Windows para imprimirlo.
Podemos ver esto abriendo cmd.exeen un depurador cuando va a type
un archivo:

Después de typeabrir un archivo, comprueba una lista de materiales de 0xFEFF—es decir, los bytes
0xFF 0xFEen little-endian— y, si existe dicha lista de materiales, typeestablece un fOutputUnicodeindicador interno . Esta bandera se verifica más adelante para decidir si se debe llamar WriteConsoleW.
Pero esa es la única forma de obtener typeunicode de salida, y solo para archivos que tienen BOM y están en UTF-16LE. Para todos los demás archivos, y para programas que no tienen un código especial para manejar la salida de la consola, sus archivos se interpretarán de acuerdo con la página de códigos actual, y probablemente se mostrarán como galimatías.
Puede emular cómo las typesalidas Unicode a la consola en sus propios programas de esta manera:
#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 en la consola de Windows utilizando la página de códigos predeterminada.
Para el programa Java de muestra, podemos obtener un poco de salida correcta configurando la página de códigos manualmente, aunque la salida se confunde 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 programa en C que establece una página de códigos Unicode 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 actual
- Los programas Win32 se pueden programar para generar Unicode en la consola, utilizando
WriteConsoleW.
- Otros programas que configuran la página de códigos y ajustan su codificación de salida en consecuencia pueden imprimir Unicode en la consola, independientemente de la página de códigos cuando se inició el programa.
- Para todo lo demás, tendrá que jugar
chcpy probablemente aún obtendrá resultados extraños.
WriteFileinforma el número de caracteres escritos en lugar del número de bytes, por lo que los escritores almacenados en búfer vuelven a intentar los bytes 'restantes' varias veces en proporción al número de caracteres no ASCII . También en 65001, la lectura de caracteres no ASCII falla en conhost.exe porque asume 1 byte ANSI por código UTF-16 al llamarWideCharToMultiByte.GetStdHandle(STD_OUTPUT_HANDLE)y Cstdoutson controladores de consola. En la práctica, para probar una consola, verifique queGetConsoleModetenga éxito. Además, no use la_isattyfunción de tiempo de ejecución C para verificar si un descriptor de archivo de E / S bajo es una consola; eso solo busca un dispositivo en modo personaje, que incluyeNULentre otros. En su lugar, llame_get_osfhandley verifique el identificador directamente.Tipo
para ver su página de códigos actual (como ya dijo Dewfy).
Utilizar
para ver todas las páginas de códigos instaladas y averiguar qué significa su número de página de códigos.
Debe tener el kit de recursos de Windows Server 2003 instalado (funciona en Windows XP) para usar
nlsinfo.fuente
nlsinfono parece existir en mi Windows 7.nlsinfotampoco existe en mi máquina con Windows XP SP3.nlsinfotampoco existe en la máquina con Windows 10E.Para responder a su segunda consulta re. cómo funciona la codificación, Joel Spolsky escribió un excelente artículo introductorio sobre esto . Muy recomendado.
fuente
El comando CHCP muestra la página de códigos actual. Tiene tres dígitos: 8xx y es diferente de Windows 12xx. Por lo tanto, al escribir un texto solo en inglés no vería ninguna diferencia, pero una página de códigos extendida (como cirílico) se imprimirá incorrectamente.
fuente
He estado frustrado durante mucho tiempo por los problemas de la página de códigos de Windows y los problemas de portabilidad y localización de los programas C que causan. Las publicaciones anteriores han detallado los problemas en detalle, por lo que no voy a agregar nada a este respecto.
Para resumir, finalmente terminé escribiendo mi propia capa de biblioteca de compatibilidad UTF-8 sobre la biblioteca C estándar de Visual C ++. Básicamente, esta biblioteca garantiza que un programa C estándar funcione correctamente, en cualquier página de códigos, utilizando UTF-8 internamente.
Esta biblioteca, llamada MsvcLibX, está disponible como código abierto en https://github.com/JFLarvoire/SysToolsLib . Principales características:
Más detalles en el archivo README MsvcLibX en GitHub , incluyendo cómo construir la biblioteca y usarla en sus propios programas.
La sección de lanzamiento en el repositorio de GitHub anterior proporciona varios programas que usan esta biblioteca MsvcLibX, que mostrarán sus capacidades. Por ejemplo: pruebe mi herramienta which.exe con directorios con nombres que no sean ASCII en la RUTA, busque programas con nombres que no sean ASCII y cambie las páginas de códigos.
Otra herramienta útil es el programa conv.exe. Este programa puede convertir fácilmente un flujo de datos desde cualquier página de códigos a cualquier otra. Su valor predeterminado es la entrada en la página de códigos de Windows y la salida en la página de códigos de la consola actual. Esto permite ver correctamente los datos generados por las aplicaciones GUI de Windows (ej .: Bloc de notas) en una consola de comandos, con un comando simple como:
type WINFILE.txt | conv¡Esta biblioteca MsvcLibX no está completa, y las contribuciones para mejorarla son bienvenidas!
fuente
En Java utilicé la codificación "IBM850" para escribir el archivo. Eso resolvió el problema.
fuente