Có, nó bực bội — đôi khi type
và các chương trình khác in vô nghĩa và đôi khi không.
Trước hết, các ký tự Unicode sẽ chỉ hiển thị if the current console font contains the characters. Vì vậy, hãy sử dụng phông chữ TrueType như Bảng điều khiển Lucida thay vì Phông chữ Raster mặc định.
Nhưng nếu phông chữ bảng điều khiển không chứa ký tự bạn đang cố gắng hiển thị, bạn sẽ thấy dấu chấm hỏi thay vì vô nghĩa. Khi bạn nhận được vô nghĩa, sẽ có nhiều điều xảy ra hơn là cài đặt phông chữ.
Khi các chương trình sử dụng các chức năng tiêu chuẩn C-thư viện I/O như printf
, mã hóa đầu ra chương trình của phải phù hợp với mã hóa đầu ra của giao diện điều khiển, hoặc bạn sẽ nhận được vô nghia. chcp
hiển thị và đặt mã hiện tại. Tất cả các đầu ra sử dụng các hàm I/O C-library chuẩn được xử lý như thể nó nằm trong mã trang được hiển thị bởi chcp
.
Matching mã hóa đầu ra của chương trình với mã hóa đầu ra của giao diện điều khiển thể được thực hiện theo hai cách khác nhau:
Một chương trình có thể nhận được bảng mã hiện tại của giao diện điều khiển sử dụng chcp
hoặc GetConsoleOutputCP
, và cấu hình riêng của mình để đầu ra trong mã hóa đó hoặc
Bạn hoặc chương trình có thể đặt bảng mã hiện tại của bảng điều khiển bằng cách sử dụng chcp
hoặc SetConsoleOutputCP
để khớp mã hóa đầu ra mặc định của chương trình.
Tuy nhiên, các chương trình sử dụng các API Win32 có thể viết UTF-16LE chuỗi trực tiếp ra cửa sổ Console với WriteConsoleW
. Đây là cách duy nhất để có được kết quả đầu ra chính xác mà không cần cài đặt các bản mã. Và ngay cả khi sử dụng chức năng đó, nếu chuỗi không nằm trong mã hóa UTF-16LE để bắt đầu, chương trình Win32 phải chuyển đúng trang mã tới MultiByteToWideChar
. Ngoài ra, WriteConsoleW
sẽ không hoạt động nếu đầu ra của chương trình được chuyển hướng; Cần có thêm không quan trọng trong trường hợp đó.
type
làm việc một số thời gian vì nó sẽ kiểm tra sự bắt đầu của mỗi tập tin cho UTF-16LE Byte Order Mark (BOM), ví dụ: các byte 0xFF 0xFE
. Nếu nó tìm thấy một dấu hiệu như vậy , nó sẽ hiển thị các ký tự Unicode trong tệp bằng cách sử dụng WriteConsoleW
bất kể mã hiện tại. Nhưng khi type
nhập bất kỳ tệp nào không có UTF-16LE BOM hoặc để sử dụng các ký tự không phải ASCII với bất kỳ lệnh nào không gọi WriteConsoleW
—bạn sẽ cần đặt mã bảng điều khiển và mã hóa chương trình.
Làm cách nào chúng tôi có thể tìm ra điều này?
Đây là một tập tin thử nghiệm có chứa các ký tự Unicode:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Dưới đây là một chương trình Java để in ra các tập tin thử nghiệm trong một loạt các khác nhau bảng mã Unicode. Nó có thể bằng bất kỳ ngôn ngữ lập trình nào; nó chỉ in ký tự ASCII hoặc byte được mã hóa thành 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();
}
}
}
}
Đầu ra trong bảng mã mặc định? Tổng lượng rác!
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}
Tuy nhiên, những gì nếu chúng ta type
các tập tin đã lưu? Chúng chứa chính xác cùng một byte đã được in trên bảng điều khiển.
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 õ¢áÕÑ¢
Các chỉ điều mà các công trình là tập tin UTF-16LE, với một BOM, in đến console qua type
.
Nếu chúng ta sử dụng bất cứ điều gì khác hơn là type
để in các tập tin, chúng tôi nhận rác:
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.
Từ thực tế là copy CON
không hiển thị Unicode một cách chính xác, chúng ta có thể kết luận rằng lệnh type
có logic để phát hiện một UTF-16LE BOM tại số bắt đầu của tệp và sử dụng các API Windows đặc biệt để in.
Chúng ta có thể thấy điều này bằng cách mở cmd.exe
trong một trình gỡ lỗi khi nó đi vào type
ra một tập tin:
Sau type
mở một tập tin, nó sẽ kiểm tra cho một BOM của 0xFEFF
-ie, các byte 0xFF 0xFE
ở cấp độ nhỏ — và nếu có BOM như vậy, type
đặt cờ nội bộ fOutputUnicode
. Cờ này được chọn sau để quyết định có nên gọi số WriteConsoleW
hay không.
Nhưng đó là cách duy nhất để có được type
để xuất Unicode và chỉ cho các tệp có BOM và ở dạng UTF-16LE. Đối với tất cả các tệp khác và đối với các chương trình không có mã đặc biệt để xử lý đầu ra của bảng điều khiển, tệp của bạn sẽ là được diễn giải theo trang mã hiện tại và có thể hiển thị là ngữ pháp.
Bạn có thể bắt chước cách type
đầu ra Unicode để giao diện điều khiển trong các chương trình của riêng bạn như vậy:
#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;
}
chương trình này hoạt động để in Unicode trên Windows giao diện điều khiển bằng cách sử dụng bảng mã mặc định.
Đối với chương trình Java mẫu, chúng ta có thể có được một chút đầu ra chính xác bằng cách thiết lập các bảng mã bằng tay, mặc dù sản lượng được điều sai lầm trong cách kỳ lạ:
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
…
Tuy nhiên, một C chương trình mà bộ một Unicode UTF-8 bảng mã:
#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;
}
không có đầu ra chính xác:
Z:\andrew\projects\sx\1259084>.\test
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Tinh thần của câu chuyện?
type
có thể in file UTF-16LE với BOM không phụ thuộc bảng mã hiện tại của bạn chương trình
- Win32 có thể được lập trình để đầu ra Unicode để giao diện điều khiển, sử dụng
WriteConsoleW
.
- Các chương trình khác mà thiết lập các bảng mã và điều chỉnh mã hóa đầu ra cho phù hợp có thể in Unicode trên giao diện điều khiển bất kể những gì các bảng mã là khi chương trình bắt đầu
- Đối với mọi thứ khác bạn sẽ phải mess xung quanh với
chcp
, và có thể sẽ vẫn có đầu ra lạ.
Whoa, đây phải là câu trả lời chi tiết nhất mà tôi từng thấy trên SO. Thêm tín dụng cho các bản in dissasembly và skillz đa ngôn ngữ! Chỉ đẹp thôi, thưa ngài! –
Người ta cũng có thể muốn nghiên cứu phần mở rộng cụ thể của Microsoft _setmode (_fileno (stdout), _O_U16TEXT) được giới thiệu trong VS2008. Xem http://stackoverflow.com/a/9051543 và http://stackoverflow.com/a/12015918 và http://msdn.microsoft.com/en-us/library/tw4k6df8(v=vs. 90) .aspx Bên cạnh sự khác biệt về tính di động rõ ràng giữa _setmode() và SetConsoleOutputCP(), cũng có thể có các sự tinh tế và tác dụng phụ khác ẩn trong cả hai cách tiếp cận không được hiểu rõ ngay từ cái nhìn đầu tiên. Nếu andrewdotn có thể cập nhật câu trả lời của mình với bất kỳ quan sát nào về _setmode (fd, _O_U16TEXT), điều đó thật tuyệt. – JasDev
Trong khi đây là một câu trả lời tuyệt vời, nó gây hiểu lầm khi nói rằng giao diện điều khiển hỗ trợ UTF-16. Nó được giới hạn trong UCS-2, tức là giới hạn đối với các ký tự trong mặt phẳng đa ngôn ngữ cơ bản (BMP). Khi máy chủ bàn điều khiển Win32 (conhost.exe, ngày nay) được thiết kế vào khoảng năm 1990, Unicode là chuẩn 16 bit, do đó bộ đệm màn hình bàn điều khiển sử dụng một WCHAR 16 bit cho mỗi ô ký tự. Cặp thay thế UTF-16 in dưới dạng hai ký tự hộp. – eryksun