288 votes

Quel encodage/page de code cmd.exe utilise-t-il ?

Lorsque j'ouvre cmd.exe dans Windows, quel encodage utilise-t-il ?

Comment puis-je vérifier quel encodage est actuellement utilisé ? Cela dépend-il de mes paramètres régionaux ou y a-t-il des variables d'environnement à vérifier ?

Que se passe-t-il lorsque vous tapez un fichier avec un certain encodage ? Parfois, j'obtiens des caractères déformés (encodage incorrect utilisé) et parfois, cela fonctionne plus ou moins bien. Cependant, je ne fais confiance à rien tant que je ne sais pas ce qui se passe. Quelqu'un peut-il m'expliquer ?

424voto

andrewdotn Points 9183

Oui, c'est frustrant, parfois. type et d'autres programmes impriment du charabia, et parfois non.

Tout d'abord, les caractères Unicode s'afficheront uniquement si la police de la console actuelle contient les caractères . Utilisez donc une police TrueType comme Lucida Console au lieu de la police Raster par défaut.

Mais si la police de la console ne contient pas le caractère que vous essayez d'afficher, vous verrez des points d'interrogation au lieu du charabia. Quand vous obtenez du charabia, il y a plus que les paramètres de la police.

Lorsque les programmes utilisent des fonctions d'E/S standard de la bibliothèque C comme printf , le site L'encodage de sortie du programme doit correspondre à l'encodage de sortie de la console. ou vous obtiendrez du charabia. chcp affiche et définit la page de code actuelle. Tout sortie utilisant les fonctions d'E/S standard de la bibliothèque C est traitée comme si elle se trouvait dans la page de code affichée par le programme. page de code affichée par chcp .

La correspondance entre l'encodage de sortie du programme et l'encodage de sortie de la console peut être réalisée de deux manières différentes :

  • Un programme peut obtenir la page de code actuelle de la console en utilisant chcp ou GetConsoleOutputCP et se configurer pour sortir dans cet encodage, ou bien

  • Vous ou un programme pouvez définir la page de code actuelle de la console à l'aide de la commande chcp ou SetConsoleOutputCP pour correspondre à l'encodage de sortie par défaut du programme.

Cependant, les programmes qui utilisent les API Win32 peuvent écrire des chaînes UTF-16LE directement sur la console avec WriteConsoleW . C'est la seule façon d'obtenir un résultat correct sans avoir à définir des pages de code. Et même en utilisant cette fonction, si une chaîne de caractères n'est pas dans l'encodage UTF-16LE pour commencer, un programme Win32 doit passer le codepage correct à la fonction MultiByteToWideChar . Aussi, WriteConsoleW ne fonctionnera pas si la sortie du programme est redirigée ; Dans ce cas, il faudra faire plus d'efforts.

type fonctionne parfois car il vérifie le début de chaque fichier pour y trouver un UTF-16LE Marque de commande d'octets (BOM) c'est-à-dire les octets 0xFF 0xFE . S'il trouve une telle il affiche les caractères Unicode du fichier à l'aide de la fonction WriteConsoleW indépendamment de la page de code actuelle. Mais lorsque type pour tout fichier sans BOM UTF-16LE, ou pour l'utilisation de caractères non ASCII avec toute commande qui n'appelle pas WriteConsoleW -vous devrez définir la codepage de la console et le codage de sortie du programme pour qu'ils correspondent l'un à l'autre.


Comment pouvons-nous le savoir ?

Voici un fichier de test contenant des caractères Unicode :

ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    
Russian    
CJK       

Voici un programme Java qui permet d'imprimer le fichier de test dans un certain nombre de différents différents encodages Unicode. Il pourrait être dans n'importe quel langage de programmation ; il n'imprime que des caractères ASCII ou des octets codés à l'adresse suivante 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 sortie dans la page de code par défaut ? C'est de la merde !

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|DB
 R u s s i a n       0123456  MNO
 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|DB
 R u s s i a n       0123456  MNO
 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|DB
 R u s s i a n      0123456  MNO
 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|DB
 R u s s i a n      0123456  MNO
 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}

Cependant, et si nous type les fichiers qui ont été sauvegardés ? Ils contiennent exactement les mêmes octets qui ont été imprimés sur la console.

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|DB
 R u s s i a n      0123456  MNO
 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|DB
 R u s s i a n      0123456  MNO
 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|DB
 R u s s i a n       0123456  MNO
 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       õ¢áÕ

El sólo Ce qui fonctionne, c'est un fichier UTF-16LE, avec une nomenclature, qui est imprimé sur la console par le biais de l'interface de l'utilisateur. console via type .

Si nous utilisons autre chose que type pour imprimer le fichier, nous obtenons des déchets :

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|DB
 R u s s i a n       0123456  MNO
 C J K               `O}Y
         1 file(s) copied.

Du fait que copy CON n'affiche pas Unicode correctement, nous pouvons conclure que le type est dotée d'une logique permettant de détecter une BOM UTF-16LE au début du fichier, et utilise des API Windows spéciales pour l'imprimer.

Nous pouvons voir cela en ouvrant cmd.exe dans un débogueur lorsqu'il passe à type un fichier :

enter image description here

Après type ouvre un fichier, il vérifie la présence d'une nomenclature de 0xFEFF c'est-à-dire les octets 0xFF 0xFE en little-endian et s'il existe une telle nomenclature, type définit un interne fOutputUnicode drapeau. Ce drapeau est vérifié plus tard pour décider s'il faut appeler WriteConsoleW .

Mais c'est le seul moyen d'obtenir type pour sortir Unicode, et seulement pour les fichiers qui ont des nomenclatures et sont en UTF-16LE. Pour tous les autres fichiers, et pour les programmes qui n'ont pas de code spécial pour gérer la sortie console, vos fichiers seront interprétés en fonction de la page de code actuelle, et apparaîtront probablement comme du charabia.

Vous pouvez imiter la façon dont type envoie l'Unicode à la console dans vos propres programmes comme suit :

#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;
}

Ce programme permet d'imprimer du code Unicode sur la console Windows en utilisant la page de code par défaut. codepage par défaut.


Pour le programme Java de l'exemple, nous pouvons obtenir une sortie un peu correcte en en définissant manuellement la page de code, bien que la sortie soit brouillée de manière étrange :

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
…

Cependant, un programme C qui définit une page de code 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;
}

a une sortie correcte :

Z:\andrew\projects\sx\1259084>.\test
ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    
Russian    
CJK       

La morale de l'histoire ?

  • type peut imprimer des fichiers UTF-16LE avec une nomenclature, quelle que soit votre page de code actuelle.
  • Les programmes Win32 peuvent être programmés pour afficher le code Unicode sur la console, en utilisant les éléments suivants WriteConsoleW .
  • D'autres programmes qui définissent la page de code et ajustent leur codage de sortie en conséquence peuvent imprimer Unicode sur la console, quelle que soit la page de code utilisée au démarrage du programme.
  • Pour tout le reste, vous devrez utiliser les outils suivants chcp et vous obtiendrez probablement toujours des résultats bizarres.

84 votes

Whoa, ça doit être la réponse la plus détaillée que j'ai jamais vu sur SO. Un crédit supplémentaire pour les impressions de dissociation et les compétences multilingues ! Juste magnifique, monsieur !

2 votes

On peut également étudier l'extension spécifique à Microsoft _setmode(_fileno(stdout), _O_U16TEXT) qui a été introduite dans VS2008. Voir stackoverflow.com/a/9051543 y stackoverflow.com/a/12015918 y msdn.microsoft.com/fr/us/library/tw4k6df8(v=vs.90).aspx Outre les différences évidentes de portabilité entre _setmode() et SetConsoleOutputCP(), il peut également y avoir d'autres subtilités et effets secondaires cachés dans les deux approches qui ne sont pas entièrement compris à première vue. Si andrewdotn pouvait mettre à jour sa réponse avec toute observation sur _setmode(fd,_O_U16TEXT), ce serait formidable.

14 votes

Bien que ce soit une excellente réponse, il est trompeur de dire que la console prend en charge l'UTF-16. Elle est limitée à UCS-2, c'est-à-dire limitée aux caractères du plan multilingue de base (BMP). Lorsque le serveur de console Win32 (conhost.exe, de nos jours) a été conçu vers 1990, Unicode était une norme 16 bits, de sorte que le tampon d'écran de la console utilise un WCHAR 16 bits par cellule de caractère. Une paire de substituts UTF-16 s'imprime comme deux caractères de case.

36voto

Cagdas Altinkaya Points 839

Type

chcp

pour voir votre page de code actuelle (comme Dewfy l'a déjà dit).

Utilisez

nlsinfo

pour voir toutes les pages de code installées et découvrir ce que signifie votre numéro de page de code.

Le kit de ressources de Windows Server 2003 doit être installé (fonctionne sous Windows XP) pour pouvoir utiliser les services suivants nlsinfo .

21 votes

C'est intéressant, nlsinfo ne semble pas exister sur mon Windows 7.

2 votes

nlsinfo n'existe pas non plus sur ma machine Windows XP SP3.

2 votes

Oh, je suis désolé. Je pense qu'il est livré avec les outils du kit de ressources Windows Server. Je l'ai utilisé quelques fois sur ma machine Windows XP SP3 auparavant et je ne savais pas qu'il n'était pas installé par défaut.

22voto

Brian Agnew Points 143181

Pour répondre à votre deuxième question sur le fonctionnement de l'encodage, Joel Spolsky a écrit un excellent article intitulé article d'introduction sur ce . Fortement recommandé.

14 votes

Je l'ai lu et je le sais. Cependant, sous Windows, je me sens toujours perdu car le système d'exploitation et la plupart des applications semblent totalement ignorants de l'encodage.

6voto

Dewfy Points 11277

La commande CHCP indique la page de code actuelle. Elle comporte trois chiffres : 8xx et est différente de Windows 12xx. Ainsi, en tapant un texte en anglais seulement, vous ne verrez aucune différence, mais une page de code étendue (comme le cyrillique) sera mal imprimée.

7 votes

CHCP n'affiche pas seulement 3 chiffres et n'est pas non plus dans le format 8##. 437 est par exemple un encodage américain, et c'est la norme de facto sur les systèmes anglais. -- 65001 est un encodage Unicode (si je me souviens bien, il est UTF-8 et 65000 est UTF-7) et peut être choisi. CMD permet également de passer à la page de code 1250 par exemple, mais je ne sais pas depuis quand ces pages de code sont sélectionnables. (C'est sous Win7).

6voto

Je suis frustré depuis longtemps par les problèmes de pages de code de Windows, et les problèmes de portabilité et de localisation des programmes C qu'ils entraînent. Les messages précédents ont longuement détaillé ces problèmes, je ne vais donc pas ajouter quoi que ce soit à cet égard.

Pour faire court, j'ai fini par écrire ma propre couche de bibliothèque de compatibilité UTF-8 sur la bibliothèque C standard de Visual C++. En gros, cette bibliothèque garantit qu'un programme C standard fonctionne correctement, dans n'importe quelle page de code, en utilisant UTF-8 en interne.

Cette bibliothèque, appelée MsvcLibX, est disponible en source ouverte à l'adresse suivante https://github.com/JFLarvoire/SysToolsLib . Caractéristiques principales :

  • Sources C codées en UTF-8, utilisant des chaînes C char[] normales et les API standard de la bibliothèque C.
  • Dans toute page de code, tout est traité en interne comme UTF-8 dans votre code, y compris la routine main() argv[], avec l'entrée et la sortie standard automatiquement converties dans la bonne page de code.
  • Toutes les fonctions du fichier stdio.h supportent les noms de chemin UTF-8 > 260 caractères, jusqu'à 64 KBytes en réalité.
  • Les mêmes sources peuvent compiler et lier avec succès sous Windows en utilisant Visual C++ et MsvcLibX et la bibliothèque C de Visual C++, et sous Linux en utilisant gcc et la bibliothèque C standard de Linux, sans avoir besoin de blocs #ifdef .... #endif.
  • Ajoute des fichiers include communs à Linux, mais manquants dans Visual C++. Ex : unistd.h
  • Ajoute les fonctions manquantes, comme celles pour les entrées/sorties de répertoires, la gestion des liens symboliques, etc, le tout avec le support UTF-8 bien sûr :-).

Plus de détails dans le MsvcLibX README sur GitHub y compris comment construire la bibliothèque et l'utiliser dans vos propres programmes.

El section de libération dans le dépôt GitHub ci-dessus fournit plusieurs programmes utilisant cette bibliothèque MsvcLibX, qui montreront ses capacités. Ex : Essayez mon outil which.exe avec des répertoires avec des noms non-ASCII dans le PATH, en recherchant des programmes avec des noms non-ASCII, et en changeant les pages de code.

Un autre outil utile est le programme conv.exe. Ce programme peut facilement convertir un flux de données de n'importe quelle page de code en n'importe quelle autre. Par défaut, l'entrée se fait dans la page de code de Windows, et la sortie dans la page de code de la console actuelle. Cela permet de visualiser correctement les données générées par les applications Windows GUI (ex : Notepad) dans une console de commande, avec une simple commande comme : type WINFILE.txt | conv

Cette bibliothèque MsvcLibX n'est en aucun cas complète, et les contributions pour l'améliorer sont les bienvenues !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X