2009-08-11 27 views
228

Cuando abro cmd.exe en Windows, ¿qué codificación está usando?¿Qué página de codificación/código usa cmd.exe?

¿Cómo puedo verificar qué codificación está usando actualmente? ¿Depende de mi configuración regional o hay variables de entorno para verificar?

¿Qué sucede cuando escribe un archivo con una cierta codificación? A veces obtengo caracteres ilegibles (se usa una codificación incorrecta) y, a veces, funciona. Sin embargo, no confío en nada siempre que no sepa lo que está pasando. ¿Alguien puede explicar?

Respuesta

318

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:

enter image description here

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.
+48

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! –

+2

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

+9

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

20

Para responder a su segunda consulta re. cómo funciona la codificación, Joel Spolsky escribió un gran introductory article on this. Muy recomendado.

+12

Lo he leído y lo sé.Sin embargo, en Windows siempre me siento perdido porque el sistema operativo y la mayoría de las aplicaciones parecen ignorar la codificación. – danglund

5

Comando CHCP muestra la página de códigos actual. Tiene tres dígitos: 8xx y es diferente de Windows 12xx. Por lo tanto, si escribe un texto en inglés, no verá ninguna diferencia, pero una página de códigos extendida (como cirílico) se imprimirá incorrectamente.

+5

CHCP no muestra solo 3 dígitos ni tiene el formato 8 ##. 437 es, por ejemplo, una codificación de los EE. UU., Y es el estándar de facto en los sistemas ingleses. - 65001 es una codificación Unicode (si recuerdo bien, es UTF-8 y 65000 es UTF-7) y se puede elegir. También CMD permite cambiar a la página de códigos 1250, por ejemplo, pero no sé desde cuándo estas páginas de códigos son seleccionables. (Está bajo Win7.) –

21

Tipo

chcp 

para ver la página de códigos actual (como ya se dijo Dewfy).

Uso

nlsinfo 

para ver todas las páginas de códigos instalados y averiguar lo que significa que su número de página de códigos.

Necesita tener el kit de recursos de Windows Server 2003 instalado (funciona en Windows   XP) para usar nlsinfo.

+14

Curiosamente, 'nlsinfo' no parece existir en mi Windows 7. – Joey

+2

' nlsinfo' tampoco existe en mi máquina con Windows XP SP3. –

+2

Oh, lo siento. Creo que viene con las herramientas del Kit de recursos de Windows Server. Lo he usado un par de veces en mi máquina con Windows XP SP3 anteriormente y no sabía que no estaba instalado de manera predeterminada. –

1

Me he sentido frustrado por 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 al respecto.

Para resumir, eventualmente terminé escribiendo mi propia capa de biblioteca de compatibilidad UTF-8 sobre la biblioteca estándar C de Visual C++. Básicamente, esta biblioteca garantiza que un programa C estándar funcione correctamente, en cualquier página de códigos, usando UTF-8 internamente.

Esta biblioteca, llamada MsvcLibX, está disponible como código abierto en https://github.com/JFLarvoire/SysToolsLib. Características principales:

  • C fuentes codificadas en UTF-8, utilizando cadenas de caracteres normales [] C, y API de biblioteca C estándar.
  • En cualquier página de códigos, todo se procesa internamente como UTF-8 en su código, incluida la argv de rutina main(), con entrada y salida estándar convertidas automáticamente a la página de códigos correcta.
  • Todas las funciones del archivo stdio.h admiten nombres de ruta UTF-8> 260 caracteres, hasta 64 KBytes en realidad.
  • Las mismas fuentes pueden compilarse y vincularse con éxito en Windows utilizando la biblioteca Visual C++ y MsvcLibX y Visual C++ C, y en Linux utilizando la biblioteca C estándar de gcc y Linux, sin necesidad de bloques #ifdef ... #endif.
  • Agrega archivos de inclusión comunes en Linux, pero faltan en Visual C++. Ej: unistd.h
  • Agrega funciones faltantes, como las de E/S de directorio, administración simbólica de enlaces, etc., todas con soporte UTF-8 por supuesto :-).

Más detalles en el MsvcLibX README on GitHub, que incluyen cómo construir la biblioteca y usarla en sus propios programas.

El release section en el repositorio GitHub anterior proporciona varios programas utilizando esta biblioteca MsvcLibX, que mostrará sus capacidades. Ejemplo: Pruebe la 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 una secuencia de datos de cualquier página de códigos a cualquier otra. Su valor predeterminado se ingresa en la página de códigos de Windows y se imprime en la página de códigos de la consola actual. Esto permite visualizar correctamente los datos generados por las aplicaciones GUI de Windows (por ejemplo, el 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.