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 :
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.