Ответ 1
Да, его разочарование, иногда type
и другие программы
печатать тарабарщину, а иногда и нет.
Прежде всего, символы Unicode будут отображаться если текущий шрифт консоли содержит символы. Поэтому используйте шрифт TrueType, например Lucida Console, вместо стандартного растрового шрифта.
Но если шрифт консоли не содержит символ, который вы пытаетесь отобразить, вы увидите вопросительные знаки вместо тарабарщины. Когда вы получаете тарабарщину, theres больше происходит, чем просто настройки шрифта.
Когда в программах используются стандартные функции ввода-вывода C-библиотеки, такие как printf
,
кодирование вывода программ должно соответствовать кодировке вывода консолей или
вы получите тарабарщину. chcp
показывает и устанавливает текущую кодовую страницу. Все
вывод с использованием стандартных функций ввода-вывода C-библиотеки обрабатывается так, как если бы он находился в
кодовая страница отображается chcp
.
Соответствие программному кодированию вывода с кодировкой вывода консолей может выполняться двумя разными способами:
-
Программа может получить текущую кодовую страницу консоли с помощью
chcp
илиGetConsoleOutputCP
и настроить себя для вывода в этой кодировке или -
Вы или программа можете установить текущую кодовую страницу консоли с помощью
chcp
илиSetConsoleOutputCP
, чтобы соответствовать выходной кодировке программы по умолчанию.
Однако программы, использующие API Win32, могут напрямую писать строки UTF-16LE
на консоль с
WriteConsoleW
.
Это единственный способ получить правильный вывод без установки кодовых страниц. А также
даже при использовании этой функции, если строка не содержится в кодировке UTF-16LE
для начала, программа Win32 должна передать правильную кодовую страницу
MultiByteToWideChar
.
Кроме того, WriteConsoleW
не будет работать, если выход программ будет перенаправлен;
в этом случае необходимо больше возиться.
type
работает некоторое время, потому что он проверяет начало каждого файла для
UTF-16LE Знак байтового байта
(BOM), то есть байты 0xFF 0xFE
.
Если он найдет такое
mark, он отображает символы Unicode в файле с помощью WriteConsoleW
независимо от текущей кодовой страницы. Но когда type
с любым файлом без
UTF-16LE BOM или для использования символов, отличных от ASCII, с любой командой
который не вызывает WriteConsoleW
, вам нужно будет установить
кодовая страница консоли и кодирование выходного кода программы, чтобы соответствовать друг другу.
Как мы можем это узнать?
Вот тестовый файл, содержащий символы Unicode:
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 õ¢áÕÑ¢
Единственное, что работает, это файл 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
-i.e., байты
0xFF 0xFE
в little-endian-и если есть такая спецификация, type
устанавливает
внутренний флаг fOutputUnicode
. Этот флаг проверяется позже, чтобы принять решение
вызов WriteConsoleW
.
Но это единственный способ получить type
для вывода Unicode и только для файлов
которые имеют спецификации и находятся в UTF-16LE. Для всех других файлов и для программ
у которых нет специального кода для обработки вывода консоли, ваши файлы будут
интерпретируется в соответствии с текущей кодовой страницей и, вероятно, будет отображаться как
бред.
Вы можете эмулировать, как 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
и, вероятно, все равно получите странный результат.