ใช่มันน่าหงุดหงิด - บางครั้งtype
โปรแกรมอื่นก็พิมพ์ซึ่งพูดพล่อยๆและบางครั้งก็ทำไม่ได้
แรกของทุกตัวอักษร Unicode จะแสดงเฉพาะถ้าตัวอักษรคอนโซลปัจจุบันมีตัวละคร ดังนั้นใช้ฟอนต์ TrueType เช่น Lucida Console แทนฟอนต์ Raster เริ่มต้น
แต่ถ้าฟอนต์คอนโซลไม่มีอักขระที่คุณพยายามแสดงคุณจะเห็นเครื่องหมายคำถามแทนซึ่งพูดไม่ชัด เมื่อคุณพูดพล่อยๆมีเกิดขึ้นมากกว่าเพียงแค่การตั้งค่าแบบอักษร
เมื่อโปรแกรมใช้ฟังก์ชั่น C-ห้องสมุด I / O มาตรฐานเช่นprintf
, การเข้ารหัสเอาท์พุทของโปรแกรมจะต้องตรงกับการเข้ารหัสการส่งออกของคอนโซลหรือคุณจะได้รับการพูดพล่อยๆ chcp
แสดงและตั้งค่าเพจรหัสปัจจุบัน การส่งออกทั้งหมดโดยใช้มาตรฐาน C-ห้องสมุดฟังก์ชั่น I / O chcp
จะถือว่าเป็นถ้ามันอยู่ในเพจที่แสดงโดย
การจับคู่การเข้ารหัสเอาต์พุตของโปรแกรมกับการเข้ารหัสเอาต์พุตคอนโซลสามารถทำได้สองวิธี:
โปรแกรมสามารถรับ codepage ปัจจุบันของคอนโซลโดยใช้chcp
หรือ
GetConsoleOutputCP
และกำหนดค่าตัวเองเป็นเอาต์พุตในการเข้ารหัสนั้นหรือ
คุณหรือโปรแกรมสามารถตั้งค่าเพจรหัสปัจจุบันของคอนโซลโดยใช้chcp
หรือ
SetConsoleOutputCP
เพื่อให้ตรงกับการเข้ารหัสเอาต์พุตเริ่มต้นของโปรแกรม
แต่โปรแกรมที่ใช้ Win32 API ที่สามารถเขียน UTF-16LE
WriteConsoleW
สตริงโดยตรงไปยังคอนโซลด้วย นี่เป็นวิธีเดียวในการรับเอาต์พุตที่ถูกต้องโดยไม่ต้องตั้งค่าเพจรหัส และแม้กระทั่งเมื่อใช้ฟังก์ชั่นว่าถ้าสตริงไม่ได้อยู่ใน UTF-16LE การเข้ารหัสจะเริ่มต้นด้วยโปรแกรม Win32
MultiByteToWideChar
ต้องผ่านเพจรหัสที่ถูกต้อง นอกจากนี้WriteConsoleW
จะไม่ทำงานหากเอาต์พุตของโปรแกรมถูกเปลี่ยนเส้นทาง เล่นซอเป็นสิ่งจำเป็นในกรณีนี้
type
การทำงานบางส่วนของเวลาเพราะมันจะตรวจสอบจุดเริ่มต้นของแต่ละไฟล์สำหรับ UTF-16LE มาร์คไบต์สั่งซื้อ (BOM)0xFF 0xFE
คือไบต์ หากพบเครื่องหมายดังกล่าวจะแสดงอักขระ Unicode ในไฟล์โดยใช้โดยWriteConsoleW
ไม่คำนึงถึงเพจรหัสปัจจุบัน แต่เมื่อนำtype
ไฟล์ใด ๆ ที่ไม่มี UTF-16LE BOM หรือใช้อักขระที่ไม่ใช่ ASCII กับคำสั่งใด ๆ ที่ไม่ได้เรียกWriteConsoleW
- คุณจะต้องตั้งค่า codepage ของคอนโซลและการเข้ารหัสเอาต์พุตของโปรแกรมเพื่อให้เข้ากัน
เราจะทราบได้อย่างไร?
นี่คือไฟล์ทดสอบที่มีอักขระ Unicode:
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 ที่จุดเริ่มต้นของไฟล์และใช้ Windows API พิเศษเพื่อพิมพ์
เราสามารถเห็นสิ่งนี้ได้โดยการเปิดcmd.exe
ในตัวดีบั๊กเมื่อมันดับtype
ไฟล์:
หลังจากtype
เปิดไฟล์มันจะตรวจสอบ BOM ของ0xFEFF
- เป็นไบต์
0xFF 0xFE
ในหน่วยเล็ก - และถ้ามี BOM เช่นนั้นให้type
ตั้งfOutputUnicode
ค่าสถานะภายใน WriteConsoleW
ธงนี้มีการตรวจสอบในภายหลังเพื่อตัดสินใจว่าจะโทร
แต่นั่นเป็นวิธีเดียวในการรับtype
เอาต์พุต Unicode และเฉพาะไฟล์ที่มี BOM และอยู่ใน UTF-16LE สำหรับไฟล์อื่น ๆ ทั้งหมดและสำหรับโปรแกรมที่ไม่มีรหัสพิเศษสำหรับจัดการเอาต์พุตคอนโซลไฟล์ของคุณจะถูกตีความตามเพจรหัสปัจจุบัน
คุณสามารถจำลองว่า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
และอาจจะยังคงได้รับผลแปลก