Tak, to frustrujące - czasami type
inne programy drukują bełkot, a czasem nie.
Przede wszystkim znaki Unicode będą wyświetlane tylko wtedy, gdy bieżąca czcionka konsoli zawiera te znaki . Użyj więc czcionki TrueType, takiej jak Lucida Console, zamiast domyślnej czcionki rastrowej.
Ale jeśli czcionka konsoli nie zawiera znaku, który próbujesz wyświetlić, zamiast bełkotu zobaczysz znaki zapytania. Kiedy pojawia się bełkot, dzieje się coś więcej niż tylko ustawienia czcionek.
Gdy programy używają standardowych funkcji we / wy biblioteki C printf
, takich jak , kodowanie wyjściowe programu musi być zgodne z kodowaniem wyjściowym konsoli , w przeciwnym razie otrzymasz bełkot. chcp
pokazuje i ustawia bieżącą stronę kodową. Wszystkie dane wyjściowe przy użyciu standardowych funkcji we / wy biblioteki C są traktowane tak, jakby znajdowały się na stronie kodowej wyświetlanej przez chcp
.
Dopasowanie kodowania wyjściowego programu do kodowania wyjściowego konsoli można wykonać na dwa różne sposoby:
Program może pobrać bieżącą stronę kodową konsoli za pomocą chcp
lub
GetConsoleOutputCP
i skonfigurować się tak, aby wyświetlać w tym kodowaniu, lub
Ty lub program możesz ustawić bieżącą stronę kodową konsoli za pomocą chcp
lub,
SetConsoleOutputCP
aby dopasować domyślne kodowanie wyjściowe programu.
Jednak programy używające interfejsów API Win32 mogą zapisywać ciągi UTF-16LE bezpośrednio na konsoli za pomocą
WriteConsoleW
. Jest to jedyny sposób na uzyskanie prawidłowego wyniku bez ustawiania stron kodowych. I nawet podczas korzystania z tej funkcji, jeśli ciąg znaków nie znajduje się w kodowaniu UTF-16LE na początek, program Win32 musi przekazać poprawną stronę kodową
MultiByteToWideChar
. Również,WriteConsoleW
Nie zadziała jeśli wyjście programu zostanie przekierowane; w takim przypadku potrzebne jest więcej zabawy.
type
działa przez pewien czas, ponieważ sprawdza, czy na początku każdego pliku nie ma znaku kolejności bajtów UTF-16LE (BOM) , tj. bajtów 0xFF 0xFE
. Jeśli znajdzie taki znak, wyświetla znaki Unicode w pliku przy użyciu WriteConsoleW
niezależnie od bieżącej strony kodowej. Ale podczas type
wczytywania dowolnego pliku bez BOM UTF-16LE lub używania znaków spoza ASCII z dowolną komendą, która się nie wywołuje WriteConsoleW
, konieczne będzie ustawienie strony kodowej konsoli i kodowania wyjścia programu tak, aby były ze sobą zgodne.
Jak możemy się tego dowiedzieć?
Oto plik testowy zawierający znaki Unicode:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Oto program Java do wydrukowania pliku testowego w wielu różnych kodowaniach Unicode. Może być w dowolnym języku programowania; wypisuje tylko znaki ASCII lub zakodowane bajty 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();
}
}
}
}
Dane wyjściowe w domyślnej stronie kodowej? Całkowite śmieci!
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}
A co jeśli type
pliki, które zostały zapisane? Zawierają dokładnie te same bajty, które zostały wydrukowane na konsoli.
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 õ¢áÕÑ¢
Tylko rzeczą, która działa jest plik UTF-16LE, z BOM, drukowane do konsoli poprzez type
.
Jeśli użyjemy czegoś innego niż type
wydruk pliku, otrzymujemy śmieci:
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.
Z faktu, że copy CON
nie wyświetla poprawnie Unicode, możemy wywnioskować, że type
polecenie ma logikę wykrywania BOM UTF-16LE na początku pliku i użyj specjalnych interfejsów API Windows, aby go wydrukować.
Możemy to zobaczyć, otwierając cmd.exe
w debuggerze, gdy wychodzi type
plik:
Po type
otwiera plik, to sprawdza BOM z 0xFEFF
-ie, bajty
0xFF 0xFE
w ostrokońcej a jeśli istnieje taka BOM, type
ustawia wewnętrzną fOutputUnicode
flagą. Ta flaga jest później sprawdzana, aby zdecydować, czy zadzwonić WriteConsoleW
.
Ale to jedyny sposób, aby uzyskać type
wyjściowy Unicode i tylko dla plików, które mają BOM i są w UTF-16LE. W przypadku wszystkich innych plików i programów, które nie mają specjalnego kodu do obsługi danych wyjściowych konsoli, pliki zostaną zinterpretowane zgodnie z bieżącą stroną kodową i prawdopodobnie będą wyświetlane jako bełkot.
Możesz emulować sposób, w jaki type
wypisuje Unicode na konsolę w swoich własnych programach:
#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;
}
Ten program działa do drukowania Unicode na konsoli Windows przy użyciu domyślnej strony kodowej.
W przypadku przykładowego programu Java możemy uzyskać trochę poprawnego wyniku, ustawiając stronę kodową ręcznie, chociaż dane wyjściowe psują się w dziwny sposób:
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
…
Jednak program C, który ustawia stronę kodową 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;
}
ma poprawny wynik:
Z:\andrew\projects\sx\1259084>.\test
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Morał tej historii?
type
może drukować pliki UTF-16LE z BOM niezależnie od bieżącej strony kodowej
- Programy Win32 można zaprogramować do wyświetlania Unicode na konsoli za pomocą
WriteConsoleW
.
- Inne programy, które ustawiają stronę kodową i odpowiednio dostosowują kodowanie wyjściowe, mogą drukować Unicode na konsoli bez względu na to, jaka była strona kodowa podczas uruchamiania programu
- W przypadku wszystkiego innego będziesz musiał się zepsuć
chcp
i prawdopodobnie nadal będziesz mieć dziwne wyniki.