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.
WriteFile
raportuje liczbę zapisanych znaków zamiast liczby bajtów, więc buforowani pisarze ponawiają kilka razy „pozostałe” bajty proporcjonalnie do liczby znaków spoza ASCII . Również w 65001 odczyt znaków spoza ASCII kończy się niepowodzeniem w pliku conhost.exe, ponieważ podczas wywoływania przyjmuje on 1 bajt ANSI na kod UTF-16WideCharToMultiByte
.GetStdHandle(STD_OUTPUT_HANDLE)
i Cstdout
są uchwytami konsoli. W praktyce, aby przetestować konsolę, sprawdź, czy sięGetConsoleMode
udało. Nie należy także używać_isatty
funkcji środowiska wykonawczego C do sprawdzania, czy deskryptor pliku niskiego We / Wy jest konsolą; sprawdza tylko urządzenie w trybie znakowym, które obejmujeNUL
między innymi. Zamiast tego zadzwoń_get_osfhandle
i sprawdź bezpośrednio uchwyt.Rodzaj
aby zobaczyć swoją aktualną stronę kodową (jak już powiedział Dewfy).
Posługiwać się
aby zobaczyć wszystkie zainstalowane strony kodowe i dowiedzieć się, co oznacza numer strony kodowej.
Aby móc korzystać, musisz mieć zainstalowany zestaw zasobów systemu Windows Server 2003 (działa w systemie Windows XP)
nlsinfo
.źródło
nlsinfo
wydaje się , że nie istnieje w moim systemie Windows 7.nlsinfo
również nie istnieje na moim komputerze z systemem Windows XP SP3.nlsinfo
również nie istnieje na komputerze z systemem Windows 10E.Aby odpowiedzieć na drugie zapytanie, ponownie. jak działa kodowanie, Joel Spolsky napisał na ten temat świetny artykuł wprowadzający . Zdecydowanie polecam.
źródło
Command CHCP pokazuje bieżącą stronę kodową. Ma trzy cyfry: 8xx i różni się od Windows 12xx. Więc wpisując tekst tylko w języku angielskim, nie zobaczysz żadnej różnicy, ale rozszerzona strona kodowa (jak cyrylica) zostanie wydrukowana nieprawidłowo.
źródło
Długo byłem sfrustrowany problemami ze stroną kodową Windows oraz powodowanymi przez nie problemami z przenośnością i lokalizacją programów C. Poprzednie posty szczegółowo opisywały problemy, więc nie zamierzam niczego dodawać w tym zakresie.
Krótko mówiąc, w końcu napisałem własną warstwę biblioteki kompatybilności UTF-8 nad standardową biblioteką C Visual C ++. Zasadniczo ta biblioteka zapewnia, że standardowy program C działa poprawnie, na dowolnej stronie kodowej, przy użyciu UTF-8 wewnętrznie.
Ta biblioteka o nazwie MsvcLibX jest dostępna jako open source na https://github.com/JFLarvoire/SysToolsLib . Główne cechy:
Więcej informacji w pliku MsvcLibX README na GitHub , w tym jak zbudować bibliotekę i używać jej we własnych programach.
Sekcja wydania w powyższym repozytorium GitHub zawiera kilka programów korzystających z tej biblioteki MsvcLibX, które pokażą jej możliwości. Przykład: Wypróbuj moje narzędzie which.exe z katalogami o nazwach innych niż ASCII w ścieżce PATH, wyszukując programy o nazwach innych niż ASCII i zmieniając strony kodowe.
Kolejnym przydatnym narzędziem jest program conv.exe. Ten program może łatwo konwertować strumień danych z dowolnej strony kodowej na dowolną inną. Domyślnie jest on wprowadzany na stronie kodowej Windows i wyprowadzany na bieżącej stronie kodowej konsoli. Umożliwia to prawidłowe przeglądanie danych generowanych przez aplikacje Windows GUI (np. Notatnik) w konsoli poleceń za pomocą prostego polecenia, takiego jak:
type WINFILE.txt | conv
Ta biblioteka MsvcLibX w żadnym wypadku nie jest kompletna, a wkład w jej ulepszanie jest mile widziany!
źródło
W Javie użyłem kodowania „IBM850” do napisania pliku. To rozwiązało problem.
źródło