std :: wstring VS std :: string


741

ผมไม่สามารถที่จะเข้าใจความแตกต่างระหว่างและstd::string std::wstringฉันรู้ว่าwstringรองรับตัวละครกว้างเช่นตัวอักษร Unicode ฉันมีคำถามต่อไปนี้:

  1. เมื่อใดที่ฉันควรใช้std::wstringมากกว่าstd::string?
  2. สามารถstd::stringเก็บชุดอักขระ ASCII ทั้งหมดรวมถึงอักขระพิเศษได้หรือไม่
  3. มีการstd::wstringสนับสนุนจากทุกที่นิยม C ++ คอมไพเลอร์?
  4. " ตัวกว้าง " คืออะไร?

10
ชุดอักขระแบบ ASCII ไม่มีอักขระ "พิเศษ" จำนวนมากแปลกใหม่ที่สุดน่าจะเป็น `(backquote) มาตรฐาน :: สตริงสามารถถือประมาณ 0.025% ของทุกตัวอักษร Unicode (ปกติถ่าน 8 บิต)
MSalters

3
ข้อมูลที่ดีเกี่ยวกับตัวละครกว้างและประเภทที่ใช้สามารถพบได้ที่นี่: programmers.stackexchange.com/questions/102205/ …
Yariv

14
ดีและเนื่องจากเราอยู่ในปี 2012 utf8everywhere.orgจึงถูกเขียนขึ้น มันค่อนข้างตอบทุกคำถามเกี่ยวกับสิทธิและความผิดกับ C ++ / Windows
Pavel Radzivilovsky

42
@MSalters: std :: string สามารถเก็บ 100% ของอักขระ Unicode ทั้งหมดแม้ว่า CHAR_BIT คือ 8 มันขึ้นอยู่กับการเข้ารหัสของ std :: string ซึ่งอาจเป็น UTF-8 ในระดับระบบ (เกือบทุกที่ยกเว้นหน้าต่าง ) หรือในระดับแอปพลิเคชันของคุณ การเข้ารหัสแบบเนทิฟแบบแคบไม่รองรับ Unicode? ไม่มีปัญหาไม่ต้องใช้งานใช้ UTF-8 แทน
Yakov Galka

8
การอ่านที่ยอดเยี่ยมในหัวข้อนี้: utf8everywhere.org
Timothy Shields

คำตอบ:


991

string? wstring?

std::stringเป็นbasic_stringเทมเพลตบนcharและบนstd::wstringwchar_t

char เมื่อเทียบกับ wchar_t

charควรจะถือตัวละครมักจะเป็นตัวละคร 8 บิต
wchar_tควรถือตัวกว้างและจากนั้นสิ่งที่ยุ่งยาก:
บน Linux, a wchar_tคือ 4 ไบต์, ในขณะที่บน Windows, มันคือ 2 ไบต์

แล้วUnicodeล่ะ?

ปัญหาคือว่าไม่ว่าcharมิได้wchar_tมีการเชื่อมโยงโดยตรงกับ Unicode

บน Linux?

ลองใช้ระบบปฏิบัติการ Linux: ระบบ Ubuntu ของฉันเป็น unicode ที่ทราบแล้ว เมื่อฉันทำงานกับสตริงถ่านมันถูกเข้ารหัสในUTF-8 (เช่น Unicode string chars) รหัสต่อไปนี้:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(ordinals)  :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(ordinals) :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

เอาต์พุตข้อความต่อไปนี้:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(ordinals)  : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(ordinals) : 111 108 233

คุณจะเห็นข้อความ "olé" charสร้างขึ้นโดยตัวอักษรสี่ตัว: 110, 108, 195 และ 169 (ไม่นับศูนย์ต่อท้าย) ฉันจะให้คุณเรียนwchar_tโค้ดเป็นการออกกำลังกาย)

ดังนั้นเมื่อทำงานกับcharลีนุกซ์คุณควรลงเอยด้วยการใช้ Unicode โดยที่ไม่รู้ตัว และstd::stringทำงานร่วมกับcharดังนั้นstd::stringพร้อมใช้งาน Unicode

สังเกตได้ว่า std::stringเช่นเดียวกับ C string API จะพิจารณาสตริง "olé" ให้มี 4 ตัวอักษรไม่ใช่สามตัว ดังนั้นคุณควรระมัดระวังเมื่อตัดทอน / เล่นกับ unicode chars เนื่องจากการรวมกันของ chars บางอย่างเป็นสิ่งต้องห้ามใน UTF-8

บน Windows?

บน Windows มันแตกต่างกันเล็กน้อย Win32 ต้องสนับสนุนแอพพลิเคชันจำนวนมากที่ทำงานกับcharและในcharsets / codepages ที่แตกต่างกันผลิตในโลกก่อนที่ Unicode จะมาถึง

ดังนั้นวิธีการแก้ปัญหาของพวกเขาจึงเป็นสิ่งที่น่าสนใจ: หากแอปพลิเคชันทำงานด้วยcharดังนั้นสตริงอักขระจะถูกเข้ารหัส / พิมพ์ / แสดงบนฉลาก GUI โดยใช้ charset / codepage บนเครื่อง ตัวอย่างเช่น "olé" จะเป็น "olé" ใน Windows ที่แปลเป็นภาษาฝรั่งเศส แต่จะเป็นสิ่งที่แตกต่างใน Windows ที่แปลด้วยภาษาซิริลลิก ("olй" ถ้าคุณใช้Windows-1251 ) ดังนั้น "แอปในอดีต" จึงยังคงใช้งานได้เหมือนเดิม

สำหรับแอพพลิเคชั่นที่ใช้ Unicode Windows จะใช้wchar_tความกว้าง 2 ไบต์และเข้ารหัสในUTF-16ซึ่งเป็น Unicode เข้ารหัสด้วยอักขระ 2 ไบต์ (หรืออย่างน้อยที่สุด UCS-2 ที่เข้ากันได้ส่วนใหญ่ซึ่งเกือบจะเป็น สิ่งเดียวกัน IIRC)

แอปพลิเคชันที่ใช้charจะกล่าวว่า "หลายไบต์" (เพราะแต่ละสัญลักษณ์ประกอบด้วยหนึ่งหรือมากกว่านั้นchar) ในขณะที่แอปพลิเคชันที่ใช้wchar_tจะพูดว่า "widechar" (เพราะแต่ละสัญลักษณ์ประกอบด้วยหนึ่งหรือสองwchar_tไฟล์ดูMultiByteToWideCharและWideCharToMultiByte Win32 API สำหรับข้อมูลเพิ่มเติม

ดังนั้นหากคุณทำงานบน Windows คุณต้องการที่จะใช้อย่างรุนแรงwchar_t (เว้นแต่คุณจะใช้กรอบการซ่อนเช่นGTK +หรือQT ... ) ความจริงก็คือว่าเบื้องหลัง Windows ทำงานกับwchar_tสตริงดังนั้นแม้แต่แอปพลิเคชันในอดีตจะมีการcharแปลงสตริงของพวกเขาwchar_tเมื่อใช้ API เช่นSetWindowText()(ฟังก์ชั่น API ระดับต่ำเพื่อตั้งค่าฉลากบน Win32 GUI)

ปัญหาหน่วยความจำ?

UTF-32 คือ 4 ไบต์ต่อตัวอักษรดังนั้นจึงไม่มีอะไรเพิ่มถ้าข้อความ UTF-8 และข้อความ UTF-16 จะใช้หน่วยความจำน้อยกว่าหรือเท่ากับจำนวนข้อความ UTF-32 เสมอ (และมักจะน้อยกว่า )

หากมีปัญหาเกี่ยวกับหน่วยความจำคุณควรรู้มากกว่าภาษาตะวันตกส่วนใหญ่ข้อความ UTF-8 จะใช้หน่วยความจำน้อยกว่า UTF-16 อันเดียวกัน

สำหรับภาษาอื่น (จีนญี่ปุ่นและอื่น ๆ ) หน่วยความจำที่ใช้จะเหมือนกันหรือใหญ่กว่าสำหรับ UTF-8 เล็กน้อยสำหรับ UTF-16

โดยรวมแล้ว UTF-16 ส่วนใหญ่จะใช้ 2 และ 4 ไบต์ต่ออักขระเป็นครั้งคราว (เว้นแต่คุณกำลังติดต่อกับร่ายมนตร์ภาษาลึกลับบางอย่าง (Klingon? Elvish?) ในขณะที่ UTF-8 จะใช้เวลา 1 ถึง 4 ไบต์

ดูhttp://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16สำหรับข้อมูลเพิ่มเติม

ข้อสรุป

  1. เมื่อใดที่ฉันควรใช้ std :: wstring บน std :: string?

    บน Linux? แทบจะไม่เคย (§).
    บน Windows? เกือบตลอดเวลา (§).
    เกี่ยวกับรหัสข้ามแพลตฟอร์ม? ขึ้นอยู่กับชุดเครื่องมือของคุณ ...

    (§): ยกเว้นว่าคุณใช้ชุดเครื่องมือ / กรอบงานที่พูดเป็นอย่างอื่น

  2. สามารถstd::stringเก็บชุดอักขระ ASCII ทั้งหมดรวมถึงอักขระพิเศษได้หรือไม่

    ประกาศ: A std::stringเหมาะสำหรับเก็บบัฟเฟอร์ 'ไบนารี' โดยที่std::wstringไม่ใช่!

    บน Linux? ใช่.
    บน Windows? เฉพาะอักขระพิเศษที่มีอยู่สำหรับสถานที่ปัจจุบันของผู้ใช้ Windows

    แก้ไข (หลังจากความคิดเห็นจากJohann Gerell ):
    a std::stringจะเพียงพอที่จะจัดการกับcharสตริงที่ใช้ทั้งหมด(แต่ละอันcharมีค่าตั้งแต่ 0 ถึง 255) แต่:

    1. ASCII ควรจะไปจาก 0 ถึง 127 chars ที่สูงกว่าไม่ใช่ ASCII
    2. a charตั้งแต่ 0 ถึง 127 จะจัดขึ้นอย่างถูกต้อง
    3. char128-255 จะมีความหมายขึ้นอยู่กับการเข้ารหัสของคุณ (Unicode, ไม่ใช่ Unicode, ฯลฯ ) แต่ก็จะสามารถที่จะถือทุกร่ายมนตร์ Unicode ตราบเท่าที่พวกเขาจะถูกเข้ารหัส UTF-8
  3. คือการstd::wstringได้รับการสนับสนุนโดยเกือบทั้งหมดนิยม C ++ คอมไพเลอร์?

    ส่วนใหญ่มีข้อยกเว้นของคอมไพเลอร์ที่ใช้ GCC ที่พอร์ตไปยัง Windows
    มันทำงานบน g ++ 4.3.2 ของฉัน (ภายใต้ Linux) และฉันใช้ Unicode API บน Win32 ตั้งแต่ Visual C ++ 6

  4. ตัวกว้างคืออะไร?

    บน C / C ++ เป็นชนิดอักขระที่เขียนwchar_tซึ่งมีขนาดใหญ่กว่าcharชนิดอักขระแบบง่าย ควรใช้เพื่อใส่อักขระที่มีดัชนี (เช่น Unicode glyphs) ที่มีขนาดใหญ่กว่า 255 (หรือ 127 ขึ้นอยู่กับ ... )


4
@gnud: บางที wchar_t น่าจะเพียงพอที่จะจัดการกับ UCS-2 chars ทั้งหมด (ส่วนใหญ่ UTF-16 chars) ก่อนการถือกำเนิดของ UTF-16 ... หรือ Microsoft อาจมีลำดับความสำคัญอื่น ๆ กว่า POSIX เช่นให้เข้าถึง Unicode ได้ง่าย โดยไม่ต้องดัดแปลงการใช้ char ของ codepaged บน Win32
paercebal

4
@ โซริน Sbarnea: UTF-8 อาจใช้เวลา 1-6 ไบต์ แต่ดูเหมือนว่ามาตรฐานจะ จำกัด ไว้ที่ 1-4 ดูen.wikipedia.org/wiki/UTF8#Descriptionสำหรับข้อมูลเพิ่มเติม
paercebal

8
ในขณะที่ตัวอย่างนี้สร้างผลลัพธ์ที่แตกต่างกันบน Linux และ Windows โปรแกรม C ++ จะมีพฤติกรรมการปรับใช้งานว่าolèมีการเข้ารหัสเป็น UTF-8 หรือไม่ ต่อไปอีกเหตุผลที่คุณไม่สามารถกำเนิดสตรีมwchar_t *จะstd::coutเป็นเพราะชนิดเข้ากันไม่ได้ส่งผลให้โปรแกรมที่ไม่ดีเกิดขึ้นและมันมีอะไรจะทำอย่างไรกับการใช้งานของการเข้ารหัส เป็นค่าชี้ให้เห็นว่าไม่ว่าคุณจะใช้std::stringหรือstd::wstringขึ้นอยู่กับการตั้งค่าการเข้ารหัสของคุณเองมากกว่าแพลตฟอร์มโดยเฉพาะอย่างยิ่งถ้าคุณต้องการให้รหัสของคุณเป็นแบบพกพา
John Leidegren

14
จริง ๆ แล้ว Windows ใช้ UTF-16 มานานแล้ว Windows รุ่นเก่าใช้ UCS-2 แล้วแต่กรณีนี้ไม่ได้อีกต่อไป ปัญหาเดียวของฉันที่นี่คือข้อสรุปที่std::wstringควรใช้กับ Windows เพราะมันเหมาะสมกว่าสำหรับ Unicode Windows API ซึ่งฉันคิดว่าผิดพลาด หากข้อกังวลเพียงอย่างเดียวของคุณคือการเรียกใช้ Unicode Windows API และไม่ใช่สตริงการเรียงลำดับจากนั้นให้แน่ใจ แต่ฉันไม่ได้ซื้อสิ่งนี้เป็นกรณีทั่วไป
John Leidegren

15
@ John Leidegren If your only concern was calling into the Unicode Windows API and not marshalling strings then sure:: แล้วเราเห็นด้วย ฉันกำลังเขียนโปรแกรมใน C ++ ไม่ใช่ JavaScript การหลีกเลี่ยงการจัดการที่ไร้ประโยชน์หรือการประมวลผลอื่น ๆ ที่อาจก่อให้เกิดค่าใช้จ่ายสูงเมื่อรันไทม์เมื่อมันสามารถทำได้ในเวลารวบรวมเป็นหัวใจสำคัญของภาษานั้น การเขียนโปรแกรมกับ WinAPI และการใช้std::stringเป็นเพียงทรัพยากรที่สิ้นเปลืองโดยไม่ยุติธรรม คุณคิดว่ามันผิดพลาดและไม่เป็นไรเพราะเป็นมุมมองของคุณ ของตัวเองคือฉันจะไม่เขียนโค้ดด้วยการลบล้างบน Windows เพียงเพราะมันดูดีกว่าจากฝั่ง Linux
paercebal

71

ฉันขอแนะนำให้หลีกเลี่ยงstd::wstringใน Windows หรือที่อื่นยกเว้นเมื่อจำเป็นต้องใช้อินเทอร์เฟซหรือที่ใดก็ได้ที่อยู่ใกล้การโทร Windows API และการแปลงการเข้ารหัสตามลำดับเป็นน้ำตาลประโยค

มุมมองของฉันสรุปในhttp://utf8everywhere.orgซึ่งฉันเป็นผู้ร่วมเขียน

เว้นแต่ว่าแอปพลิเคชันของคุณเป็น API-call-centric เช่นส่วนใหญ่เป็นแอปพลิเคชัน UI คำแนะนำคือการจัดเก็บสตริง Unicode ใน std :: string และเข้ารหัสใน UTF-8 ทำการแปลงใกล้กับการเรียก API ประโยชน์ที่ระบุไว้ในบทความนี้มีมากกว่าความน่ารำคาญของการแปลงโดยเฉพาะอย่างยิ่งในการใช้งานที่ซับซ้อน นี่เป็นทวีคูณสำหรับการพัฒนาหลายแพลตฟอร์มและห้องสมุด

และตอนนี้ตอบคำถามของคุณ:

  1. เหตุผลที่อ่อนแอเล็กน้อย มันมีอยู่สำหรับเหตุผลทางประวัติศาสตร์ที่ widechars เชื่อว่าเป็นวิธีที่เหมาะสมในการสนับสนุน Unicode ตอนนี้มันถูกใช้เพื่ออินเตอร์เฟส API ที่ต้องการสตริง UTF-16 ฉันใช้พวกเขาเฉพาะในบริเวณใกล้เคียงโดยตรงของการโทร API ดังกล่าว
  2. สิ่งนี้ไม่เกี่ยวข้องกับ std :: string มันสามารถเข้ารหัสสิ่งที่คุณใส่เข้าไปได้ คำถามเดียวคือคุณปฏิบัติต่อเนื้อหาอย่างไร คำแนะนำของฉันคือ UTF-8 ดังนั้นมันจะสามารถเก็บอักขระ Unicode ทั้งหมดได้อย่างถูกต้อง เป็นการปฏิบัติทั่วไปบน Linux แต่ฉันคิดว่าโปรแกรม Windows ควรทำเช่นนั้น
  3. เลขที่
  4. อักขระไวด์เป็นชื่อที่สับสน ในยุคแรก ๆ ของ Unicode มีความเชื่อว่าตัวอักษรสามารถเข้ารหัสในสองไบต์ดังนั้นชื่อ วันนี้มันย่อมาจาก "ส่วนใดส่วนหนึ่งของตัวละครที่มีความยาวสองไบต์" UTF-16 ถูกมองว่าเป็นลำดับของคู่ไบต์ (aka อักขระไวด์) อักขระใน UTF-16 ใช้เวลาหนึ่งหรือสองคู่

37

ดังนั้นผู้อ่านทุกคนที่นี่ควรมีความเข้าใจที่ชัดเจนเกี่ยวกับข้อเท็จจริงสถานการณ์ ถ้าไม่เช่นนั้นคุณต้องอ่านคำตอบที่ครอบคลุมอย่างเด่นชัดของ paercebal [btw: ขอบคุณ!]

ข้อสรุปเชิงปฏิบัติของฉันง่ายมากอย่างน่าตกใจ: สิ่งที่ C ++ (และ STL) "การเข้ารหัสตัวอักษร" ทุกอย่างแตกหักและไร้ประโยชน์ ตำหนิใน Microsoft หรือไม่ว่าจะไม่ช่วย

วิธีการแก้ปัญหาของฉันหลังจากการตรวจสอบในเชิงลึกความยุ่งยากและประสบการณ์ที่ตามมาคือ:

  1. ยอมรับว่าคุณต้องรับผิดชอบตัวเองในการเข้ารหัสและการแปลงข้อมูล (และคุณจะเห็นว่าส่วนใหญ่ค่อนข้างไม่สำคัญ)

  2. ใช้ std :: string สำหรับสตริงที่เข้ารหัส UTF-8 (เพียง a typedef std::string UTF8String)

  3. ยอมรับว่าวัตถุ UTF8String ดังกล่าวเป็นเพียงภาชนะบรรจุที่ราคาไม่แพง ไม่เคยเข้าถึงและ / หรือจัดการกับตัวละครโดยตรง (ไม่ต้องค้นหาแทนที่และอื่น ๆ ) คุณทำได้ แต่จริงๆแล้วคุณไม่ต้องการเสียเวลาเขียนอัลกอริธึมการจัดการข้อความสำหรับสตริงแบบหลายไบต์! แม้ว่าคนอื่นจะทำสิ่งที่โง่ ๆ ไปแล้วอย่าทำอย่างนั้น! ช่างมันเถอะ! (มีสถานการณ์ที่เหมาะสม ... เพียงใช้ห้องสมุด ICU สำหรับสิ่งเหล่านั้น)

  4. ใช้ std :: wstring สำหรับสตริงเข้ารหัส UCS-2 ( typedef std::wstring UCS2String) - นี่คือการประนีประนอมและยอมให้ระเบียบที่ WIN32 API แนะนำ) UCS-2 นั้นเพียงพอสำหรับพวกเราส่วนใหญ่ (เพิ่มเติมในภายหลัง ... )

  5. ใช้อินสแตนซ์ UCS2String ทุกครั้งที่จำเป็นต้องใช้การเข้าถึงแบบอักขระต่ออักขระ (อ่านจัดการและอื่น ๆ ) การประมวลผลด้วยอักขระใด ๆ ควรทำในรูปแบบที่ไม่ใช่มัลติไบต์ มันง่ายรวดเร็วและง่าย

  6. เพิ่มสองฟังก์ชั่นยูทิลิตี้เพื่อแปลงไปมาระหว่าง UTF-8 และ UCS-2:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );

การแปลงตรงไปตรงมา google ควรช่วยที่นี่ ...

แค่นั้นแหละ. ใช้ UTF8String ทุกที่ที่หน่วยความจำมีค่าและสำหรับ UTF-8 I / O ทั้งหมด ใช้ UCS2String ที่ใดก็ตามที่สตริงจะต้องมีการแยกวิเคราะห์และ / หรือจัดการ คุณสามารถแปลงระหว่างการรับรองสองครั้งได้ทุกเมื่อ

ทางเลือกและการปรับปรุง

  • การแปลงจาก & ถึงการเข้ารหัสอักขระไบต์เดียว (เช่น ISO-8859-1) สามารถรับรู้ได้ด้วยความช่วยเหลือของตารางการแปลธรรมดาเช่นconst wchar_t tt_iso88951[256] = {0,1,2,...};และรหัสที่เหมาะสมสำหรับการแปลงเป็น & จาก UCS2

  • ถ้า UCS-2 ไม่เพียงพอกว่าเปลี่ยนเป็น UCS-4 ( typedef std::basic_string<uint32_t> UCS2String)

ICU หรือไลบรารี unicode อื่น ๆ

สำหรับสิ่งที่ทันสมัย


แดงไม่ดีที่จะรู้ว่าการสนับสนุน Unicode ดั้งเดิมไม่ได้อยู่ที่นั่น
หมดเวลา Danila

@Frunsi ฉันอยากรู้ว่าคุณลอง Glib :: ustring หรือไม่ถ้าเช่นนั้นคุณมีความคิดอย่างไร?
Caroline Beltran

@CarolineBeltran: ฉันรู้ว่า Glib แต่ฉันไม่เคยใช้มันและฉันอาจจะไม่เคยใช้มันเพราะมันค่อนข้าง จำกัด เฉพาะแพลตฟอร์มเป้าหมายที่ไม่เจาะจง (ระบบ unixoid ... ) พอร์ต windows ของมันขึ้นอยู่กับ win2unix-layer ภายนอกและ IMHO นั้นไม่มีเลเยอร์ OSX ที่เข้ากันได้ ทุกสิ่งนี้นำไปสู่ทิศทางที่ผิดอย่างน้อยที่สุดสำหรับโค้ดของฉัน (ในระดับโค้งนี้ ... ) ;-) ดังนั้น Glib จึงไม่ใช่ตัวเลือก
Frunsi

9
ค้นหาแทนที่และอื่น ๆ ทำงานได้ดีบนสตริง UTF-8 (ส่วนหนึ่งของลำดับไบต์ที่แทนอักขระไม่สามารถตีความผิดเป็นอักขระอื่นได้) อันที่จริงแล้ว UTF-16 และ UTF-32 ไม่ได้ทำให้เรื่องนี้ง่ายขึ้นเลย: การเข้ารหัสทั้งสามเป็นการเข้ารหัสแบบมัลติไบต์ในทางปฏิบัติเนื่องจากอักขระที่ผู้ใช้รับรู้ วิธีแก้ปัญหาในทางปฏิบัติคือการใช้ UTF-8 สำหรับทุกสิ่งและแปลงเป็น UTF-16 เฉพาะเมื่อทำงานกับ Windows API เท่านั้น
แดเนียล

5
@Frunsi: ค้นหาและแทนที่งานได้ดีเหมือนกันกับ UTF-8 เช่นเดียวกับ UTF-32 เป็นเพราะการประมวลผลข้อความ Unicode ที่เหมาะสมที่เหมาะสมนั้นจำเป็นต้องจัดการกับตัวอักษร 'codepoint' แบบหลายรหัสที่ใช้การเข้ารหัสความยาวตัวแปรเช่น UTF-8 ไม่ได้ทำให้การประมวลผลสตริงมีความซับซ้อนมากขึ้น ดังนั้นเพียงใช้ UTF-8 ในทุกที่ ฟังก์ชั่นสตริง C ปกติจะทำงานได้ดีบน UTF-8 (และสอดคล้องกับการเปรียบเทียบลำดับของสตริง Unicode) และหากคุณต้องการอะไรที่ต้องใช้ภาษามากขึ้นคุณจะต้องเรียกใช้ไลบรารี Unicode ต่อไป UTF-16/32 ไม่สามารถช่วยคุณได้
Daniel

25
  1. เมื่อคุณต้องการให้มีตัวละครกว้างเก็บไว้ในสายของคุณ wideขึ้นอยู่กับการใช้งาน Visual C ++ ค่าเริ่มต้นเป็น 16 บิตถ้าฉันจำได้อย่างถูกต้องในขณะที่ค่าเริ่มต้น GCC ขึ้นอยู่กับเป้าหมาย ที่นี่ยาว 32 บิต โปรดทราบว่า wchar_t (ประเภทตัวอักษรกว้าง) ไม่มีส่วนเกี่ยวข้องกับ unicode มันรับประกันได้เพียงว่ามันสามารถเก็บสมาชิกทั้งหมดของชุดอักขระที่ใหญ่ที่สุดที่การใช้งานสนับสนุนโดยโลแคลและอย่างน้อยก็ตราบใด คุณสามารถเก็บสตริงยูนิโคดไว้ได้std::stringโดยใช้การutf-8เข้ารหัสเช่นกัน แต่มันจะไม่เข้าใจความหมายของจุดโค้ดยูนิโค้ด ดังนั้นstr.size()จะไม่ให้จำนวนตัวอักษรเชิงตรรกะในสตริงของคุณ แต่เพียงจำนวนขององค์ประกอบถ่านหรือองค์ประกอบ wchar_t ที่เก็บไว้ในสตริง / wstring นั้น ด้วยเหตุผลดังกล่าวกลุ่มผู้ห่อหุ้ม gtk / glib C ++ จึงได้พัฒนาGlib::ustringคลาสที่สามารถจัดการ utf-8 ได้

    หาก wchar_t ของคุณมีความยาว 32 บิตคุณสามารถใช้utf-32เป็นการเข้ารหัสแบบ Unicode และคุณสามารถจัดเก็บและจัดการกับสตริง Unicode โดยใช้การเข้ารหัสแบบตายตัว (utf-32 คือความยาวคงที่) ซึ่งหมายความ wstring ของs.size()ฟังก์ชั่นจะแล้วกลับมาในปริมาณที่เหมาะสมขององค์ประกอบ wchar_t และตัวอักษรตรรกะ

  2. ใช่ถ่านมีความยาวอย่างน้อย 8 บิตเสมอซึ่งหมายความว่ามันสามารถเก็บค่า ASCII ทั้งหมดได้
  3. ใช่คอมไพเลอร์รายใหญ่ทั้งหมดสนับสนุน

ฉันอยากรู้เกี่ยวกับ # 2 ฉันคิดว่า 7 บิตจะใช้ได้ในทางเทคนิคเช่นกัน? หรือจำเป็นต้องมีการจัดเก็บสิ่งใด ๆ ที่ผ่านมาตัวอักษร ASCII 7 บิต?
jalf

1
ใช่ jalf c89 ระบุช่วงที่น้อยที่สุดสำหรับประเภทพื้นฐานในเอกสารของ limit.h (สำหรับถ่านที่ไม่ได้ลงนามนั่นคือ 0.255 นาที) และระบบไบนารีบริสุทธิ์สำหรับประเภทจำนวนเต็ม มันเป็นไปตามถ่าน, ถ่านที่ไม่ได้ลงชื่อและถ่านที่ลงนามมีความยาวบิตต่ำสุดที่ 8 c ++ สืบทอดกฎเหล่านั้น
Johannes Schaub - litb

15
"นี่หมายถึงฟังก์ชั่น s.size () ของ wstring ของคุณจะส่งคืนองค์ประกอบ wchar_t ที่เหมาะสมและอักขระตรรกะ" สิ่งนี้ไม่ถูกต้องทั้งหมดแม้แต่กับ Unicode มันจะมีความแม่นยำมากขึ้นในการพูด codepoint กว่า "ตัวอักษรเชิงตรรกะ" แม้ใน UTF-32 อักขระที่กำหนดอาจประกอบด้วย codepoint หลายตัว
Logan Capaldo

คุณเป็นคนสำคัญที่บอกว่า C ++ ไม่มีการสนับสนุนพื้นเมืองสำหรับชุดอักขระ Unicode หรือไม่?
หมดเวลา Danila

1
"แต่มันจะไม่เข้าใจความหมายของจุดโค้ดยูนิโค้ด" บน Windows, std::wstringไม่ไม่
Deduplicator

5

ฉันมักจะใช้ std :: string เพื่อเก็บอักขระ utf-8 โดยไม่มีปัญหาใด ๆ เลย ฉันขอแนะนำให้ทำอย่างนี้เมื่อเชื่อมต่อกับ API ที่ใช้ utf-8 เป็นชนิดสตริงดั้งเดิมเช่นกัน

ตัวอย่างเช่นฉันใช้ utf-8 เมื่อเชื่อมต่อโค้ดของฉันกับล่าม Tcl

ข้อแม้ที่สำคัญคือความยาวของสตริง std :: ไม่เท่ากับจำนวนอักขระในสตริงอีกต่อไป


1
Juan: คุณหมายความว่าสตริง std :: สามารถเก็บอักขระ Unicode ได้ทั้งหมด แต่ความยาวจะรายงานไม่ถูกต้องหรือไม่? มีเหตุผลที่รายงานความยาวไม่ถูกต้องหรือไม่?

3
เมื่อใช้การเข้ารหัส utf-8 อักขระยูนิโค้ดตัวเดียวอาจประกอบด้วยหลายไบต์ นี่คือสาเหตุที่การเข้ารหัส utf-8 มีขนาดเล็กลงเมื่อใช้อักขระส่วนใหญ่จากชุด ascii มาตรฐาน คุณต้องใช้ฟังก์ชั่นพิเศษ (หรือหมุนของคุณเอง) เพื่อวัดจำนวนตัวอักษรยูนิโค้ด

2
(เฉพาะ Windows) ฟังก์ชั่นส่วนใหญ่คาดว่าสตริงที่ใช้ไบต์คือ ASCII และ 2 ไบต์เป็น Unicode รุ่นที่เก่ากว่า MBCS ซึ่งหมายความว่าถ้าคุณกำลังเก็บ Unicode 8 บิตที่คุณจะต้องแปลงเป็น Unicode 16 บิตเพื่อเรียกใช้ฟังก์ชัน windows มาตรฐาน (เว้นแต่ว่าคุณใช้ ASCII เพียงส่วนเดียว)
Greg Domjan

2
std :: string จะรายงานความยาวไม่ถูกต้องเท่านั้น แต่จะส่งออกสตริงที่ไม่ถูกต้องด้วย หากอักขระ Unicode บางตัวแสดงเป็น UTF-8 เป็นหลายไบต์ซึ่ง std :: string คิดว่าเป็นอักขระของตัวเองดังนั้นโดยทั่วไปแล้ว std :: string manipulation ของคุณตามปกติจะส่งออกอักขระแปลก ๆ หลายตัวซึ่งเป็นผลมาจากการตีความที่ผิด ๆ ตัวละครที่ถูกต้อง
หมดเวลา Danila

2
ฉันขอแนะนำให้เปลี่ยนคำตอบเพื่อระบุว่าสตริงควรคิดว่าเป็นเพียงคอนเทนเนอร์ของไบต์และถ้าไบต์เป็นการเข้ารหัสแบบ Unicode (UTF-8, UTF-16, ... ) คุณควรใช้ไลบรารีเฉพาะที่เข้าใจ ที่. API แบบอิงสตริง (ความยาว, สตริงย่อย, ฯลฯ ) จะล้มเหลวอย่างน่าสมเพชด้วยอักขระหลายไบต์ หากมีการปรับปรุงนี้ฉันจะลบ downvote ของฉัน
หมดเวลา Danila

4
  1. เมื่อคุณต้องการจัดเก็บอักขระ 'wide' (Unicode)
  2. ใช่: 255 คน (ยกเว้น 0)
  3. ใช่.
  4. นี่คือบทความเบื้องต้น: http://www.joelonsoftware.com/articles/Unicode.html

11
std :: string สามารถเก็บ 0 ได้ดี (ระวังให้ดีถ้าคุณเรียกใช้เมธอด c_str ())
Mr Fooz

3
และการพูดอย่างเคร่งครัดถ่านไม่รับประกันว่าจะเป็น 8 บิต :) ลิงค์ของคุณใน # 4 เป็นสิ่งที่ต้องอ่าน แต่ฉันไม่คิดว่ามันจะตอบคำถาม ตัวละครที่กว้างนั้นไม่เกี่ยวอะไรกับยูนิโค้ด มันเป็นเพียงตัวละครที่กว้างขึ้น (วิธีกว้างมากขึ้นอยู่กับ OS แต่โดยทั่วไปแล้ว 16 หรือ 32 บิต)
jalf

2
  1. เมื่อคุณต้องการใช้สตริง Unicode ไม่ใช่แค่ ascii มีประโยชน์สำหรับการทำให้เป็นสากล
  2. ใช่ แต่มันเล่นได้ไม่ดีกับ 0
  3. ไม่ได้ตระหนักถึงสิ่งที่ไม่ได้
  4. ตัวกว้างเป็นวิธีเฉพาะของคอมไพเลอร์ในการจัดการการแสดงความยาวคงที่ของตัวละครยูนิโค้ดสำหรับ MSVC มันเป็นตัวอักษร 2 ไบต์สำหรับ gcc ฉันเข้าใจว่ามันเป็น 4 ไบต์ และ +1 สำหรับhttp://www.joelonsoftware.com/articles/Unicode.html

1
2. std :: string สามารถเก็บค่า NULL ไว้ได้ นอกจากนี้ยังสามารถเก็บ utf-8 และอักขระที่กว้างเช่นกัน

@ Juan: ทำให้ฉันสับสนอีกครั้ง ถ้า std :: string สามารถเก็บอักขระ Unicode ได้สิ่งใดที่พิเศษกับ std :: wstring

1
@Appu: std :: string สามารถเก็บอักขระ Unicode ได้ UTF-8 มีมาตรฐานยูนิโคดจำนวนหนึ่งที่กำหนดเป้าหมายที่ความกว้างอักขระที่แตกต่างกัน UTf8 กว้าง 8 บิต นอกจากนี้ยังมี UTF-16 และ UTF-32 ที่ความกว้าง 16 และ 32 บิตตามลำดับ
Greg D

ด้วย std :: wstring อักขระ Unicode แต่ละตัวสามารถเป็นหนึ่ง wchar_t เมื่อใช้การเข้ารหัสความยาวคงที่ ตัวอย่างเช่นหากคุณเลือกที่จะใช้ joel บนวิธีการใช้ซอฟต์แวร์เป็นลิงก์ของ Greg จากนั้นความยาวของ wstring คือจำนวนอักขระยูนิโค้ดในสตริง แต่จะใช้พื้นที่มากขึ้น

ฉันไม่ได้บอกว่ามันไม่สามารถถือ 0 '\ 0' และสิ่งที่ฉันหมายถึงไม่เล่นได้ดีคือวิธีการบางอย่างอาจไม่ให้ผลลัพธ์ที่คาดหวังซึ่งมีข้อมูลทั้งหมดของ wstring ดังนั้นคะแนนโหวตที่รุนแรง
Greg Domjan

2

แอปพลิเคชันที่ไม่พอใจกับอักขระที่แตกต่างกันเพียง 256 ตัวมีตัวเลือกในการใช้อักขระแบบกว้าง (มากกว่า 8 บิต) หรือการเข้ารหัสแบบความยาวผันแปร อักขระไวด์โดยทั่วไปต้องการพื้นที่มากกว่าการเข้ารหัสความยาวผันแปร แต่เร็วกว่าในการประมวลผล แอปพลิเคชั่นหลายภาษาที่ประมวลผลข้อความจำนวนมากมักใช้ตัวอักษรกว้างเมื่อประมวลผลข้อความ แต่แปลงเป็น UTF-8 เมื่อจัดเก็บลงดิสก์

ความแตกต่างเพียงอย่างเดียวระหว่าง a stringและ a wstringคือชนิดข้อมูลของอักขระที่จัดเก็บ สตริงจะจัดเก็บchars ที่มีขนาดรับประกันว่าอย่างน้อย 8 บิตดังนั้นคุณสามารถใช้สตริงสำหรับการประมวลผลเช่น ASCII, ISO-8859-15 หรือข้อความ UTF-8 มาตรฐานไม่ได้เกี่ยวกับชุดอักขระหรือการเข้ารหัส

คอมไพเลอร์ทุกตัวใช้ชุดอักขระที่มีอักขระ 128 ตัวแรกตรงกับ ASCII นี่เป็นกรณีที่คอมไพเลอร์ที่ใช้การเข้ารหัส UTF-8 สิ่งสำคัญที่ควรระวังเมื่อใช้สตริงใน UTF-8 หรือการเข้ารหัสความยาวตัวแปรอื่น ๆ คือดัชนีและความยาววัดเป็นไบต์ไม่ใช่อักขระ

ชนิดข้อมูลของ wstring คือ wchar_tซึ่งขนาดไม่ได้กำหนดไว้ในมาตรฐานยกเว้นว่าจะต้องมีขนาดใหญ่เท่ากับ char อย่างน้อย 16 บิตหรือ 32 บิต wstring สามารถใช้สำหรับการประมวลผลข้อความในการดำเนินการเข้ารหัสตัวกว้างที่กำหนดไว้ เนื่องจากการเข้ารหัสไม่ได้กำหนดไว้ในมาตรฐานจึงไม่ตรงไปตรงมาในการแปลงระหว่างสตริงและ wstrings ไม่มีใครสามารถสันนิษฐานได้ว่า wstrings มีการเข้ารหัสความยาวคงที่เช่นกัน

หากคุณไม่ต้องการการสนับสนุนหลายภาษาคุณอาจใช้ได้กับสตริงปกติเท่านั้น ในทางกลับกันหากคุณกำลังเขียนแอปพลิเคชันแบบกราฟิกมักจะเป็นกรณีที่ API รองรับอักขระที่มีความกว้างเท่านั้น จากนั้นคุณอาจต้องการใช้อักขระแบบกว้างเดียวกันเมื่อประมวลผลข้อความ โปรดทราบว่า UTF-16 เป็นการเข้ารหัสที่มีความยาวผันแปรได้ซึ่งหมายความว่าคุณไม่สามารถสันนิษฐานได้ว่าlength()จะส่งคืนจำนวนอักขระ หาก API ใช้การเข้ารหัสที่มีความยาวคงที่เช่น UCS-2 การประมวลผลจะกลายเป็นเรื่องง่าย การแปลงระหว่างอักขระไวด์และ UTF-8 ทำได้ยากในวิธีพกพา แต่จากนั้นอีกครั้ง API ส่วนติดต่อผู้ใช้ของคุณอาจสนับสนุนการแปลง


ดังนั้นการถอดความย่อหน้าแรก: แอปพลิเคชันที่ต้องการอักขระมากกว่า 256 ตัวจำเป็นต้องใช้การเข้ารหัสแบบหลายไบต์หรือแบบเข้ารหัสบางที _multibyte
Deduplicator

โดยทั่วไปการเข้ารหัส 16 และ 32 บิตเช่น UCS-2 และ UCS-4 ไม่ได้เรียกว่าการเข้ารหัสแบบหลายไบต์ มาตรฐาน C ++ แยกความแตกต่างระหว่างการเข้ารหัสแบบหลายไบต์และอักขระแบบกว้าง การแสดงอักขระที่กว้างใช้จำนวนคงที่ (โดยทั่วไปมากกว่า 8) บิตต่ออักขระ การเข้ารหัสที่ใช้ไบต์เดียวเพื่อเข้ารหัสอักขระที่พบบ่อยที่สุดและหลายไบต์เพื่อเข้ารหัสส่วนที่เหลือของชุดอักขระเรียกว่าการเข้ารหัสแบบหลายไบต์
Seppo Enarvi

ขออภัยความคิดเห็นเลอะเทอะ ควรมีการเข้ารหัสความยาวแปรผัน UTF-16 เป็นการเข้ารหัสแบบความยาวผันแปรเช่นเดียวกับ UTF-8 การแกล้งทำเป็นว่าไม่ใช่ความคิดที่เลว
Deduplicator

นั่นเป็นจุดที่ดี ไม่มีเหตุผลว่าทำไม wstrings ไม่สามารถใช้เพื่อจัดเก็บ UTF-16 (แทน UCS-2) ได้ แต่ความสะดวกในการเข้ารหัสที่มีความยาวคงที่จะหายไป
Seppo Enarvi

2

เป็นคำถามที่ดี! ฉันคิดว่าการเข้ารหัสข้อมูล (บางครั้งCHARSETยังเกี่ยวข้อง) คือการแสดงออกของหน่วยความจำกลไกการเพื่อบันทึกข้อมูลไปยังไฟล์หรือถ่ายโอนข้อมูลผ่านเครือข่ายดังนั้นฉันตอบคำถามนี้เป็น:

1. เมื่อใดที่ฉันควรใช้ std :: wstring บน std :: string

หากแพลตฟอร์มการเขียนโปรแกรมหรือฟังก์ชั่น API เป็นไบต์เดียวและเราต้องการประมวลผลหรือแยกข้อมูล Unicode บางอย่างเช่นอ่านจากไฟล์ Windows'.REG หรือกระแสข้อมูล 2 ไบต์ของเครือข่ายเราควรประกาศตัวแปร std :: wstring ให้ง่ายขึ้น ประมวลผลพวกเขา เช่น: wstring ws = L "中国 a" (หน่วยความจำ 6 octets: 0x4E2D 0x56FD 0x0061) เราสามารถใช้ ws [0] เพื่อรับตัวอักษร '中' และ ws [1] เพื่อรับตัวอักษร '国' และ ws [2] เพื่อ รับตัวอักษร 'a' ฯลฯ

2. std :: string สามารถเก็บชุดอักขระ ASCII ทั้งหมดรวมถึงอักขระพิเศษได้หรือไม่

ใช่. แต่แจ้งให้ทราบล่วงหน้า: American ASCII หมายถึง octet 0x00 ~ 0xFF แต่ละตัวแทนอักขระหนึ่งตัวรวมถึงข้อความที่พิมพ์ได้เช่น "123abc & * _ &" และคุณกล่าวว่าแบบพิเศษส่วนใหญ่พิมพ์เป็น '' หลีกเลี่ยงความสับสนในการแก้ไขหรือขั้ว และบางประเทศก็ขยายชุดอักขระ "ASCII" ของตัวเองเช่นจีนใช้ 2 อ็อกเท็ตเพื่อแทนอักขระหนึ่งตัว

3. is std :: wstring รองรับโดยคอมไพเลอร์ C ++ ยอดนิยมทั้งหมดหรือไม่

อาจจะหรือเป็นส่วนใหญ่ ฉันใช้แล้ว: VC ++ 6 และ GCC 3.3, ใช่

4. "ตัวกว้าง" คืออะไร?

อักขระแบบกว้างส่วนใหญ่ระบุว่าใช้ 2 octets หรือ 4 octets เพื่อเก็บอักขระของทุกประเทศ 2 octet UCS2 เป็นตัวอย่างตัวแทนและต่อไปเช่นภาษาอังกฤษ 'a' หน่วยความจำของมันคือ 2 octet ของ 0x0061 (เทียบกับ ASCII 'a หน่วยความจำของคือ 1 octet 0x61)


0

มีคำตอบที่ดีมากที่นี่ แต่ฉันคิดว่ามีสองสิ่งที่ฉันสามารถเพิ่มเกี่ยวกับ Windows / Visual Studio Tis นั้นขึ้นอยู่กับประสบการณ์ของฉันกับ VS2015 บน Linux โดยทั่วไปคำตอบคือการใช้ UTF-8 เข้ารหัสstd::stringทุกที่ บน Windows / VS มันซับซ้อนมากขึ้น นี่คือเหตุผล Windows คาดว่าสตริงที่จัดเก็บโดยใช้chars จะถูกเข้ารหัสโดยใช้เพจรหัสภาษา นี่เป็นชุดอักขระ ASCII เกือบตลอดเวลาตามด้วยอักขระพิเศษอื่น ๆ อีก 128 ตัวขึ้นอยู่กับตำแหน่งของคุณ ให้ฉันบอกว่าสิ่งนี้ไม่เพียงแค่เมื่อใช้ Windows API มีอีกสามสถานที่สำคัญที่สตริงเหล่านี้โต้ตอบกับ C ++ มาตรฐาน เหล่านี้เป็นตัวอักษรสตริงออกไปstd::coutใช้<<และผ่านชื่อไฟล์std::fstreamและผ่านชื่อไฟล์ที่จะ

ฉันจะอยู่ตรงหน้าว่าฉันเป็นโปรแกรมเมอร์ไม่ใช่ผู้เชี่ยวชาญด้านภาษา ฉันขอขอบคุณ USC2 และ UTF-16 ที่ไม่เหมือนกัน แต่สำหรับวัตถุประสงค์ของฉันพวกเขาอยู่ใกล้พอที่จะเปลี่ยนได้และฉันใช้พวกเขาเช่นที่นี่ ฉันไม่แน่ใจจริงๆว่า Windows ใช้อะไร แต่โดยทั่วไปฉันไม่จำเป็นต้องรู้เช่นกัน ฉันได้กล่าว UCS2 ในคำตอบนี้ดังนั้นขออภัยล่วงหน้าหากฉันไม่พอใจใครก็ตามที่ไม่รู้เรื่องนี้และฉันยินดีที่จะเปลี่ยนแปลงหากฉันมีสิ่งผิดปกติ

สตริงตัวอักษร

หากคุณป้อนตัวอักษรสตริงที่มีเฉพาะอักขระที่สามารถแสดงได้ด้วยเพจรหัสของคุณ VS จะจัดเก็บไว้ในไฟล์ของคุณด้วย 1 ไบต์ต่อการเข้ารหัสอักขระตามเพจรหัสของคุณ โปรดทราบว่าถ้าคุณเปลี่ยนเพจเพจของคุณหรือให้แหล่งที่มาของคุณแก่นักพัฒนาอื่นโดยใช้หน้ารหัสที่แตกต่างกันฉันคิดว่า (แต่ยังไม่ได้ทดสอบ) ว่าตัวละครจะจบลงแตกต่างกัน หากคุณเรียกใช้รหัสของคุณบนคอมพิวเตอร์โดยใช้รหัสหน้าอื่นฉันไม่แน่ใจว่าตัวละครนั้นจะเปลี่ยนไปหรือไม่

หากคุณป้อนตัวอักษรสตริงใด ๆ ที่ไม่สามารถแทนด้วยเพจรหัสของคุณ VS จะขอให้คุณบันทึกไฟล์เป็น Unicode ไฟล์จะถูกเข้ารหัสเป็น UTF-8 ซึ่งหมายความว่าอักขระที่ไม่ใช่ ASCII ทั้งหมด (รวมถึงอักขระที่อยู่ในเพจรหัสของคุณ) จะถูกแสดงด้วย 2 หรือมากกว่าไบต์ ซึ่งหมายความว่าถ้าคุณให้แหล่งข้อมูลแก่บุคคลอื่นแหล่งข้อมูลจะมีลักษณะเดียวกัน อย่างไรก็ตามก่อนที่จะผ่านแหล่งที่มาเพื่อคอมไพเลอร์ VS แปลง UTF-8 ?ข้อความที่เข้ารหัสที่หน้ารหัสเข้ารหัสข้อความและตัวอักษรใดหายไปจากหน้ารหัสจะถูกแทนที่ด้วย

วิธีเดียวที่จะรับประกันว่าจะแสดงสตริงตัวอักษร Unicode ใน VS อย่างถูกต้องคือนำหน้าสตริงตัวอักษรด้วยการLทำให้เป็นตัวอักษรสตริงที่กว้าง ในกรณีนี้ VS จะแปลงข้อความที่เข้ารหัส UTF-8 จากไฟล์เป็น UCS2 จากนั้นคุณต้องผ่านตัวอักษรข้อความนี้เป็นstd::wstringตัวสร้างหรือที่คุณจำเป็นต้องแปลงเป็น UTF-8 std::stringและใส่ไว้ใน หรือถ้าคุณต้องการคุณสามารถใช้ฟังก์ชั่น Windows API เพื่อเข้ารหัสโดยใช้หน้ารหัสของคุณเพื่อใส่ลงในstd::stringแต่คุณอาจไม่ได้ใช้ตัวอักษรสตริงที่กว้างเช่นกัน

มาตรฐาน :: ศาล

เมื่อส่งออกไปยังคอนโซลโดยใช้<<คุณสามารถใช้ได้เท่านั้นstd::stringไม่std::wstringและข้อความจะต้องถูกเข้ารหัสโดยใช้เพจรหัสของโลแคล หากคุณมีstd::wstringแล้วคุณต้องแปลงมันโดยใช้หนึ่งในฟังก์ชั่น Windows API และตัวละครใด ๆ ที่ไม่ได้อยู่ในเพจของคุณได้รับการแทนที่ด้วย?(บางทีคุณสามารถเปลี่ยนตัวละครฉันจำไม่ได้)

std :: ชื่อไฟล์ f สตรีม

Windows OS ใช้ UCS2 / UTF-16 สำหรับชื่อไฟล์ดังนั้นไม่ว่าเพจรหัสของคุณจะมีไฟล์อะไรก็ตามที่มีอักขระ Unicode std::wstringแต่ที่นี้หมายถึงว่าในการเข้าถึงหรือสร้างไฟล์ที่มีตัวอักษรที่ไม่ได้อยู่ในเพจของคุณคุณต้องใช้ ไม่มีวิธีอื่น นี่เป็นส่วนขยายเฉพาะของ Microsoft ซึ่งstd::fstreamอาจไม่ได้รวบรวมในระบบอื่น หากคุณใช้ std :: string คุณจะสามารถใช้ชื่อไฟล์ที่มีเฉพาะอักขระบนเพจรหัสของคุณเท่านั้น

ตัวเลือกของคุณ

หากคุณเพิ่งทำงานบน Linux คุณก็อาจจะไม่ได้ไกลขนาดนี้ เพียงใช้ UTF-8 ในstd::stringทุกที่

หากคุณกำลังทำงานบน Windows เพียงแค่ใช้ UCS2 std::wstringทุกที่ นักพิถีพิถันบางคนอาจบอกว่าใช้ UTF8 แล้วแปลงเมื่อจำเป็น แต่ทำไมต้องรำคาญกับความยุ่งยาก

หากคุณเป็นแพลตฟอร์มข้ามก็เป็นระเบียบที่จะเปิดเผย หากคุณพยายามใช้ UTF-8 ทุกที่บน Windows คุณจะต้องระมัดระวังตัวอักษรสตริงและเอาต์พุตไปยังคอนโซล คุณสามารถทำลายสตริงของคุณได้อย่างง่ายดาย หากคุณใช้std::wstringทุกที่บน Linux คุณอาจไม่สามารถเข้าถึงเวอร์ชันกว้างstd::fstreamได้ดังนั้นคุณต้องทำการแปลง แต่ไม่มีความเสี่ยงต่อความเสียหาย โดยส่วนตัวแล้วฉันคิดว่านี่เป็นตัวเลือกที่ดีกว่า หลายคนไม่เห็นด้วย แต่ฉันไม่ใช่คนเดียว - เป็นเส้นทางที่ wxWidgets ใช้

อีกตัวเลือกหนึ่งอาจจะพิมพ์unicodestringเป็นstd::stringLinux และstd::wstringWindows และมีแมโครที่เรียกว่า UNI () ซึ่งนำหน้า L บน Windows และไม่มีอะไรบน Linux จากนั้นโค้ด

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

น่าจะใช้ได้ทั้งสองแพลตฟอร์มฉันคิดว่า

คำตอบ

ดังนั้นเพื่อตอบคำถามของคุณ

1) หากคุณกำลังเขียนโปรแกรมสำหรับ Windows อยู่ตลอดเวลาถ้าข้ามแพลตฟอร์มแล้วอาจจะตลอดเวลาเว้นแต่คุณต้องการจัดการกับปัญหาการทุจริตที่อาจเกิดขึ้นบน Windows หรือเขียนโค้ดบางอย่างกับแพลตฟอร์มเฉพาะ#ifdefsเพื่อแก้ไขความแตกต่างหากใช้เพียง ลินุกซ์ไม่เคย

2) ใช่ นอกจากนี้บน Linux คุณสามารถใช้มันสำหรับ Unicode ทั้งหมดได้เช่นกัน บน Windows คุณสามารถใช้สำหรับ unicode ทั้งหมดหากคุณเลือกเข้ารหัสด้วยตนเองโดยใช้ UTF-8 แต่ Windows API และคลาส C ++ มาตรฐานจะคาดว่าstd::stringจะถูกเข้ารหัสโดยใช้รหัสเพจ ซึ่งรวมถึง ASCII ทั้งหมดรวมทั้งอักขระอื่นอีก 128 ตัวซึ่งอาจเปลี่ยนแปลงได้ขึ้นอยู่กับเพจรหัสที่คอมพิวเตอร์ของคุณตั้งค่าให้ใช้

3) ฉันเชื่ออย่างนั้น แต่ถ้าไม่เช่นนั้นมันเป็นเพียง typedef ง่ายๆของ 'std :: basic_string' ใช้wchar_tแทนchar

4) ตัวกว้างเป็นพิมพ์ตัวอักษรที่มีขนาดใหญ่กว่า 1 ไบต์มาตรฐานcharประเภท บน Windows มันคือ 2 ไบต์บน Linux มันคือ 4 ไบต์


1
เกี่ยวกับ "อย่างไรก็ตามก่อนที่จะส่งซอร์สไปยังคอมไพเลอร์ VS จะแปลงข้อความที่เข้ารหัส UTF-8 เป็นข้อความที่เข้ารหัสหน้าโค้ดและอักขระใด ๆ ที่หายไปจากโค้ดเพจจะถูกแทนที่ด้วย?" -> ฉันไม่คิดว่ามันจะเป็นจริงเมื่อคอมไพเลอร์ใช้การเข้ารหัส UTF-8 (ใช้/utf-8)
Roi Danton

ฉันไม่ได้ตระหนักถึงสิ่งนี้เป็นตัวเลือก จากลิงค์นี้docs.microsoft.com/en-us/cpp/build/reference/ ......ดูเหมือนว่าไม่มีช่องทำเครื่องหมายให้เลือกในคุณสมบัติโครงการคุณต้องเพิ่มเป็นตัวเลือกบรรทัดคำสั่งเพิ่มเติม จุดที่ดี!
Phil Rosenberg

-2

1) ตามที่ Greg กล่าวไว้ wstring มีประโยชน์สำหรับการทำให้เป็นสากลนั่นคือเมื่อคุณจะปล่อยผลิตภัณฑ์ของคุณในภาษาอื่นที่ไม่ใช่ภาษาอังกฤษ

4) ลองดูตัวละครตัวกว้าง http://en.wikipedia.org/wiki/Wide_character


-6

เมื่อใดที่คุณไม่ควรใช้ตัวอักษรกว้าง

เมื่อคุณเขียนโค้ดก่อนปี 1990

เห็นได้ชัดว่าฉันกำลังพลิก แต่จริงๆแล้วมันเป็นศตวรรษที่ 21 ในขณะนี้ 127 ตัวอักษรมีความยาวตั้งแต่หยุดให้เพียงพอ ใช่คุณสามารถใช้ UTF8 ได้ แต่ทำไมต้องปวดหัว?


16
@dave: ฉันไม่รู้ว่า UTF-8 สร้างอาการปวดหัวแบบไหนดีกว่า Widechars (UTF-16) ใน UTF-16 คุณยังมีอักขระหลายตัว
Pavel Radzivilovsky

ปัญหาคือว่าถ้าคุณอยู่ที่ใดก็ได้ยกเว้นประเทศที่พูดภาษาอังกฤษคุณควรใช้ wchar_t ไม่ต้องพูดถึงว่าตัวอักษรบางตัวมีจำนวนอักขระมากกว่าที่คุณสามารถใส่ลงในไบต์ได้ เราอยู่ที่นั่นบน DOS โรคจิตเภท Codepage ไม่ขอบคุณไม่มาก ..
Swift - Friday Pie

1
@Swift ปัญหาที่เกิดขึ้นwchar_tคือขนาดและความหมายของระบบนั้นขึ้นอยู่กับระบบปฏิบัติการ มันแค่แลกเปลี่ยนปัญหาเก่ากับปัญหาใหม่ ในขณะที่chara charไม่คำนึงถึงระบบปฏิบัติการ (บนแพลตฟอร์มที่คล้ายกันอย่างน้อย) ดังนั้นเราก็อาจใช้ UTF-8 ห่อทุกอย่างเป็นลำดับของchars และเสียใจที่ C ++ ปล่อยเราไว้อย่างสมบูรณ์ด้วยตัวเราเองโดยไม่มีวิธีมาตรฐานสำหรับการวัดการจัดทำดัชนีการค้นหาและอื่น ๆ ภายในลำดับดังกล่าว
underscore_d

1
@ Swift คุณดูเหมือนจะย้อนกลับมาได้อย่างสมบูรณ์ wchar_tเป็นประเภทข้อมูลที่มีความกว้างคงที่ดังนั้นอาร์เรย์ 10 wchar_tจะมีจำนวนsizeof(wchar_t) * 10ไบต์ของแพลตฟอร์มเสมอ และ UTF-16 เป็นการเข้ารหัสความกว้างแปรผันซึ่งตัวอักษรอาจประกอบด้วย codepoints 1 หรือ 2 16 บิต (และ s / 16/8 / g สำหรับ UTF-8)
underscore_d

1
@SteveHollasch การแสดง wchar_t ของสตริงบน windows จะเข้ารหัสอักขระที่มากกว่า FFFF ในฐานะตัวแทนเสมือนตัวแทนพิเศษและอื่น ๆ จะใช้องค์ประกอบ wchar_t เพียงองค์ประกอบเดียวเท่านั้น ดังนั้นการแสดงนั้นจะไม่สามารถใช้งานร่วมกับการแสดงที่สร้างขึ้นโดยคอมไพเลอร์ gnu (ที่ตัวละครทุกตัวที่น้อยกว่า FFFF จะมีคำศูนย์อยู่ข้างหน้า) สิ่งที่เก็บไว้ใน wchar_t ถูกกำหนดโดยโปรแกรมเมอร์และคอมไพเลอร์ไม่ใช่โดยข้อตกลงบางอย่าง
Swift - Friday Pie
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.