Да, это расстраивает - иногда type
и другие программы печатать тарабарщину, а иногда и нет.
Прежде всего, символы Юникода будут отображаться только if the current console font contains the characters. Поэтому используйте шрифт TrueType, например Lucida Console, вместо стандартного растрового шрифта.
Но если шрифт консоли не содержит символ, который вы пытаетесь отобразить, вы увидите вопросительные знаки вместо тарабарщины. Когда вы получаете тарабарщину, происходит больше, чем только настройки шрифта.
Когда программы используют стандартные функции C-библиотеку ввода/вывод, как printf
, выход кодирование в программах должно соответствовать выходной кодировке консоли или вы получите тарабарщину. chcp
показывает и устанавливает текущую кодовую страницу. Все выходные данные с использованием стандартных функций ввода-вывода C-библиотеки обрабатываются так, как если бы они находились в кодовой странице , отображаемой chcp
.
Matching выходного кодирования программы с выходом кодировкой консоли может быть осуществлено двумя различными способами:
Программа может получить текущую кодовую в консоли с помощью chcp
или GetConsoleOutputCP
и настроить себя на выход в что кодирование или
Вы или программа может установить текущую кодовую в консоли с помощью chcp
или SetConsoleOutputCP
, чтобы соответствовать стандартная выходная кодировка программы.
Однако программы, использующие API-интерфейсы Win32 могут написать UTF-16LE строки непосредственно на консоль с WriteConsoleW
. Это единственный способ получить правильный вывод без установки кодовых страниц. И даже при использовании этой функции, если строка не в кодировке UTF-16LE для начала, программа Win32 должна передать правильную кодовую страницу до MultiByteToWideChar
. Кроме того, WriteConsoleW
не будет работать, если выход программы перенаправлен; В этом случае необходимо больше возиться.
type
работает некоторое время, потому что он проверяет начало каждого файла для в кодировке UTF-16LE Byte Order Mark (BOM), т.е. байт 0xFF 0xFE
. Если он находит такой знак , он отображает символы Unicode в файле, используя WriteConsoleW
независимо от текущей кодовой страницы. Но когда type
в любом файле без спецификации UTF-16LE или для использования символов, отличных от 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 õ¢áÕÑ¢
только вещь, которая работает на файл UTF-16LE, с BOM, выводимого на консоль через 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 BOM в начале файла и использовать специальные API Windows для его печати.
Мы можем увидеть это, открыв cmd.exe
в отладчике, когда он идет в type
из файла:
После type
открывает файл, он проверяет наличие BOM в 0xFEFF
-ie, то байты 0xFF 0xFE
в little-endian-и если есть такая спецификация, type
устанавливает внутренний fOutputUnicode
флаг. Этот флаг проверяется позже, чтобы принять решение о том, следует ли позвонить WriteConsoleW
.
Но это единственный способ получить type
для вывода Unicode и только для файлов , которые имеют спецификации и находятся в UTF-16LE. Для всех других файлов и для программ , у которых нет специального кода для обработки вывода консоли, ваши файлы будут , интерпретированные в соответствии с текущей кодовой страницей, и, вероятно, будут отображаться как gibberish.
Вы можете эмулировать как 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 файлы с BOM, независимо от текущей кодовой страницы программы
- Win32 могут быть запрограммированы для вывода Unicode в консоль, используя
WriteConsoleW
.
- Других программ, которые устанавливают кодовые и настроить их кодировку выходных данных соответственно может печатать Unicode на консоли независимо от того, что кодового был, когда программа начала
- Для всего остального вам придется возиться с
chcp
, и, вероятно, до сих пор получить странный вывод.
Эй, это должен быть самый подробный ответ, который я когда-либо видел на SO. Дополнительный кредит на рассыпные отпечатки и многоязычные навыки! Просто красиво, сэр! –
Можно также изучить расширение _setmode для Microsoft (_fileno (stdout), _O_U16TEXT), которое было представлено в VS2008. См. Http://stackoverflow.com/a/9051543 и http://stackoverflow.com/a/12015918 и http://msdn.microsoft.com/en-us/library/tw4k6df8(v=vs. 90) .aspx Помимо очевидных различий в переносимости между _setmode() и SetConsoleOutputCP(), в обоих подходах могут быть и другие тонкости и побочные эффекты, которые на первый взгляд не полностью понятны. Если andrewdotn может обновить свой ответ с любыми наблюдениями о _setmode (fd, _O_U16TEXT), это было бы здорово. – JasDev
Хотя это отличный ответ, неверно сказать, что консоль поддерживает UTF-16. Он ограничен UCS-2, то есть ограничен символами базовой многоязычной плоскости (BMP). Когда консольный сервер Win32 (conhost.exe, в настоящее время) был спроектирован около 1990 года, Unicode был 16-битным стандартом, поэтому буфер экрана консоли использует одну 16-разрядную WCHAR для ячейки символа. Параметр суррогатной пары UTF-16 печатается как два символа. – eryksun