Sí, es frustrante, a veces type
y 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. chcp
muestra 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 chcp
o
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 chcp
o
SetConsoleOutputCP
para 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.
type
funciona 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 type
crear cualquier archivo sin una lista de materiales UTF-16LE, o para usar caracteres no ASCII con cualquier comando que no llame, 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í 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 type
los 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 type
para 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 CON
no muestra Unicode correctamente, podemos concluir que el type
comando 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.exe
en un depurador cuando va a type
un archivo:
Después de type
abrir un archivo, comprueba una lista de materiales de 0xFEFF
—es decir, los bytes
0xFF 0xFE
en little-endian— y, si existe dicha lista de materiales, type
establece un fOutputUnicode
indicador interno . Esta bandera se verifica más adelante para decidir si se debe llamar WriteConsoleW
.
Pero esa es la única forma de obtener type
unicode 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 type
salidas 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
chcp
y probablemente aún obtendrá resultados extraños.
WriteFile
informa 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 Cstdout
son controladores de consola. En la práctica, para probar una consola, verifique queGetConsoleMode
tenga éxito. Además, no use la_isatty
funció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 incluyeNUL
entre otros. En su lugar, llame_get_osfhandle
y 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
nlsinfo
no parece existir en mi Windows 7.nlsinfo
tampoco existe en mi máquina con Windows XP SP3.nlsinfo
tampoco 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