¿Qué página de codificación / código está usando cmd.exe?

271

Cuando abro cmd.exe en Windows, ¿qué codificación está usando?

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

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

Danglund
fuente

Respuestas:

389

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:

ingrese la descripción de la imagen aquí

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.
andrewdotn
fuente
73
Whoa, esta debe ser la respuesta más detallada que he visto en SO. ¡Crédito extra por las impresiones de desmontaje y las habilidades multilingües! Simplemente hermoso, señor!
ataque aéreo
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. Consulte stackoverflow.com/a/9051543 y stackoverflow.com/a/12015918 , y 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 comprenden completamente a primera vista. Si andrewdotn pudiera actualizar su respuesta con cualquier observación sobre _setmode (fd, _O_U16TEXT), eso sería genial.
JasDev
13
Si bien esta es una excelente respuesta, es engañoso decir que la consola es compatible con 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 utiliza un WCHAR de 16 bits por celda de carácter. Un par sustituto UTF-16 se imprime como dos caracteres de cuadro.
Eryk dom
3
@ user200783, no se admite el formulario descompuesto; por lo general, uno puede transformarse en un equivalente NFC. Además, la consola en las configuraciones regionales occidentales no permite mezclar glifos de ancho completo y medio ancho. Además, cuando se usa la página de códigos 65001 (UTF-8), antes de Windows 8 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 llamar WideCharToMultiByte.
Eryk dom
2
Los programas de demostración simples en esta respuesta asumen que GetStdHandle(STD_OUTPUT_HANDLE)y C stdoutson controladores de consola. En la práctica, para probar una consola, verifique que GetConsoleModetenga é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 incluye NULentre otros. En su lugar, llame _get_osfhandley verifique el identificador directamente.
Eryk dom
29

Tipo

chcp

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

Utilizar

nlsinfo

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.

Cagdas Altinkaya
fuente
19
Curiosamente, nlsinfono parece existir en mi Windows 7.
Joey
2
nlsinfotampoco existe en mi máquina con Windows XP SP3.
Thomas Owens
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.
Cagdas Altinkaya
Ah, eso explica por qué está allí en mi máquina Vista, donde instalé esos.
Joey
44
nlsinfotampoco existe en la máquina con Windows 10E.
Yousha Aleayoub
21

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.

Brian Agnew
fuente
13
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 totalmente la codificación.
danglund
5

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.

Rocío
fuente
55
CHCP no muestra solo 3 dígitos ni está en el formato 8 ##. 437 es, por ejemplo, una codificación de EE. UU., Y es el estándar de facto en los sistemas ingleses. - 65001 es una codificación Unicode (si no recuerdo mal, 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.)
Adam LS
4

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:

  • Fuentes C codificadas en UTF-8, utilizando cadenas char [] C normales 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 rutina main () argv [], con entradas y salidas estándar convertidas automáticamente a la página de códigos correcta.
  • Todas las funciones de archivo stdio.h admiten nombres de ruta UTF-8> 260 caracteres, hasta 64 KBytes en realidad.
  • Las mismas fuentes pueden compilarse y vincularse exitosamente en Windows usando Visual C ++ y MsvcLibX y la biblioteca Visual C ++ C, y en Linux usando gcc y la biblioteca estándar C de Linux, sin necesidad de bloques #ifdef ... #endif.
  • Las adiciones incluyen archivos comunes en Linux, pero que faltan en Visual C ++. Ej: unistd.h
  • Agrega funciones faltantes, como las de E / S de directorio, administración de enlaces simbólicos, etc., todo con soporte UTF-8, por supuesto :-).

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!

Jean-François Larvoire
fuente
2

En Java utilicé la codificación "IBM850" para escribir el archivo. Eso resolvió el problema.

Neumi
fuente