Да, это расстраивает - иногда type
и другие программы
печатать бред, а иногда нет.
Прежде всего, символы Unicode будут отображаться только , если
текущий шрифт консоли содержит символы . Так что используйте
шрифт TrueType, например Lucida Console, вместо стандартного шрифта Raster.
Но если шрифт консоли не содержит символа, который вы пытаетесь отобразить,
вы увидите знаки вопроса вместо тарабарщины. Когда вы получаете тарабарщину,
там больше, чем просто настройки шрифтов.
Когда программы используют стандартные функции ввода-вывода C-библиотеки, такие как printf
, ,
выходная кодировка программы должна соответствовать выходной кодировке консоли , или
вы получите бред. chcp
показывает и устанавливает текущую кодовую страницу. Все
Вывод с использованием стандартных функций ввода-вывода библиотеки C обрабатывается так, как если бы он находился в
кодовая страница отображается как chcp
.
Сопоставление выходной кодировки программы с выходной кодировкой консоли
может быть достигнуто двумя различными способами:
Программа может получить текущую кодовую страницу консоли, используя chcp
или
GetConsoleOutputCP
и сконфигурируйте себя для вывода в этой кодировке, или
Вы или программа можете установить текущую кодовую страницу консоли, используя chcp
или
SetConsoleOutputCP
для соответствия выходной кодировке программы по умолчанию.
Однако программы, использующие Win32 API, могут напрямую писать строки UTF-16LE
на консоль с
WriteConsoleW
.
Это единственный способ получить правильный вывод без установки кодовых страниц. А также
даже при использовании этой функции, если строка не в кодировке UTF-16LE
для начала программа Win32 должна передать правильную кодовую страницу
MultiByteToWideChar
.
Кроме того, WriteConsoleW
не будет работать, если вывод программы перенаправлен;
в этом случае нужно больше возиться.
type
работает иногда, потому что проверяет начало каждого файла на
UTF-16LE метка порядка байтов
(Спецификация) , то есть байты 0xFF 0xFE
.
Если он найдет такой
Отметьте, что отображает символы Unicode в файле, используя WriteConsoleW
независимо от текущей кодовой страницы. Но при type
использовании любого файла без
UTF-16LE BOM или для использования не-ASCII символов с любой командой
который не вызывает WriteConsoleW
- вам нужно будет установить
Кодовая страница консоли и выходная кодировка программы соответствуют друг другу.
Как мы можем это выяснить?
Вот тестовый файл, содержащий символы Юникода:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Вот Java-программа для распечатки тестового файла в куче разных
Unicode кодировки. Это может быть на любом языке программирования; это только печатает
ASCII-символы или закодированные байты до 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();
}
}
}
}
Вывод в кодовой странице по умолчанию? Всего мусора!
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}
Однако, что если мы type
сохранили файлы? Они содержат точные
те же байты, которые были напечатаны на консоли.
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 õ¢áÕÑ¢
Работает only - это файл UTF-16LE с спецификацией, напечатанной на
консоль через type
.
Если мы используем что-либо кроме type
для печати файла, мы получаем мусор:
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.
Из-за того, что copy CON
не отображает Unicode правильно, мы можем
сделать вывод, что команда type
имеет логику для обнаружения спецификации UTF-16LE на
начало файла и используйте специальные API-интерфейсы Windows для его печати.
Мы можем увидеть это, открыв cmd.exe
в отладчике, когда он переходит к type
из файла:
После того, как type
открывает файл, он проверяет спецификацию 0xFEFF
, т.е. байты
0xFF 0xFE
в порядке с прямым порядком байтов - и если есть такая спецификация, type
устанавливает
внутренний fOutputUnicode
флаг. Этот флаг проверяется позже, чтобы решить
стоит ли звонить WriteConsoleW
.
Но это единственный способ заставить type
выводить Unicode и только для файлов
которые имеют спецификации и находятся в UTF-16LE. Для всех остальных файлов и программ
которые не имеют специального кода для обработки вывода на консоль, ваши файлы будут
интерпретируется в соответствии с текущей кодовой страницей и, скорее всего, будет отображаться как
тарабарщина.
Вы можете подражать хоw type
выводит Unicode на консоль в ваших собственных программах, например:
#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;
}
Эта программа работает для печати Unicode на консоли Windows, используя
кодовая страница по умолчанию.
Для примера программы на Java мы можем получить немного правильного вывода:
установка кодовой страницы вручную, хотя вывод путается странным образом:
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
…
Однако, программа на C, которая устанавливает кодовую страницу 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;
}
имеет правильный вывод:
Z:\andrew\projects\sx\1259084>.\test
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Мораль этой истории?
type
может печатать файлы UTF-16LE с спецификацией независимо от вашей текущей кодовой страницы
- Программы Win32 могут быть запрограммированы для вывода Unicode на консоль, используя
WriteConsoleW
.
- Другие программы, которые устанавливают кодовую страницу и соответственно корректируют свою выходную кодировку, могут печатать Unicode на консоли независимо от того, какая кодовая страница была при запуске программы
- Для всего остального вам придется возиться с
chcp
, и, вероятно, все равно будет получаться странный вывод.