Có, đôi khi nó làm nản lòng type
và các chương trình khác in vô nghĩa, và đôi khi chúng không.
Trước hết, các ký tự Unicode sẽ chỉ hiển thị nếu phông chữ bảng điều khiển hiện tại có chứa các ký tự . 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ố hiển thị, bạn sẽ thấy các dấu hỏi thay vì vô nghĩa. Khi bạn cảm thấy vô nghĩa, sẽ có nhiều hơn là chỉ cài đặt phông chữ.
Khi các chương trình sử dụng các chức năng I / O của thư viện C tiêu chuẩn như printf
, mã hóa đầu ra của chương trình phải khớp với mã hóa đầu ra của bàn điều khiển , nếu không bạn sẽ bị sai lệch. chcp
hiển thị và thiết lập bảng mã hiện tại. Tất cả đầu ra sử dụng các chức năng I / O của thư viện C tiêu chuẩn được xử lý như thể nó nằm trong bảng mã được hiển thị bởi chcp
.
Việc kết hợp mã hóa đầu ra của chương trình với mã hóa đầu ra của bàn điều khiển có thể được thực hiện theo hai cách khác nhau:
Một chương trình có thể lấy bảng mã hiện tại của bàn điều khiển bằng cách sử dụng chcp
hoặc
GetConsoleOutputCP
, và định cấu hình chính nó để xuất ra mã hóa đó, hoặc
Bạn hoặc một chương trình có thể thiết lập bảng mã hiện tại của bàn điều khiển bằng cách sử dụng chcp
hoặc
SetConsoleOutputCP
để khớp với 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 API Win32 có thể ghi các chuỗi UTF-16LE trực tiếp vào bảng điều khiển với
WriteConsoleW
. Đây là cách duy nhất để có được đầu ra chính xác mà không cần thiết lập bảng mã. Và ngay cả khi sử dụng chức năng đó, nếu một chuỗi không có trong mã hóa UTF-16LE để bắt đầu, một chương trình Win32 phải chuyển mã chính xác đến
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; khó khăn hơn là cần thiết trong trường hợp đó.
type
hoạt động đôi khi vì nó kiểm tra sự bắt đầu của mỗi tệp cho Dấu thứ tự byte (BOM) UTF-16LE , tức là các byte 0xFF 0xFE
. Nếu nó tìm thấy một dấ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ể bảng mã hiện tại. Nhưng khi nhập type
bất kỳ tệp nào không có BOM UTF-16LE 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 thì WriteConsoleW
bạn sẽ cần phải thiết lập bảng mã điều khiển và mã hóa đầu ra chương trình để khớp với nhau.
Làm thế nào chúng ta có thể tìm ra điều này?
Đây là một tệp thử nghiệm chứa các ký tự Unicode:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Đây là một chương trình Java để in ra tệp thử nghiệm trong một loạt các mã hóa Unicode khác nhau. Nó có thể là trong bất kỳ ngôn ngữ lập trình; nó chỉ in các 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 codepage mặc định? Tổ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, điều gì sẽ xảy ra nếu chúng ta lưu type
các tệp? Chúng chứa các byte chính xác được in ra bàn đ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 õ¢áÕÑ¢
Điều duy nhất hoạt động là tệp UTF-16LE, với BOM, được in ra bàn điều khiển thông qua type
.
Nếu chúng tôi sử dụng bất cứ thứ gì ngoài type
việc in tệp, chúng tôi sẽ nhận được 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ế copy CON
không hiển thị Unicode chính xác, chúng ta có thể kết luận rằng type
lệnh có logic để phát hiện BOM UTF-16LE khi bắt đầu tệp và sử dụng API Windows đặc biệt để in.
Chúng ta có thể thấy điều này bằng cách mở cmd.exe
trình gỡ lỗi khi nó đi type
ra một tệp:
Sau khi type
mở một tệp, nó sẽ kiểm tra BOM của 0xFEFF
Đieie, các byte
0xFF 0xFE
trong Little endian endian và nếu có BOM như vậy, hãy type
đặt fOutputUnicode
cờ nội bộ . Cờ này được kiểm tra sau để quyết định có gọi hay không WriteConsoleW
.
Nhưng đó là cách duy nhất để có được type
đầu ra 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àn điều khiển, các tệp của bạn sẽ được diễn giải theo bảng mã hiện tại và có thể sẽ hiển thị dưới dạng vô nghĩa.
Bạn có thể mô phỏng cách type
xuất Unicode cho bảng điều khiển trong các chương trình của riêng bạn như sau:
#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 bảng điều khiển Windows 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ể nhận được một chút đầu ra chính xác bằng cách đặt mã hóa theo cách thủ công, mặc dù đầu ra bị rối theo những 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, chương trình C thiết lập bảng mã 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;
}
không có đầu ra chính xác:
Z:\andrew\projects\sx\1259084>.\test
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
Noi dung chinh cua cau chuyen?
type
có thể in các tệp UTF-16LE bằng BOM bất kể mã hóa hiện tại của bạn
- Các chương trình Win32 có thể được lập trình để xuất Unicode ra bàn điều khiển, bằng cách sử dụng
WriteConsoleW
.
- Các chương trình khác thiết lập codepage và điều chỉnh mã hóa đầu ra của chúng phù hợp có thể in Unicode trên bàn điều khiển bất kể codepage là gì khi chương trình bắt đầu
- Đối với mọi thứ khác, bạn sẽ phải loay hoay với
chcp
, và có thể vẫn sẽ nhận được đầu ra kỳ lạ.