หน้าต่างเป็นระบบ Unicode (UTF-16) คอนโซลยูนิโค้ดเช่นกัน หากคุณต้องการพิมพ์ข้อความ Unicode - คุณต้อง (และมีประสิทธิภาพมากที่สุด) ให้ใช้ WriteConsoleW
BOOL PrintString(PCWSTR psz)
{
DWORD n;
return WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), psz, (ULONG)wcslen(psz), &n, 0);
}
PrintString(L"—");
ในกรณีนี้ในไฟล์ไบนารี่ของคุณจะเป็นอักขระแบบกว้าง —
(2 ไบต์ 0x2014
) และคอนโซลพิมพ์ตามที่เป็นอยู่
หากเรียกใช้ฟังก์ชัน ansi (หลายไบต์) สำหรับคอนโซลเอาต์พุต - เช่น WriteConsoleA
หรือ WriteFile
- คอนโซลก่อนจะแปลสตริงแบบหลายไบต์เป็น Unicode ผ่าน MultiByteToWideChar
และในสถานที่ CodePage จะถูกใช้ค่าที่ส่งคืนโดย GetConsoleOutputCP
และที่นี่ (การแปล) อาจมีปัญหาได้หากคุณใช้อักขระ > 0x80
ก่อนอื่นคอมไพเลอร์สามารถให้คำเตือนแก่คุณได้: ไฟล์มีอักขระที่ไม่สามารถแสดงในโค้ดเพจปัจจุบัน (ตัวเลข) บันทึกไฟล์ในรูปแบบ Unicode เพื่อป้องกันข้อมูลสูญหาย (C4819) แต่แม้หลังจากที่คุณบันทึกไฟล์ต้นฉบับในรูปแบบ Unicode แล้ว ก็สามารถเป็นต่อไปได้:
wprintf(L"ù"); // no warning
printf("ù"); //warning C4566
เนื่องจาก L"ù"
บันทึกเป็น สตริงอักขระแบบกว้าง (ตามสภาพ) ในไฟล์ ไบนารี - ทั้งหมดนี้ใช้ได้และไม่มีปัญหาและคำเตือนใดๆ แต่ "ù"
ถูกบันทึกเป็น สตริงอักขระ (สตริงไบต์เดียว) คอมไพเลอร์จำเป็นต้องแปลง สตริงแบบกว้าง "ù" จากไฟล์ต้นฉบับเป็น สตริงแบบหลายไบต์ ในไฟล์ไบนารี (.obj ซึ่งตัวเชื่อมโยงจะสร้าง pe มากกว่า) และคอมไพเลอร์ใช้สำหรับ WideCharToMultiByte
< /a> ด้วย CP_ACP (หน้ารหัส Windows ANSI เริ่มต้นของระบบปัจจุบัน)
แล้วจะเกิดอะไรขึ้นถ้าคุณพูดว่าโทร printf("ù");
?
- สตริงยูนิโค้ด "ù" จะถูกแปลงเป็นหลายไบต์
WideCharToMultiByte(CP_ACP, )
และจะเป็นเวลาเวลาคอมไพล์ สตริง หลายไบต์ ผลลัพธ์จะถูกบันทึกในไฟล์ไบนารี
- คอนโซล รันไทม์ แปลงสตริง หลายไบต์ ของคุณเป็นอักขระแบบกว้าง
MultiByteToWideChar(GetConsoleOutputCP(), ..)
และพิมพ์สตริงนี้
คุณได้รับ 2 Conversion: unicode -> CP_ACP -> multi-byte -> GetConsoleOutputCP() -> unicode
โดยค่าเริ่มต้น GetConsoleOutputCP() == CP_OEMCP != CP_ACP
แม้ว่าคุณจะรันโปรแกรมบนคอมพิวเตอร์ที่คุณคอมไพล์มันก็ตาม (บนคอมพิวเตอร์เครื่องอื่นที่มี CP_OEMCP
อีกเครื่องโดยเฉพาะ)
ปัญหาในการแปลงที่เข้ากันไม่ได้ - ใช้โค้ดเพจที่แตกต่างกัน แต่แม้ว่าคุณจะเปลี่ยนโค้ดเพจคอนโซลเป็น CP_ACP
ของคุณ - การแปลงก็ยังอาจแปลอักขระบางตัวผิดได้
และเกี่ยวกับ CRT api wprintf
- สถานการณ์ต่อไปนี้คือ:
wprintf
แปลงสตริงที่กำหนดจากยูนิโค้ดเป็นหลายไบต์ก่อนโดยใช้ locale ปัจจุบัน (และโปรดทราบว่าภาษา crt เป็นอิสระและแตกต่างจากภาษา คอนโซล) จากนั้นโทร WriteFile
ด้วยสตริงแบบหลายไบต์ console แปลงสตริงหลายไบต์กลับเป็นยูนิโค้ด
unicode -> current_crt_locale -> multi-byte -> GetConsoleOutputCP() -> unicode
ดังนั้นสำหรับการใช้งาน wprintf
เราต้องตั้งค่า crt locale ปัจจุบันเป็น GetConsoleOutputCP()
ก่อน
char sz[16];
sprintf(sz, ".%u", GetConsoleOutputCP());
setlocale(LC_ALL, sz);
wprintf(L"—");
แต่อย่างไรก็ตาม ที่นี่ ฉันดู (ในคอมพ์ของฉัน) -
บนหน้าจอแทน —
ดังนั้นจะเป็น -—
หากโทร PrintString(L"—");
(ซึ่งใช้ WriteConsoleW
) หลังจากนี้
ดังนั้นวิธีที่เชื่อถือได้เท่านั้นในการพิมพ์อักขระ Unicode ใด ๆ (รองรับโดย windows) - ใช้ WriteConsoleW api
person
RbMm
schedule
14.09.2017
WriteConsoleW
หรือแปลงยูนิโค้ดเป็นมัลติไบต์ก่อนโดยใช้WideCharToMultiByte(GetConsoleOutputCP(),..)
เพื่อใช้ในฟังก์ชันเอาต์พุต A - person RbMm   schedule 14.09.2017printf ("—\n");
ทำงานในคอนโซล Windows ของฉันส่งออกÔÇö
- person Weather Vane   schedule 14.09.2017WriteConsoleW
กับL"—\n"
คุณเข้าใจไหมว่าทำไมถึงเกิดข้อผิดพลาดเมื่อคุณใช้เวอร์ชัน ansi เนื่องจากใช้โค้ดเพจอื่น (โดยค่าเริ่มต้นCP_OEMCP
) เพื่อแปลสตริงของคุณเป็นยูนิโค้ด (ใน src ของคุณCP_ACP
ของคุณถูกใช้) - person RbMm   schedule 14.09.2017_setmode(_fileno(stdout), _O_U16TEXT)
เมื่อเริ่มต้นโปรแกรม และใช้ I/O แบบอักขระกว้าง C/C++ เช่นwprintf
และstd::wcout
- person Eryk Sun   schedule 15.09.2017GetConsoleOutputCP
) ฉันไม่คิดว่าความคิดเห็นหมายความว่าคอนโซลโดยทั่วไปไม่รองรับ Unicode แม้ว่าในส่วนหลัง คอนโซลจะถูกจำกัดไว้ที่ BMP (เช่น รหัสตัวแทนจะแสดงเป็นอักขระเริ่มต้น แทนที่จะถอดรหัสคู่ตัวแทนเสมือน UTF-16) ไม่รองรับการรวมรหัส และต้องใช้แบบอักษร monospace พร้อมสัญลักษณ์สำหรับอักขระ (ความช่วยเหลือในการเชื่อมโยงแบบอักษรด้วยตนเอง) - person Eryk Sun   schedule 15.09.2017current output codepage
- นี่เป็นประโยคที่ไม่ถูกต้องแน่นอน เอาต์พุตคอนโซลจะเป็นยูนิโค้ดเสมอGetConsoleOutputCP
- นี่คือโค้ดเพจเพื่อแปลสตริงหลายไบต์เป็นยูนิโค้ด ก่อนที่จะแสดง - person RbMm   schedule 15.09.2017WriteFile
ถูกเรียกด้วยสตริงไบต์ ใน Windows 8+ สิ่งนี้จะเรียกNtWriteFile
สำหรับไฟล์ที่กำหนดบนอุปกรณ์ ConDrv คอนโซลที่แนบ (conhost.exe) กำลังรออยู่ที่NtDeviceIoControlFile
ซึ่งเสร็จสิ้นพร้อมกับคำขอให้เขียนไบต์ที่กำหนดไปยังบัฟเฟอร์หน้าจอเป้าหมาย คอนโซลจะถอดรหัสไบต์เหล่านี้ก่อนโดยใช้เพจโค้ดเอาต์พุตโดยการเรียกMultiByteToWideChar
และสิ่งที่คล้ายคลึงกัน - person Eryk Sun   schedule 15.09.2017GetConsoleOutputCP
เช่น เพจโค้ดเอาต์พุต คุณกำลังมีปัญหากับชื่อเท่าที่ฉันสามารถบอกได้ ไม่มีอะไรที่ฉันพูดผิดเกี่ยวกับการปฏิบัติการ - person Eryk Sun   schedule 15.09.2017WriteConsoleW
- person RbMm   schedule 15.09.2017_setmode(_fileno(stdout), _O_U16TEXT)
จากนั้นใช้ฟังก์ชัน CRT แบบอักขระกว้างเช่นwprintf
มันไม่ได้มีประสิทธิภาพมากนักเนื่องจาก CRT สิ้นสุดการเรียก_putwch_nolock
วนซ้ำอักขระ ดังนั้นจึงทำการเรียกWriteConsoleW
สำหรับอักขระแต่ละตัว แต่นี่คือคอนโซล I/O แบบโต้ตอบ ดังนั้นเราจึงไม่ต้องการความเร็วและประสิทธิภาพที่สูงมาก - person Eryk Sun   schedule 15.09.2017_setmode(_fileno(stdout), _O_U16TEXT)
wprintf
เริ่มใช้WriteConsoleW
(ถ่านต่อถ่าน) แทนWriteFile
แต่โดยส่วนตัวแล้วฉันไม่เข้าใจเลย - สำหรับสิ่งที่มีปัญหาทั้งหมดนี้กับเอาต์พุต CRT และ/หรือ ansi เมื่อสามารถโทรWriteConsoleW
ได้และไม่มีปัญหาใดๆ เลย - person RbMm   schedule 15.09.2017WriteConsoleW
และ main -printf
จะแสดง—
เป็น-
ด้วยวิธีใดก็ตาม เพียงWriteConsoleW
ให้การแสดงผลที่ถูกต้อง - person RbMm   schedule 15.09.2017SetConsoleOutputCP(CP_UTF8)
ได้เช่นกัน ตัวเลือกคอมไพเลอร์/utf-8
บังคับใช้ตัวอักษรสตริง UTF-8 ฉันจะไม่ใช้สิ่งนี้ก่อน Windows 8 ซึ่งในกรณีนี้WriteFile
ไปยังคอนโซลส่งคืนจำนวนอักขระที่ถอดรหัสที่เขียนไม่ถูกต้องแทนที่จะเป็นจำนวนไบต์ที่เขียน นอกจากนี้SetConsoleCP(CP_UTF8)
ไม่มีประโยชน์สำหรับอินพุตที่ไม่ใช่ ASCII ในทุกเวอร์ชันเนื่องจากคอนโซลทำให้สันนิษฐานว่ากำลังเข้ารหัสเป็น ANSI (เช่น 1 ไบต์ต่ออักขระ) เมื่อปรับขนาดบัฟเฟอร์สำหรับWideCharToMultiByte
ซึ่งล้มเหลวและยังReadFile
'สำเร็จ' เมื่ออ่าน เป็นศูนย์ไบต์ เช่น EOF - person Eryk Sun   schedule 15.09.2017CP_UTF8
(65001) นั้น 'ปรับปรุง' เล็กน้อย เห็นได้ชัดว่าก่อนการเข้ารหัสตอนนี้พวกเขาจะแทนที่อักขระที่ไม่ใช่ ASCII ทั้งหมดด้วย Unicode NUL ดังนั้นอย่างน้อยมันก็ดูไม่เหมือน EOF เพียงแต่อักขระอินพุตที่ไม่ใช่ ASCII ทั้งหมดจะลงท้ายด้วย \x00 ในบัฟเฟอร์ - person Eryk Sun   schedule 15.09.2017