ควรถือว่า UTF-16 เป็นอันตรายหรือไม่?


432

ฉันจะถามสิ่งที่อาจเป็นคำถามโต้เถียง: "หนึ่งในการเข้ารหัสที่นิยมมากที่สุด UTF-16 ถือว่าเป็นอันตรายหรือไม่"

ทำไมฉันถึงถามคำถามนี้

มีโปรแกรมเมอร์กี่คนที่ตระหนักถึงความจริงที่ว่า UTF-16 เป็นความยาวแปรผันได้หรือไม่? โดยสิ่งนี้ฉันหมายความว่ามีจุดรหัสที่แสดงเป็นคู่ตัวแทนแทนใช้องค์ประกอบมากกว่าหนึ่ง

ฉันรู้ว่า; แอปพลิเคชั่นเฟรมเวิร์กและ API จำนวนมากใช้ UTF-16 เช่นสตริงของ Java, สตริง C #, Win32 APIs, ไลบรารี Qt GUI, ไลบรารี ICU Unicode เป็นต้นอย่างไรก็ตามจากทั้งหมดนี้มีข้อบกพร่องพื้นฐานมากมายในการประมวลผล ของอักขระจาก BMP (อักขระที่ควรเข้ารหัสโดยใช้สององค์ประกอบ UTF-16)

ตัวอย่างเช่นลองแก้ไขหนึ่งในตัวละครเหล่านี้:

  • LE ( U + 1D11E ) ดนตรีซิมโฟนี G CLEF
  • 𝕥 ( U + 1D565 ) คณิตศาสตร์สองชั้นซ้อนขนาดเล็ก
  • 𝟶 ( U + 1D7F6 ) ศูนย์หลักสี่เชิงคณิตศาสตร์
  • 𠂊 ( U + 2008A ) ตัวละครฮัน

คุณอาจพลาดบางอย่างขึ้นอยู่กับแบบอักษรที่คุณติดตั้ง ตัวละครเหล่านี้ล้วนอยู่นอก BMP (Basic Multilingual Plane) หากคุณไม่สามารถมองเห็นตัวละครเหล่านี้คุณยังสามารถลองมองพวกเขาในการอ้างอิงอักขระ Unicode

ตัวอย่างเช่นลองสร้างชื่อไฟล์ใน Windows ที่มีอักขระเหล่านี้ พยายามลบอักขระเหล่านี้ด้วย "backspace" เพื่อดูว่าอักขระเหล่านั้นทำงานอย่างไรในแอปพลิเคชันต่างๆที่ใช้ UTF-16 ฉันทำการทดสอบและผลลัพธ์ค่อนข้างแย่:

  • Opera มีปัญหาในการแก้ไข (ลบ 2 กดที่ backspace)
  • Notepad ไม่สามารถจัดการกับมันได้อย่างถูกต้อง (ลบ 2 กดบน backspace ที่จำเป็น)
  • การแก้ไขชื่อไฟล์ในหน้าต่างไดอะล็อกเสีย (ต้องลบ 2 กดที่ backspace)
  • ทุกการใช้งาน Qt3 ไม่สามารถจัดการกับพวกเขา - การแสดงสองสี่เหลี่ยมที่ว่างแทนสัญลักษณ์หนึ่ง
  • Python เข้ารหัสอักขระดังกล่าวไม่ถูกต้องเมื่อใช้โดยตรงu'X'!=unicode('X','utf-16')ในบางแพลตฟอร์มเมื่อ X เป็นอักขระนอก BMP
  • Python 2.5 unicodedata ล้มเหลวในการรับคุณสมบัติของอักขระดังกล่าวเมื่อไพ ธ อนคอมไพล์ด้วยสตริง UTF-16 Unicode
  • StackOverflow ดูเหมือนจะลบอักขระเหล่านี้ออกจากข้อความหากแก้ไขโดยตรงในรูปแบบอักขระ Unicode (อักขระเหล่านี้แสดงโดยใช้ HTML Unicode escapes)
  • กล่องข้อความ WinForms อาจสร้างสตริงที่ไม่ถูกต้องเมื่อถูก จำกัด ด้วย MaxLength

ดูเหมือนว่าข้อผิดพลาดดังกล่าวหาได้ง่ายมากในหลาย ๆ แอปพลิเคชั่นที่ใช้ UTF-16

ดังนั้น ... คุณคิดว่า UTF-16 น่าจะถือว่าเป็นอันตรายหรือไม่?


64
ไม่ถูกต้องจริงๆ ฉันอธิบายว่าถ้าคุณเขียน "שָׁ" อักขระผสมที่ประกอบด้วย "ש", "ָ" และ "ׁ", vovels แล้วการลบแต่ละตัวนั้นเป็นตรรกะคุณลบหนึ่งรหัสจุดเมื่อคุณกด " backspace "และลบอักขระทั้งหมดรวมถึง vovels เมื่อกด" del " แต่คุณไม่เคยผลิตที่ผิดกฎหมายรัฐของข้อความ - จุดรหัสผิดกฎหมาย ดังนั้นสถานการณ์เมื่อคุณกด backspace และรับข้อความผิดกฎหมายไม่ถูกต้อง

41
CiscoIPPhone: หากมีข้อผิดพลาด "รายงานหลายครั้งหลายครั้งโดยผู้คนหลายคน" จากนั้นสองสามปีต่อมาผู้พัฒนาเขียนลงในบล็อกของนักพัฒนาซอฟต์แวร์ที่ "เชื่อหรือไม่ว่าพฤติกรรมนั้นเป็นเจตนาส่วนใหญ่!" มันเบา ๆ ) ฉันมักจะคิดว่ามันอาจไม่ใช่การตัดสินใจออกแบบที่ดีที่สุดเท่าที่เคยมีมา :-) เพียงเพราะเจตนาไม่ได้หมายความว่าไม่ใช่ข้อผิดพลาด

145
โพสต์ยอดเยี่ยม UTF-16 เป็น "ที่สุดของทั้งสองโลก": UTF8 นั้นมีความยาวผันแปรได้ครอบคลุม Unicode ทั้งหมดต้องการอัลกอริธึมการแปลงไปและกลับจาก codepoints แบบดิบ จำกัด ถึง ASCII และไม่มีปัญหาเรื่องความเอนเอียง UTF32 มีความยาวคงที่ไม่ต้องมีการแปลง แต่ใช้พื้นที่มากขึ้นและมีปัญหาเรื่องความเอน จนถึงตอนนี้คุณสามารถใช้ UTF32 ภายในและ UTF8 สำหรับการทำให้เป็นอนุกรม แต่ UTF16 ไม่มีประโยชน์อะไร: มันขึ้นอยู่กับ endian ความยาวผันแปรของมันใช้พื้นที่มากมายไม่เข้ากันได้กับ ASCII ความพยายามที่จำเป็นในการจัดการกับ UTF16 อย่างถูกต้องสามารถใช้กับ UTF8 ได้ดีขึ้น
Kerrek SB

26
@Ian: UTF-8 ไม่มีคำเตือนเหมือนกับ UTF-8 คุณไม่สามารถตั้งตัวแทนใน UTF-8 ได้ UTF-8 ไม่ได้ปลอมตัวเป็นสิ่งที่ไม่ใช่ แต่โปรแกรมเมอร์ส่วนใหญ่ที่ใช้ UTF-16 ใช้งานผิด ฉันรู้ว่า. ฉันเคยดูพวกเขาครั้งแล้วครั้งเล่าและอีกครั้งและอีกครั้งและอีกครั้ง
tchrist

18
นอกจากนี้ UTF-8 ยังไม่มีปัญหาเพราะทุกคนถือว่าเป็นการเข้ารหัสความกว้างของตัวแปร เหตุผลที่ UTF-16 มีปัญหาคือเพราะทุกคนปฏิบัติเหมือนการเข้ารหัสความกว้างคงที่
Christoffer Hammarström

คำตอบ:


340

นี่คือคำตอบเก่า
ดูUTF-8 ทุกที่สำหรับการอัพเดทล่าสุด

ความคิดเห็นที่: ใช่ UTF-16 ควรได้รับการพิจารณาเป็นอันตราย เหตุผลที่มันมีอยู่ก็เพราะว่าเมื่อก่อนเคยมีความเชื่อที่เข้าใจผิดว่า Widechar จะเป็นสิ่งที่ UCS-4 เป็นอยู่ตอนนี้

แม้จะมี "แองโกล - centrism" ของ UTF-8 แต่ก็ควรได้รับการพิจารณาให้เป็นประโยชน์สำหรับการเข้ารหัสข้อความเท่านั้น หนึ่งสามารถยืนยันว่าซอร์สโค้ดของโปรแกรม, หน้าเว็บและไฟล์ XML, ชื่อไฟล์ OS และส่วนต่อประสานข้อความข้อความจากคอมพิวเตอร์สู่คอมพิวเตอร์อื่น ๆ ไม่ควรมีอยู่ แต่เมื่อพวกเขาทำข้อความไม่ได้มีไว้สำหรับผู้อ่านที่เป็นมนุษย์เท่านั้น

ในทางตรงกันข้ามค่าใช้จ่าย UTF-8 เป็นราคาขนาดเล็กที่ต้องจ่ายในขณะที่มีข้อได้เปรียบที่สำคัญ char*ข้อดีเช่นเข้ากันได้กับรหัสไม่รู้ว่าเพียงแค่ผ่านสตริง นี่คือสิ่งที่ดี มีอักขระที่มีประโยชน์น้อยซึ่งเป็น SHORTER ใน UTF-16 มากกว่าอักขระ UTF-8

ฉันเชื่อว่าการเข้ารหัสอื่น ๆ ทั้งหมดจะตายในที่สุด สิ่งนี้เกี่ยวข้องกับ MS-Windows, Java, ICU, python หยุดใช้มันเป็นรายการโปรด หลังจากการวิจัยและการอภิปรายเป็นระยะเวลานานข้อตกลงการพัฒนาที่บริษัท ของฉันสั่งห้ามใช้ UTF-16 ทุกที่ยกเว้นการเรียกใช้ OS API และสิ่งนี้แม้จะมีความสำคัญของประสิทธิภาพในแอปพลิเคชันของเราและข้อเท็จจริงที่ว่าเราใช้ Windows ฟังก์ชั่นการแปลงถูกพัฒนาขึ้นเพื่อแปลงเสมอสันนิษฐาน-UTF8 std::stringเพื่อพื้นเมือง UTF-16 ซึ่ง Windows เองไม่สนับสนุนอย่างถูกต้อง

สำหรับผู้ที่พูดว่า " ใช้สิ่งที่ต้องการในที่ที่จำเป็น " ฉันพูดว่า: มีประโยชน์มากในการใช้การเข้ารหัสแบบเดียวกันทุกที่และฉันไม่เห็นเหตุผลเพียงพอที่จะทำเช่นนั้น โดยเฉพาะอย่างยิ่งฉันคิดว่าการเพิ่มwchar_tใน C ++ นั้นเป็นข้อผิดพลาดและดังนั้นการเพิ่ม Unicode ใน C ++ 0x สิ่งที่จะต้องได้รับการเรียกร้องจากการใช้งาน STL ว่าเป็นที่ทุกคนstd::stringหรือchar*พารามิเตอร์ที่จะได้รับการพิจารณา Unicode ที่เข้ากันได้

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

ฉันต้องการแบ่งปันข้อมูลเพิ่มเติมเกี่ยวกับวิธีที่ฉันทำข้อความบน Windows และสิ่งที่ฉันแนะนำให้คนอื่น ๆ สำหรับการคอมไพล์เวลาที่ตรวจสอบความถูกต้องแบบ Unicode เวลาใช้งานง่ายและดีกว่าหลายแพลตฟอร์มของรหัส ข้อเสนอแนะแตกต่างอย่างมากจากสิ่งที่มักจะแนะนำว่าเป็นวิธีที่เหมาะสมในการใช้ Unicode บน windows แต่ในการวิจัยเชิงลึกของคำแนะนำเหล่านี้ส่งผลให้ข้อสรุปเดียวกัน ดังนั้นที่นี่ไป:

  • ห้ามใช้wchar_tหรือstd::wstringในสถานที่อื่นนอกเหนือจากจุดติดกับ API ที่ยอมรับ UTF-16
  • อย่าใช้_T("")หรือL""UTF-16 ตัวอักษร (IMO เหล่านี้ควรถูกนำออกจากมาตรฐานซึ่งเป็นส่วนหนึ่งของการคัดค้าน UTF-16)
  • อย่าใช้ประเภทฟังก์ชั่นหรืออนุพันธ์ของพวกเขาที่มีความไวต่อความ_UNICODEคงที่เช่นหรือLPTSTRCreateWindow()
  • กระนั้น_UNICODEก็มีการกำหนดไว้เสมอเพื่อหลีกเลี่ยงการส่งผ่านสchar*ตริไปยัง WinAPI ในการรวบรวมอย่างเงียบ ๆ
  • std::stringsและchar*ที่ใดก็ได้ในโปรแกรมถือว่าเป็น UTF-8 (ถ้าไม่ได้พูดเป็นอย่างอื่น)
  • สตริงทั้งหมดของฉันมีstd::stringแต่คุณสามารถส่งผ่าน char * convert(const std::string &)หรือสตริงตัวอักษรที่จะ
  • ใช้ฟังก์ชัน Win32 เท่านั้นที่ยอมรับ widechars ( LPWSTR) ผู้ที่ไม่เคยยอมรับหรือLPTSTR LPSTRผ่านพารามิเตอร์ด้วยวิธีนี้:

    ::SetWindowTextW(Utils::convert(someStdString or "string litteral").c_str())
    

    (นโยบายใช้ฟังก์ชันการแปลงด้านล่าง)

  • ด้วยสตริง MFC:

    CString someoneElse; // something that arrived from MFC. Converted as soon as possible, before passing any further away from the API call:
    
    std::string s = str(boost::format("Hello %s\n") % Convert(someoneElse));
    AfxMessageBox(MfcUtils::Convert(s), _T("Error"), MB_OK);
    
  • การทำงานกับไฟล์ชื่อไฟล์และ fstream บน Windows:

    • อย่าส่งต่อstd::stringหรือconst char*โต้แย้งชื่อไฟล์ไปยังfstreamครอบครัว MSVC STL ไม่สนับสนุนอาร์กิวเมนต์ UTF-8 แต่มีส่วนขยายที่ไม่ได้มาตรฐานซึ่งควรใช้ดังนี้:
    • แปลงstd::stringข้อโต้แย้งstd::wstringกับUtils::Convert:

      std::ifstream ifs(Utils::Convert("hello"),
                        std::ios_base::in |
                        std::ios_base::binary);
      

      เราจะต้องลบการแปลงด้วยตนเองเมื่อทัศนคติของ MSVC ต่อการfstreamเปลี่ยนแปลง

    • รหัสนี้ไม่ใช่หลายแพลตฟอร์มและอาจต้องเปลี่ยนด้วยตนเองในอนาคต
    • ดูfstreamงานวิจัย unicode / การอภิปราย 4215 สำหรับข้อมูลเพิ่มเติม
    • ไม่ผลิตไฟล์ข้อความที่มีเนื้อหาที่ไม่ใช่ UTF8
    • หลีกเลี่ยงการใช้fopen()เหตุผล RAII / OOD หากจำเป็นให้ใช้_wfopen()และจัดการประชุม WinAPI ด้านบน

// For interface to win32 API functions
std::string convert(const std::wstring& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

std::wstring convert(const std::string& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

// Interface to MFC
std::string convert(const CString &mfcString)
{
#ifdef UNICODE
    return Utils::convert(std::wstring(mfcString.GetString()));
#else
    return mfcString.GetString();   // This branch is deprecated.
#endif
}

CString convert(const std::string &s)
{
#ifdef UNICODE
    return CString(Utils::convert(s).c_str());
#else
    Exceptions::Assert(false, "Unicode policy violation. See W569"); // This branch is deprecated as it does not support unicode
    return s.c_str();   
#endif
}

39
ฉันไม่เห็นด้วย ข้อได้เปรียบของ utf16 เหนือ utf8 สำหรับภาษาในเอเชียหลาย ๆ คนนั้นถือเป็นจุดสำคัญที่คุณทำ มันไร้เดียงสาที่จะหวังว่าญี่ปุ่นไทยจีน ฯลฯ จะยอมแพ้การเข้ารหัสนี้ ปัญหาที่ขัดแย้งกันระหว่าง charsets คือเมื่อ charsets ส่วนใหญ่ดูคล้ายกันยกเว้นความแตกต่าง ฉันขอแนะนำให้ใช้มาตรฐานใน: 7 บิตคงที่: iso-irv-170; ตัวแปร 8 บิต: utf8; ตัวแปร 16 บิต: utf16; คงที่ 32 บิต: ucs4

82
@Charles: ขอบคุณสำหรับการป้อนข้อมูลของคุณ จริงอักขระ BMP บางตัวมีความยาวเป็น UTF-8 มากกว่าใน UTF-16 แต่ลองมาดูกัน: ปัญหาไม่ได้อยู่ในหน่วยไบต์ที่ตัวอักษรจีน BMP ใช้ แต่ความซับซ้อนในการออกแบบซอฟต์แวร์ที่เกิดขึ้น หากโปรแกรมเมอร์ภาษาจีนต้องออกแบบตัวอักษรที่มีความยาวผันแปรได้ดูเหมือนว่า UTF-8 ยังคงเป็นราคาขนาดเล็กที่ต้องจ่ายเมื่อเปรียบเทียบกับตัวแปรอื่น ๆ ในระบบ เขาอาจใช้ UTF-16 เป็นอัลกอริธึมการบีบอัดหากพื้นที่มีความสำคัญดังนั้นแม้ว่ามันจะไม่ตรงกันสำหรับ LZ และหลังจาก LZ หรือการบีบอัดทั่วไปอื่น ๆ ทั้งคู่ใช้ขนาดและเอนโทรปีเดียวกัน

32
สิ่งที่ฉันพูดโดยทั่วไปคือความเรียบง่ายที่นำเสนอโดยการเข้ารหัส One ที่เข้ากันได้กับโปรแกรม char * ที่มีอยู่และยังเป็นที่นิยมที่สุดสำหรับทุกสิ่งในปัจจุบันนั้นเป็นไปไม่ได้ มันเกือบจะเหมือนในวัน "ธรรมดาธรรมดา" ที่ดี ต้องการเปิดไฟล์ที่มีชื่อหรือไม่? ไม่จำเป็นต้องสนใจว่าคุณทำ Unicode ประเภทใดเป็นต้นฉันขอแนะนำนักพัฒนาให้ จำกัด UTF-16 ไว้ในกรณีพิเศษที่มีการเพิ่มประสิทธิภาพที่รุนแรงซึ่งการทำงานที่มีค่าเพียงเล็กน้อยนั้นคุ้มค่ากับการทำงานเป็นเวลาหลายเดือน

17
Linux มีข้อกำหนดเฉพาะเมื่อเลือกใช้ UTF-8 ภายใน: ความเข้ากันได้กับ Unix Windows ไม่จำเป็นต้องทำเช่นนั้นและเมื่อนักพัฒนาใช้ Unicode พวกเขาได้เพิ่มฟังก์ชั่นการจัดการข้อความเกือบทั้งหมดใน UCS-2 และทำให้มัลติไบต์สามารถแปลงเป็น UCS-2 และโทรหาคนอื่นได้ หลังจากนั้นพวกเขาแทนที่ UCS-2 ด้วย UTF-16 Linux ในอีกทางหนึ่งเก็บไว้ในการเข้ารหัส 8 บิตและใช้ UTF-8 เนื่องจากเป็นตัวเลือกที่เหมาะสมในกรณีนั้น
Mircea Chirea

34
@Pavel Radzivilovsky: BTW งานเขียนของคุณเกี่ยวกับ"ฉันเชื่อว่าการเข้ารหัสอื่น ๆ ทั้งหมดจะตายในที่สุดนี่เกี่ยวข้องกับ MS-Windows, Java, ICU, python หยุดใช้เป็นรายการโปรดของพวกเขา" และ"โดยเฉพาะอย่างยิ่งฉันคิดว่าการเพิ่ม wchar_t ไปยัง C ++ นั้นเป็นความผิดพลาดและดังนั้นจึงเป็นการเพิ่ม Unicode ให้กับ C ++ Ox" มีทั้งไร้เดียงสาหรือหยิ่งมาก และนี่มาจากการเขียนโค้ดที่บ้านด้วย Linux และผู้ที่มีความสุขกับ UTF-8 chars ที่จะนำมันห้วนๆ: มันจะไม่เกิดขึ้น
paercebal

157

ตัวเข้ารหัส Unicode ไม่ใช่อักขระ! บางครั้งพวกเขาก็ไม่ได้ร่ายมนตร์ (รูปแบบภาพ)

ตัวอย่างบางส่วน:

  • codepoints ตัวเลขโรมันเช่น "ⅲ" (อักขระตัวเดียวที่ดูเหมือน "iii")
  • อักขระที่เน้นเสียงเช่น "á" ซึ่งสามารถแสดงเป็นอักขระที่รวมกันอย่างเดียว "\ u00e1" หรืออักขระและแยกเครื่องหมายกำกับ "\ u0061 \ u0301"
  • ตัวอักษรเช่นกรีกตัวพิมพ์เล็กซิกซึ่งมีรูปแบบที่แตกต่างกันสำหรับกลาง ("σ") และจุดสิ้นสุด ("ς") ของตำแหน่งคำ แต่ควรพิจารณาคำพ้องความหมายสำหรับการค้นหา
  • เครื่องหมายขีดคั่นที่มีเครื่องหมายกำกับ Unicode U + 00AD ซึ่งอาจแสดงหรือไม่แสดงให้เห็นด้วยสายตาขึ้นอยู่กับบริบทและจะถูกละเว้นสำหรับการค้นหาความหมาย

วิธีเดียวที่จะทำให้การแก้ไข Unicode ถูกต้องคือการใช้ห้องสมุดที่เขียนขึ้นโดยผู้เชี่ยวชาญหรือเป็นผู้เชี่ยวชาญและเขียนด้วยตัวเอง หากคุณกำลังนับ codepoints คุณกำลังมีชีวิตอยู่ในสภาพบาป


19
นี้. อย่างนี้มาก UTF-16 สามารถทำให้เกิดปัญหาได้ แต่แม้กระทั่งการใช้ UTF-32 ตลอดสามารถ (และจะ) ยังคงให้ปัญหาคุณ
bcat

11
ตัวละครคืออะไร? คุณสามารถกำหนดรหัสจุดเป็นตัวอักษรและได้รับโดยดีมาก ถ้าคุณหมายถึง glyph ที่ผู้ใช้มองเห็นนั่นเป็นอย่างอื่น
tchrist

7
@christ แน่ใจว่าสำหรับการจัดสรรพื้นที่ที่คำจำกัดความนั้นใช้ได้ แต่มีอะไรอีกไหม? ไม่มากนัก. หากคุณจัดการกับอักขระที่รวมเป็นอักขระตัวเดียว (เช่นสำหรับการลบหรือการดำเนินการ "ใช้อักขระ N ตัวแรก") คุณจะได้รับพฤติกรรมที่แปลกและผิด หากจุดรหัสมีความหมายเฉพาะเมื่อรวมกับอย่างน้อยคุณจะไม่สามารถจัดการได้ด้วยตัวเองในลักษณะที่สมเหตุสมผล
Voo

6
@Pierier นี่มันสายไปงานปาร์ตี้ แต่ฉันต้องแสดงความคิดเห็นในเรื่องนั้น บางภาษามีชุดการออกเสียงกำกับที่มีขนาดใหญ่มาก (cf เวียตนามเช่นmệtđừ) การมีชุดค่าผสมมากกว่าหนึ่งตัวอักษรต่อการออกเสียงนั้นมีประโยชน์มาก
asthasr

21
โน้ตเล็ก ๆ ในคำศัพท์: codepoints ไม่ตรงกับตัวอักษร Unicode ; สิ่งที่ Daniel กำลังพูดถึงคือตัวละครที่ผู้ใช้รับรู้ซึ่งตรงกับกลุ่ม Unicode Grapheme
Christoph

54

มีกฎง่ายๆเกี่ยวกับสิ่งที่ Unicode Transformation Form (UTF) ที่จะใช้: - utf-8 สำหรับการจัดเก็บและการสื่อสาร - utf-16 สำหรับการประมวลผลข้อมูล - คุณอาจใช้ utf-32 ถ้าแพลตฟอร์ม API ส่วนใหญ่ที่คุณใช้คือ utf-32 (พบได้ทั่วไปในโลก UNIX)

ระบบส่วนใหญ่ในปัจจุบันใช้ utf-16 (Windows, Mac OS, Java, .NET, ICU, Qt) ดูเอกสารนี้: http://unicode.org/notes/tn12/

กลับไปที่ "UTF-16 เป็นอันตราย" ฉันจะพูดว่า: ไม่แน่นอน

คนที่กลัวตัวแทน (คิดว่าพวกเขาเปลี่ยน Unicode เป็นการเข้ารหัสแบบความยาวผันแปร) ไม่เข้าใจความซับซ้อนอื่น ๆ (ใหญ่กว่า) ที่ทำให้การแมประหว่างตัวละครและจุดโค้ด Unicode ซับซ้อนมาก: การรวมตัวอักษร, ตัวเลือกการเปลี่ยนแปลง ตัวควบคุม ฯลฯ

เพียงอ่านซีรี่ส์นี้ที่นี่http://www.siao2.com/2009/06/29/9800913.aspxและดูว่า UTF-16 กลายเป็นปัญหาง่ายได้อย่างไร


26
โปรดเพิ่มตัวอย่างบางส่วนที่ UTF-32 เป็นเรื่องธรรมดาในโลก UNIX!
maxschlepzig

48
ไม่คุณไม่ต้องการใช้ UTF-16 ในการประมวลผลข้อมูล มันเป็นความเจ็บปวดในตูด มันมีข้อเสียทั้งหมดของ UTF-8 แต่ไม่มีข้อได้เปรียบ ทั้ง UTF-8 และ UTF-32 นั้นเหนือกว่าแฮ็คหินที่รู้จักกันในชื่อ Mrs UTF-16 ซึ่งมีนามสกุลเดิมคือ UCS-2
tchrist

34
ฉันเพิ่งพบข้อผิดพลาดในวิธีการของ Java core String Class equalsIgnoreCase(รวมถึงอื่น ๆ ใน class string) ที่จะไม่เคยมี Java ใช้ทั้ง UTF-8 หรือ UTF-32 มีกระสุนจำนวนมากนอนหลับอยู่ในรหัสใด ๆ ที่ใช้ UTF-16 และฉันก็เบื่อและเบื่อพวกเขา UTF-16 เป็นฝีร้ายที่ทำให้ซอฟต์แวร์ของเรามีข้อบกพร่องร้ายกาจตลอดไป เป็นอันตรายอย่างชัดเจนและควรเลิกใช้และห้าม
tchrist

7
@tchrist Wow ฟังก์ชั่นที่ไม่ต้องใช้ตัวแทน (เพราะมันถูกเขียนเมื่อไม่มีและมีเอกสารที่น่าเศร้าในแบบที่ทำให้มันเป็นไปไม่ได้ที่จะปรับตัว - มันระบุ. toUpperCase (char)) จะส่งผลให้เกิดพฤติกรรมที่ผิด? คุณทราบหรือไม่ว่าฟังก์ชั่น UTF-32 ที่มีรหัสจุดแผนที่ที่ล้าสมัยจะไม่สามารถจัดการกับสิ่งนี้ได้ดีกว่านี้? นอกจากนี้ Java API ทั้งหมดยังจัดการกับตัวแทนไม่ได้ดีเป็นพิเศษและจุดที่ซับซ้อนมากขึ้นเกี่ยวกับ Unicode ไม่ได้เลยและด้วยการเข้ารหัสที่ใช้ในภายหลังจะไม่สำคัญเลย
Voo

8
-1: ไม่มีเงื่อนไข.Substring(1)ใน. NET เป็นตัวอย่างเล็กน้อยของสิ่งที่ทำลายการสนับสนุนสำหรับไม่ใช่ BMP Unicode ทั้งหมด ทุกอย่างที่ใช้ UTF-16 มีปัญหานี้ มันง่ายเกินไปที่จะถือว่าเป็นการเข้ารหัสที่มีความกว้างคงที่และคุณเห็นปัญหาน้อยเกินไป ทำให้การเข้ารหัสที่เป็นอันตรายถ้าคุณต้องการสนับสนุน Unicode
Roman Starkov

43

ใช่แล้ว

ทำไม? มันจะทำอย่างไรกับรหัสการออกกำลังกาย

หากคุณดูสถิติการใช้งาน codepointเหล่านี้ในคลังข้อมูลขนาดใหญ่โดย Tom Christiansen คุณจะเห็นว่า codepoints BMP trans-8bit นั้นถูกใช้หลายคำสั่งถ้าขนาดมากกว่า codepoint ที่ไม่ใช่ BMP:

 2663710 U+002013 ‹–›  GC=Pd    EN DASH
 1065594 U+0000A0 ‹ ›  GC=Zs    NO-BREAK SPACE
 1009762 U+0000B1 ‹±›  GC=Sm    PLUS-MINUS SIGN
  784139 U+002212 ‹−›  GC=Sm    MINUS SIGN
  602377 U+002003 ‹ ›  GC=Zs    EM SPACE

 544 U+01D49E ‹𝒞›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL C
 450 U+01D4AF ‹𝒯›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL T
 385 U+01D4AE ‹𝒮›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL S
 292 U+01D49F ‹𝒟›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL D
 285 U+01D4B3 ‹𝒳›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL X

ใช้ TDD dictum: "รหัสที่ไม่ผ่านการทดสอบเป็นรหัสที่ใช้งานไม่ได้" และใช้ถ้อยคำใหม่เป็น "รหัสที่ไม่ได้ใช้งานเป็นรหัสที่ใช้งานไม่ได้" และคิดว่าโปรแกรมเมอร์ต้องจัดการ codepoints ที่ไม่ใช่ BMP บ่อยเพียงใด

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


16
"ข้อบกพร่องที่เกี่ยวข้องกับการไม่เกี่ยวข้องกับ UTF-16 เนื่องจากการเข้ารหัสความกว้างของตัวแปรมีแนวโน้มที่จะไม่มีข้อสังเกตมากกว่าข้อบกพร่องที่เทียบเท่าใน UTF-8" นี่คือแกนหลักของปัญหาและด้วยเหตุนี้คำตอบที่ถูกต้อง
Sean McMillan

3
แม่นยำ. หากการจัดการ UTF-8 ของคุณถูก borked จะเห็นได้ชัดทันที หากการจัดการ UTF-8 ของคุณถูก borked คุณจะสังเกตเห็นเฉพาะเมื่อคุณใส่อักขระ Han หรือสัญลักษณ์ทางคณิตศาสตร์ที่ผิดปกติ
หอยทากเครื่องกล

1
จริงมาก แต่ในทางกลับกันการทดสอบหน่วยสำหรับคุณควรขึ้นอยู่กับโชคในการค้นหาข้อบกพร่องในกรณีที่พบบ่อยน้อยคืออะไร
musiphil

@musiphil: ดังนั้นครั้งสุดท้ายที่คุณสร้างการทดสอบหน่วยสำหรับอักขระที่ไม่ใช่ BMP เมื่อใด
ninjalj

1
ในการอธิบายอย่างละเอียดเกี่ยวกับคำสั่งก่อนหน้าของฉัน: แม้จะมี UTF-8 คุณไม่สามารถมั่นใจได้ว่าคุณได้ครอบคลุมทุกกรณีหลังจากเห็นเพียงตัวอย่างการทำงาน เช่นเดียวกันกับ UTF-16: คุณต้องทดสอบว่ารหัสของคุณทำงานได้ทั้งแบบไม่มีตัวแทนและแบบตัวแทน (ใครบางคนอาจโต้แย้งว่า UTF-8 มีคดีสำคัญอย่างน้อยสี่คดีในขณะที่ UTF-16 มีเพียงสองคดีเท่านั้น)
musiphil

40

ฉันขอแนะนำว่าการคิด UTF-16 อาจถือว่าเป็นอันตรายกล่าวว่าคุณต้องเข้าใจยูนิโค้ดมากขึ้น

เนื่องจากฉันถูก downvoted ในการนำเสนอความคิดเห็นของฉันเกี่ยวกับคำถามแบบอัตนัยให้ฉันทำอย่างละเอียด อะไรคือสิ่งที่รบกวนจิตใจคุณเกี่ยวกับ UTF-16 คุณจะชอบไหมถ้าทุกอย่างถูกเข้ารหัสใน UTF-8 UTF-7? หรือวิธีการเกี่ยวกับ UCS-4 แน่นอนว่าแอปพลิเคชั่นบางตัวไม่ได้รับการออกแบบมาเพื่อจัดการกับรหัสอักขระทุกตัว แต่มีความจำเป็นโดยเฉพาะอย่างยิ่งในโดเมนข้อมูลทั่วโลกในปัจจุบันสำหรับการสื่อสารระหว่างขอบเขตระหว่างประเทศ

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

แก้ไข: เพื่อชี้แจง: เหตุใดจึงพิจารณาการใช้งานที่ไม่เหมาะสมของมาตรฐานเป็นภาพสะท้อนของคุณภาพของมาตรฐานเอง? ดังที่คนอื่น ๆ ได้กล่าวไว้ในภายหลังเพียงเพราะแอปพลิเคชันใช้เครื่องมือที่ไม่เหมาะสมไม่ได้หมายความว่าเครื่องมือนั้นเสีย หากเป็นกรณีนี้เราอาจพูดได้ว่า "คำหลัก var ถือว่าเป็นอันตราย" หรือ "เกลียวถือว่าเป็นอันตราย" ฉันคิดว่าคำถามสร้างความสับสนให้กับคุณภาพและลักษณะของมาตรฐานกับความยากลำบากที่โปรแกรมเมอร์จำนวนมากมีในการนำไปใช้และใช้งานอย่างเหมาะสมซึ่งฉันรู้สึกว่าเกิดจากการขาดความเข้าใจในการทำงานของยูนิโค้ด


33
-1: แล้วจะจัดการกับคำคัดค้านของ Artyom ได้อย่างไรแทนที่จะแค่ช่วยเขา?

8
BTW: เมื่อผมเริ่มเขียนบทความนี้ผมเกือบอยากจะเขียน "โจเอลไม่เกี่ยวกับบทความ Softeare ของ Unicode ควรได้รับการพิจารณาเป็นอันตราย" เพราะมีหลายความผิดพลาด ตัวอย่างเช่น: การเข้ารหัส utf-8 ใช้อักขระสูงสุด 4 ตัวและไม่ใช่ 6 นอกจากนี้มันไม่ได้แยกความแตกต่างระหว่าง UCS-2 และ UTF-16 ที่แตกต่างกันจริง ๆ และทำให้เกิดปัญหาที่ฉันพูดถึง

32
นอกจากนี้ควรสังเกตว่าเมื่อ Joel เขียนบทความนั้นมาตรฐาน UTF-8 WAS 6 ไบต์ไม่ใช่ 4 RFC 3629 เปลี่ยนมาตรฐานเป็น 4 ไบต์เป็นเวลาหลายเดือนหลังจากที่เขาเขียนบทความ เช่นเดียวกับสิ่งใด ๆ บนอินเทอร์เน็ตส่วนใหญ่จะจ่ายให้อ่านจากแหล่งข้อมูลมากกว่าหนึ่งแหล่งและเพื่อทราบอายุของแหล่งข้อมูลของคุณ ลิงค์ไม่ได้ตั้งใจจะให้เป็น "จุดจบทั้งหมดเป็นของทุกคน" แต่เป็นจุดเริ่มต้น

7
ฉันจะ pic: utf-8 หรือ utf-32 นั่นคือ: การเข้ารหัสความยาวตัวแปรในเกือบทุกกรณี (รวมถึง BMP) หรือการเข้ารหัสความยาวคงที่เสมอ

18
@iconiK: ไม่ต้องงี่เง่า UTF-16 ไม่ใช่มาตรฐานที่แท้จริงสำหรับการประมวลผลข้อความ แสดงให้ฉันเห็นว่าการเขียนโปรแกรม lanuage เหมาะกับการประมวลผลข้อความที่ Perl ซึ่งมี (ดีมานานกว่าทศวรรษ) ที่ใช้อักขระนามธรรมที่มีการแสดง UTF-8 พื้นฐานภายใน ด้วยเหตุนี้โปรแกรม Perl ทุกตัวจะจัดการ Unicode ทั้งหมดโดยอัตโนมัติ ความยาวของสตริงคือการนับในจุดรหัสไม่ใช่หน่วยรหัส สิ่งอื่นใดคือความโง่เขลาที่แท้จริง
tchrist

37

ไม่มีอะไรผิดปกติกับการเข้ารหัส Utf-16 แต่ภาษาที่ใช้กับหน่วย 16 บิตเป็นอักขระที่ควรได้รับการพิจารณาว่าออกแบบมาไม่ดี การมีประเภทชื่อ ' char' ซึ่งไม่ได้แสดงถึงตัวละครเสมอไปนั้นค่อนข้างสับสน เนื่องจากนักพัฒนาซอฟต์แวร์ส่วนใหญ่คาดว่าคนประเภท char จะเป็นตัวแทนของรหัสจุดหรือตัวอักษรรหัสจำนวนมากอาจจะแตกเมื่อสัมผัสกับตัวอักษรบียอนด์ BMP

อย่างไรก็ตามโปรดทราบว่าแม้การใช้ utf-32 ไม่ได้หมายความว่าจุดรหัส 32- บิตแต่ละจุดจะแสดงถึงตัวละครเสมอ เนื่องจากการรวมอักขระอักขระที่แท้จริงอาจประกอบด้วยจุดรหัสหลายจุด Unicode นั้นไม่สำคัญเลย

BTW อาจมีคลาสของบั๊กที่เหมือนกันกับแพลตฟอร์มและแอพพลิเคชั่นซึ่งคาดว่าอักขระจะเป็น 8 บิตซึ่งถูกเลี้ยงด้วย Utf-8


12
ในกรณีของ Java ถ้าคุณดูไทม์ไลน์ของพวกเขา ( java.com/en/javahistory/timeline.jsp ) คุณจะเห็นว่าการพัฒนา String ส่วนใหญ่เกิดขึ้นในขณะที่ Unicode คือ 16 บิต (เปลี่ยนในปี 1996) พวกเขาต้องขึ้นอยู่กับความสามารถในการจัดการจุดรหัส BMP ที่ไม่ใช่ความสับสน
Kathy Van Stone

10
@Kathy: ไม่ใช่ข้อแก้ตัวสำหรับ C # จริงๆ โดยทั่วไปฉันยอมรับว่าควรมีCodePointประเภทถือจุดรหัสเดียว (21 บิต) CodeUnitประเภทถือหน่วยรหัสเดียว (16 บิตสำหรับ UTF-16) และCharacterประเภทจะต้องรองรับกราฟสมบูรณ์ แต่นั่นทำให้มันใช้งานได้เทียบเท่ากับString...
Joey

1
คำตอบนี้อายุเกือบสองปี แต่ฉันไม่สามารถช่วย แต่แสดงความคิดเห็นได้ "การมีประเภทที่มีชื่อว่า 'char' ซึ่งไม่ได้แสดงถึงตัวละครเสมอไปเป็นเรื่องที่ค่อนข้างสับสน" และยังมีคนใช้มันตลอดเวลาใน C และชอบที่จะเป็นตัวแทนของข้อมูลจำนวนเต็มที่สามารถเก็บไว้ในไบต์เดียว
JAB

และฉันเห็นรหัส C จำนวนมากที่ไม่จัดการการเข้ารหัสอักขระอย่างถูกต้อง
dan04

1
C # มีข้อแก้ตัวต่าง ๆ : มันถูกออกแบบมาสำหรับ Windows และ Windows สร้างขึ้นใน UCS-2 (มันน่ารำคาญมากที่แม้กระทั่งทุกวันนี้ API ของ Windows ก็ไม่สามารถรองรับ UTF-8 ได้) นอกจากนี้ผมคิดว่าไมโครซอฟท์ต้องการ Java เข้ากันได้ (.NET 1.0 มีความเข้ากันได้ห้องสมุด Java แต่พวกเขาลดลงการสนับสนุน Java อย่างรวดเร็ว - ผมกำลังคาดเดานี้เป็นเพราะคดีของดวงอาทิตย์กับ MS?)
Qwertie

20

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


3
UTF-16 นั้นง่ายกว่าสำหรับทุกอย่างใน BMP นั่นเป็นเหตุผลว่าทำไมมันจึงถูกใช้อย่างกว้างขวาง แต่ฉันเป็นแฟนตัวยงของ UTF-8 เหมือนกันมันก็ไม่มีปัญหากับการสั่งไบต์ซึ่งทำงานเพื่อประโยชน์ของมัน
Malcolm

2
ในทางทฤษฎีใช่ ในทางปฏิบัติมีสิ่งต่าง ๆ เช่นพูด UTF-16BE ซึ่งหมายถึง UTF-16 ใน endian ใหญ่ที่ไม่มี BOM นี่ไม่ใช่สิ่งที่ฉันทำขึ้นมานี่เป็นการเข้ารหัสจริงที่อนุญาตในแท็ก ID3v2.4 (ดูดแท็ก ID3v2 แต่น่าเสียดายที่ใช้กันอย่างแพร่หลาย) และในกรณีเช่นนี้คุณต้องกำหนด endianness ภายนอกเนื่องจากข้อความนั้นไม่มี BOM UTF-8 มักเขียนทางเดียวและไม่มีปัญหาดังกล่าว
Malcolm

23
ไม่ UTF-16 ไม่ใช่เรื่องง่าย มันยากกว่า มันทำให้เข้าใจผิดและหลอกลวงให้คุณคิดว่ามันเป็นความกว้างคงที่ รหัสดังกล่าวทั้งหมดใช้งานไม่ได้และ moreso ทั้งหมดเพราะคุณไม่สังเกตจนกว่าจะสายเกินไป กรณีในจุด: ฉันเพิ่งพบข้อผิดพลาด UTF-16 โง่อีกครั้งในห้องสมุดแกน Java เมื่อวานนี้เวลาใน String.equalsIgnoreCase ซึ่งถูกทิ้งไว้ใน buggery braindeath UCS-2 และล้มเหลวในจุดรหัส Unicode ที่ถูกต้อง 16/17 รหัสนั้นมีมานานแค่ไหนแล้ว? ไม่มีข้อแก้ตัวที่จะเป็นรถ UTF-16 นำไปสู่ความโง่เขลาและอุบัติเหตุที่เกิดขึ้น เรียกใช้กรีดร้องจาก UTF-16
tchrist

3
@tchrist หนึ่งต้องเป็นนักพัฒนาที่ไม่รู้จะรู้ว่า UTF-16 ไม่ใช่ความยาวคงที่ หากคุณเริ่มต้นด้วย Wikipedia คุณจะอ่านสิ่งต่อไปนี้ที่ด้านบนสุด: "มันสร้างผลลัพธ์ความยาวผันแปรของหน่วยรหัส 16 บิตหนึ่งหรือสองหน่วยต่อจุดรหัส" คำถามที่พบบ่อย Unicode กล่าวว่าเดียวกัน: unicode.org/faq//utf_bom.html#utf16-1 ฉันไม่รู้ว่า UTF-16 จะหลอกลวงใครได้อย่างไรถ้ามันถูกเขียนขึ้นทุกที่ว่ามีความยาวผันแปรได้ สำหรับวิธีนี้มันไม่เคยถูกออกแบบมาสำหรับ UTF-16 และไม่ควรพิจารณาว่าเป็น Unicode อย่างง่าย
Malcolm

2
@tchrist คุณมีแหล่งข้อมูลสำหรับสถิติของคุณหรือไม่? แม้ว่าโปรแกรมเมอร์ที่ดีจะหายาก แต่ฉันคิดว่ามันดีเพราะเรามีค่ามากขึ้น :) สำหรับ Java APIs ชิ้นส่วนที่ใช้ถ่านอาจได้รับการปฏิเสธในที่สุด แต่นี่ไม่ใช่การรับประกันว่าจะไม่ถูกใช้งาน และแน่นอนว่ามันจะไม่ถูกลบออกด้วยเหตุผลด้านความสามารถในการใช้งาน
Malcolm

18

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

ในใจของฉันปัญหาส่วนใหญ่ปรากฏขึ้นจากความจริงที่ว่าซอฟต์แวร์บางตัวตกอยู่ภายใต้มาตรฐาน Unicode แต่ไม่รวดเร็วในการแก้ไขสถานการณ์ Opera, Windows, Python, Qt - ทั้งหมดปรากฏขึ้นก่อนที่ UTF-16 จะกลายเป็นที่รู้จักอย่างกว้างขวางหรือแม้กระทั่งมีอยู่จริง ฉันสามารถยืนยันได้ว่าใน Opera, Windows Explorer และ Notepad ไม่มีปัญหากับตัวละครนอก BMP อีกต่อไป (อย่างน้อยในพีซีของฉัน) แต่อย่างไรก็ตามหากโปรแกรมไม่รู้จักคู่ตัวแทนแทนพวกเขาจะไม่ใช้ UTF-16 ปัญหาใด ๆ ก็ตามที่เกิดขึ้นจากการจัดการกับโปรแกรมดังกล่าวพวกเขาไม่มีอะไรเกี่ยวข้องกับ UTF-16 เอง

อย่างไรก็ตามฉันคิดว่าปัญหาของซอฟต์แวร์รุ่นเก่าที่สนับสนุน BMP เพียงอย่างเดียวนั้นค่อนข้างเกินจริง อักขระนอก BMP พบเฉพาะในกรณีและพื้นที่ที่เฉพาะเจาะจงเท่านั้น จากคำถามที่พบบ่อยอย่างเป็นทางการของยูนิโค้ด "แม้ในเอเชียตะวันออกข้อความการเกิดตัวแทนคู่ควรจะน้อยกว่า 1% ของการจัดเก็บข้อความโดยเฉลี่ย" แน่นอนว่าตัวละครนอก BMP ไม่ควรถูกมองข้ามเพราะโปรแกรมนั้นไม่สอดคล้องกับ Unicode แต่โปรแกรมส่วนใหญ่ไม่ได้มีไว้สำหรับการทำงานกับข้อความที่มีตัวอักษรดังกล่าว นั่นเป็นเหตุผลว่าทำไมหากพวกเขาไม่สนับสนุนมันก็ไม่เป็นที่พอใจ แต่ไม่ใช่ความหายนะ

ทีนี้ลองพิจารณาทางเลือกอื่น หากไม่มี UTF-16 เราจะไม่มีการเข้ารหัสที่เหมาะสำหรับข้อความที่ไม่ใช่ ASCII และซอฟต์แวร์ทั้งหมดที่สร้างขึ้นสำหรับ UCS-2 จะต้องได้รับการออกแบบใหม่ทั้งหมดเพื่อให้สอดคล้องกับ Unicode หลังส่วนใหญ่มีแนวโน้มที่จะชะลอการยอมรับ Unicode เท่านั้น นอกจากนี้เราจะไม่สามารถรักษาความสามารถในการใช้งานกับข้อความใน UCS-2 เช่นเดียวกับ UTF-8 ที่เกี่ยวข้องกับ ASCII

ทีนี้ถ้าทิ้งประเด็นที่เป็นมรดกทั้งหมดเอาไว้สิ่งที่ขัดแย้งกับการเข้ารหัสนั้นคืออะไร? ฉันสงสัยจริงๆว่านักพัฒนาทุกวันนี้ไม่รู้ว่า UTF-16 นั้นมีความยาวผันแปรได้ถูกเขียนขึ้นทุกที่ที่มีวิกิพีเดีย UTF-16 นั้นแยกได้ยากกว่า UTF-8 น้อยมากถ้ามีคนชี้ให้เห็นความซับซ้อนว่าเป็นปัญหาที่เป็นไปได้ นอกจากนี้ยังเป็นเรื่องที่ผิดที่คิดว่ามันจะเลอะง่ายด้วยการกำหนดความยาวของสตริงใน UTF-16 เท่านั้น หากคุณใช้ UTF-8 หรือ UTF-32 คุณควรทราบว่าจุดโค้ด Unicode หนึ่งจุดนั้นไม่ได้แปลว่าตัวละครตัวเดียว นอกจากนั้นฉันไม่คิดว่ามันจะมีอะไรมากมายที่จะต่อต้านการเข้ารหัส

ดังนั้นฉันไม่คิดว่าการเข้ารหัสเองควรถือว่าเป็นอันตราย UTF-16 คือการประนีประนอมระหว่างความเรียบง่ายและความเป็นปึกแผ่นและไม่มีอันตรายในการใช้สิ่งที่จำเป็นซึ่งเป็นสิ่งจำเป็น ในบางกรณีคุณต้องยังคงใช้งานร่วมกับ ASCII ได้และคุณต้องใช้ UTF-8 ในบางกรณีคุณต้องการทำงานกับ Han ideographs และประหยัดพื้นที่โดยใช้ UTF-16 ในบางกรณีคุณต้องนำเสนออักขระสากลที่กำหนด การเข้ารหัสความยาว ใช้สิ่งที่เหมาะสมกว่าเพียงทำอย่างถูกต้อง


21
มัลคอล์มเป็นมุมที่ค่อนข้างกระพริบตา เกือบจะเทียบเท่ากับ "ASCII ดีพอสำหรับสหรัฐอเมริกา - ส่วนที่เหลือของโลกควรจะเข้ากับเรา"
Jonathan Leffler

28
อันที่จริงฉันมาจากรัสเซียและพบกับไซริลลิกส์ตลอดเวลา (รวมถึงรายการของตัวเองด้วย) ดังนั้นฉันไม่คิดว่าฉันมีมุมมองที่เป็นศูนย์กลางของแองโกล :) การเอ่ยถึง ASCII นั้นไม่ค่อยเหมาะนักเพราะมันไม่ใช่ Unicode และไม่รองรับตัวอักษรที่เฉพาะเจาะจง UTF-8, UTF-16, UTF-32 รองรับชุดอักขระสากลเหมือนกันมากพวกมันมีจุดประสงค์เพื่อใช้ในพื้นที่เฉพาะของพวกเขา และนี่คือประเด็นของฉัน: ถ้าคุณใช้ภาษาอังกฤษเป็นส่วนใหญ่ให้ใช้ UTF-8 ถ้าคุณใช้ Cyrillics ส่วนใหญ่ให้ใช้ UTF-16 ถ้าคุณใช้ภาษาโบราณให้ใช้ UTF-32 ค่อนข้างง่าย
Malcolm

16
"ไม่เป็นความจริงสคริปต์เอเชียเช่นญี่ปุ่นจีนหรืออาราบิคเป็นของ BMP ด้วยเช่นกัน BMP เองมีขนาดใหญ่มากและใหญ่พอที่จะรวมสคริปต์ทั้งหมดที่ใช้ในปัจจุบัน" นี่เป็นสิ่งที่ผิดทั้งหมด BMP มีอักขระ 0xFFFF (65536) คนจีนคนเดียวมีมากกว่านั้น มาตรฐานจีน (GB 18030) มีมากกว่านั้น Unicode 5.1 จัดสรรไว้แล้วมากกว่า 100,000 ตัวอักษร

12
@Marcolm: "BMP เองมีขนาดใหญ่มากและใหญ่พอที่จะรวมสคริปต์ทั้งหมดที่ใช้ในปัจจุบัน" ไม่เป็นความจริง เมื่อถึงจุดนี้ Unicode ได้จัดสรรตัวอักษรประมาณ 100K ไปแล้ววิธีที่มากกว่า BMP สามารถรองรับได้ มีตัวอักษรจีนตัวใหญ่นอก BMP และบางคนก็จำเป็นต้องใช้ GB-18030 (มาตรฐานจีนบังคับ) ข้อกำหนดอื่น ๆ เป็นไปตาม (ไม่บังคับ) มาตรฐานญี่ปุ่นและเกาหลี ดังนั้นหากคุณพยายามขายทุกอย่างในตลาดเหล่านั้นคุณต้องมีมากกว่า BMP support

8
สิ่งใดก็ตามที่ใช้ UTF-16 แต่สามารถจัดการอักขระ BMP ที่แคบได้เท่านั้นไม่ได้ใช้ UTF-16 จริงๆ มันเป็นรถและหัก หลักฐานของ OP คือเสียง: UTF-16 เป็นอันตรายเพราะมันทำให้ผู้คนไร้เดียงสาเขียนโค้ดที่ไม่สมบูรณ์ คุณสามารถจัดการกับข้อความ Unicode หรือไม่สามารถทำได้ หากคุณทำไม่ได้แสดงว่าคุณกำลังเลือกเซ็ตย่อยซึ่งโง่เหมือนกับการประมวลผลข้อความแบบ ASCII เท่านั้น
tchrist

16

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

การก้าวกระโดดสู่ UTF-16 ช่วยปรับปรุงความเพียงพอของผลิตภัณฑ์โดยเฉลี่ยที่จัดการกับข้อความระหว่างประเทศ มีเพียงไม่กี่กรณีที่แคบเมื่อคู่ตัวแทนจะต้องพิจารณา (การลบแทรกและทำลายเส้นโดยทั่วไป) และกรณีเฉลี่ยส่วนใหญ่เป็นตรงผ่าน และแตกต่างจากการเข้ารหัสก่อนหน้านี้เช่นตัวแปร JIS, UTF-16 จำกัด คู่ตัวแทนให้อยู่ในช่วงที่แคบมากดังนั้นการตรวจสอบจึงรวดเร็วมากและทำงานไปข้างหน้าและข้างหลัง

จริงอยู่ที่มันเป็น UTF-8 ที่เข้ารหัสอย่างรวดเร็วเช่นกัน แต่ยังมีแอปพลิเคชั่น UTF-8 ที่แตกหักจำนวนมากที่เข้ารหัสคู่ตัวแทนแทนอย่างไม่ถูกต้องเป็นสองลำดับ UTF-8 ดังนั้น UTF-8 จึงไม่รับประกันความรอดเช่นกัน

IE จัดการคู่ของตัวแทนเสมือนได้ดีพอสมควรตั้งแต่ปี 2000 เป็นต้นไปแม้ว่าโดยทั่วไปแล้วมันจะแปลงพวกมันจากหน้า UTF-8 ไปเป็น UTF-16 ที่เป็นตัวแทนภายใน ฉันค่อนข้างแน่ใจว่า Firefox ได้รับมันถูกต้องเช่นกันดังนั้นฉันไม่สนใจสิ่งที่ Opera ทำ

UTF-32 (aka UCS4) นั้นไม่มีประโยชน์สำหรับแอพพลิเคชั่นส่วนใหญ่เนื่องจากเป็นพื้นที่ที่มีความต้องการสูง


6
ฉันไม่ได้รับความคิดเห็นของคุณเกี่ยวกับคู่ UTF-8 และตัวแทน คู่ตัวแทนเป็นเพียงแนวคิดที่มีความหมายในการเข้ารหัส UTF-16 ใช่ไหม บางทีรหัสที่แปลงโดยตรงจากการเข้ารหัส UTF-16 เป็นการเข้ารหัส UTF-8 อาจทำให้เกิดข้อผิดพลาดและในกรณีนี้ปัญหาคือการอ่าน UTF-16 ไม่ถูกต้องไม่เขียน UTF-8 นั่นถูกต้องใช่ไหม?
Craig McQueen

11
สิ่งที่เจสันพูดถึงคือซอฟต์แวร์ที่ใช้ UTF-8 อย่างจงใจ: สร้างคู่ตัวแทนจากนั้น UTF-8 เข้ารหัสแต่ละครึ่งแยกกัน ชื่อที่ถูกต้องสำหรับการเข้ารหัสนั้นคือ CESU-8 แต่ Oracle (เช่น) แสดงผิดเป็น UTF-8 Java ใช้รูปแบบที่คล้ายกันสำหรับการทำให้เป็นอันดับวัตถุ แต่มีเอกสารชัดเจนว่า "Modified UTF-8" และสำหรับใช้ภายในเท่านั้น (ตอนนี้ถ้าเราสามารถเพียงแค่รับคนที่จะอ่านเอกสารที่และหยุดการใช้ DataInputStream # readUTF () และ DataOutputStream # writeUTF () ไม่เหมาะสม ... )

AFAIK, UTF-32 ยังคงเข้ารหัสความยาวของตัวแปรและไม่เท่ากับ UCS4 ซึ่งเป็นช่วงของจุดรหัสเฉพาะ
Eonil

@Eonil, UTF-32 จะแตกต่างจาก UCS4 เท่านั้นหากเรามีมาตรฐาน Unicode ที่มีคุณสมบัติบางอย่างเช่น UCS5 หรือใหญ่กว่า
JasonTrue

@ JasonTrue ยังมีเพียงผลลัพธ์ที่เท่ากันโดยบังเอิญไม่รับประกันโดยการออกแบบ สิ่งเดียวกันนั้นเกิดขึ้นในการกำหนดแอดเดรสหน่วยความจำ 32 บิต Y2K, UTF16 / UCS2 หรือเรามีหลักประกันความเท่าเทียมกันหรือไม่? ถ้าเรามีฉันยินดีที่จะใช้ แต่ฉันไม่ต้องการที่จะเขียนรหัสเปราะบางที่เป็นไปได้ ฉันกำลังเขียนรหัสระดับตัวอักษรและการขาดวิธีรับประกันการแปลงรหัสระหว่าง UTF <-> จุดรหัสคือการบั๊กฉันอย่างมาก
Eonil

16

UTF-8 นั้นเป็นวิธีที่แน่นอนซึ่งอาจมาพร้อมกับ UTF-32 สำหรับการใช้งานภายในในอัลกอริทึมที่ต้องการการเข้าถึงแบบสุ่มประสิทธิภาพสูง (แต่ไม่สนใจการรวมตัวอักษร)

ทั้ง UTF-16 และ UTF-32 (รวมถึงตัวแปร LE / BE ของพวกเขา) ประสบปัญหาของ endianess ดังนั้นจึงไม่ควรใช้ภายนอก


9
การเข้าถึงแบบสุ่มเวลาคงที่เป็นไปได้ด้วย UTF-8 เช่นกันเพียงใช้หน่วยรหัสแทนจุดรหัส บางทีคุณอาจต้องการรหัสจุดเข้าใช้งานแบบสุ่ม แต่ฉันไม่เคยเห็นกรณีการใช้งานและคุณมีแนวโน้มที่จะต้องการเข้าถึงการเข้าถึงคลัสเตอร์แบบสุ่มแทน

15

UTF-16? อันตรายอย่างแน่นอน เพียงแค่เม็ดเกลือของฉันที่นี่ แต่มีการเข้ารหัสข้อความที่ยอมรับได้สามรายการในโปรแกรม:

  • ASCII: เมื่อจัดการกับสิ่งต่าง ๆ ในระดับต่ำ (เช่น: ไมโครคอนโทรลเลอร์) ที่ไม่สามารถจ่ายได้ดีกว่า
  • UTF8: ที่เก็บข้อมูลในสื่อที่มีความกว้างคงที่เช่นไฟล์
  • codepoints จำนวนเต็ม ("CP"?): อาร์เรย์ของจำนวนเต็มที่ใหญ่ที่สุดที่สะดวกสำหรับภาษาการเขียนโปรแกรมและแพลตฟอร์มของคุณ (สลายตัวไปเป็น ASCII ในขีด จำกัด ของหน่วยความจำต่ำ) ควรเป็น int32 ในคอมพิวเตอร์รุ่นเก่าและ int64 ในทุกอย่างที่มีการระบุที่อยู่ 64 บิต

  • เห็นได้ชัดว่าส่วนต่อประสานกับรหัสดั้งเดิมใช้การเข้ารหัสที่จำเป็นเพื่อให้รหัสเดิมทำงานได้


4
@simon buchan, U+10ffffmax จะออกไปนอกหน้าต่างเมื่อ (ไม่ใช่) พวกเขาหมด codepoints ที่กล่าวว่าการใช้ int32 บนระบบ p64 สำหรับความเร็วน่าจะปลอดภัยเนื่องจากฉันสงสัยว่ามันจะเกินU+ffffffffก่อนที่คุณจะถูกบังคับให้เขียนรหัสของคุณใหม่สำหรับระบบ 128 บิตรอบปี 2050 (นั่นคือประเด็น "ใช้ int ที่ใหญ่ที่สุดที่ สะดวก "ตรงข้ามกับ" ที่มีขนาดใหญ่ที่สุด "(ซึ่งอาจจะเป็น int256 หรือ bignums หรืออะไรบางอย่าง).)
David X

1
@David: Unicode 5.2 เข้ารหัส codepoints 107,361 มี codepoints ที่ไม่ได้ใช้แล้ว 867,169 "เมื่อ" เป็นเพียงโง่ Unicode codepoint ถูกกำหนดเป็นตัวเลขตั้งแต่ 0 ถึง 0x10FFFF ซึ่งเป็นคุณสมบัติที่ UTF-16 ขึ้นอยู่กับ (นอกจากนี้ 2050 ดูเหมือนมากที่จะต่ำประมาณการสำหรับระบบ 128 บิตเมื่อระบบ 64 บิตสามารถถือทั้งหมดของอินเทอร์เน็ตในมันเป็นพื้นที่ที่อยู่ได้.)

3
@David: "เมื่อ" ของคุณหมายถึงการเรียกใช้ Unicode codepoints ไม่มีสวิตช์ 128 บิตซึ่งใช่จะอยู่ในอีกไม่กี่ศตวรรษ ซึ่งแตกต่างจากหน่วยความจำที่ไม่มีการเจริญเติบโตของตัวละครเพื่อให้ Unicode Consortium ได้เฉพาะรับประกันว่าพวกเขาจะไม่เคยU+10FFFFจัดสรรจุดโค้ดดังกล่าวข้างต้น นี้จริงๆเป็นหนึ่งในสถานการณ์เหล่านั้นเมื่อ 21 บิตเป็นพอสำหรับใคร

10
@Simon Buchan: อย่างน้อยก็จนกว่าจะมีการติดต่อครั้งแรก :)

3
Unicode ใช้เพื่อรับประกันว่าจะไม่มีรหัสจุดเหนือ U + FFFF
แชนนอนชดเชย

13

Unicodeกำหนดรหัสได้สูงสุด 0x10FFFF (1,114,112 รหัส) แอปพลิเคชันทั้งหมดที่ทำงานในสภาพแวดล้อมหลายภาษาที่จัดการกับสตริง / ชื่อไฟล์ ฯลฯ ควรจัดการอย่างถูกต้อง

Utf-16 : ครอบคลุมเฉพาะ 1,112,064 รหัส ถึงแม้ว่าส่วนท้ายของUnicodeจะมาจากเครื่องบิน 15-16 (พื้นที่ใช้งานส่วนตัว) มันไม่สามารถเติบโตได้อีกในอนาคตยกเว้นการทำลายแนวคิดUtf-16

Utf-8 : ครอบคลุมในทางทฤษฎี 2,216,757,376 รหัส ช่วงปัจจุบันของรหัสUnicodeสามารถแสดงด้วยลำดับสูงสุด 4 ไบต์ มันไม่ได้ประสบกับปัญหาการสั่งซื้อไบต์มันเป็น "เข้ากันได้" กับ ascii

Utf-32 : ครอบคลุมตามหลักวิชา 2 ^ 32 = 4,294,967,296 รหัส ขณะนี้ยังไม่ได้เข้ารหัสความยาวผันแปรและอาจจะไม่เป็นในอนาคต

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

ปัญหาหลักคือโปรแกรมเมอร์จำนวนมากที่เกี่ยวข้องกับ Windows Unicode = Utf-16ไม่ทราบหรือเพิกเฉยต่อความจริงที่ว่ามีการเข้ารหัสความยาวแปรผัน

วิธีการก็มักจะอยู่ใน* ระวังแพลตฟอร์มที่ดีงาม, สาย C (char *) ตีความว่าเป็นUTF-8เข้ารหัสสตริงคกว้าง (wchar_t *) ตีความว่าเป็นUTF-32


7
หมายเหตุ: UTF-16 ครอบคลุม Unicode ทั้งหมดเป็น Unicode Consortium ตัดสินใจว่า 10FFFF เป็นช่วงสูงสุดของ Unicode และกำหนด UTF-8 สูงสุด 4 ไบต์ความยาวสูงสุด 4 ไบต์และช่วงที่แยกอย่างชัดเจน 0xD800-0xDFFF จากช่วงรหัสที่ถูกต้องและช่วงนี้ใช้สำหรับการสร้าง คู่ตัวแทน ดังนั้นข้อความ Unicode ที่ถูกต้องใด ๆ สามารถแสดงด้วยการเข้ารหัสหนึ่งในแต่ละรายการเหล่านี้ ยังเกี่ยวกับการเติบโตในอนาคต ดูเหมือนว่าจุดรหัส 1 ล้านจะไม่เพียงพอในอนาคตอันไกล

7
@Kerrek: ไม่ถูกต้อง: UCS-2 ไม่ใช่การเข้ารหัส Unicode ที่ถูกต้อง การเข้ารหัส UTF- * ทั้งหมดตามคำจำกัดความสามารถแทนจุดโค้ด Unicode ใด ๆ ที่ถูกกฎหมายสำหรับการแลกเปลี่ยน UCS-2 สามารถเป็นตัวแทนน้อยกว่านั้นบวกอีกไม่กี่ ทำซ้ำ: UCS-2 ไม่ใช่การเข้ารหัส Unicode ที่ถูกต้อง moreso ใด ๆ ที่มากกว่า ASCII คือ
tchrist

1
"ฉันไม่เข้าใจการสนับสนุนการใช้งานUtf-8โดยทั่วไปมันเป็นความยาวแปรผันที่เข้ารหัส (ไม่สามารถเข้าถึงได้โดยดัชนี)"
Ian Boyd

9
@Ian Boyd ความต้องการในการเข้าถึงตัวละครของสตริงในรูปแบบการเข้าถึงแบบสุ่มนั้นเกินจริงอย่างไม่น่าเชื่อ มันเป็นเรื่องธรรมดาที่ต้องการคำนวณเส้นทแยงมุมของเมทริกซ์ของตัวละครซึ่งหายากสุด ๆ สตริงจะได้รับการประมวลผลตามลำดับทุกครั้งและเนื่องจากการเข้าถึง UTF-8 ถ่าน N + 1 เนื่องจากคุณอยู่ที่ UTF-8 ถ่าน N คือ O (1) จึงไม่มีปัญหา มีความจำเป็นเล็กน้อยในการเข้าถึงสตริงแบบสุ่ม ไม่ว่าคุณจะคิดว่ามันคุ้มค่ากับพื้นที่เก็บข้อมูลที่จะไปที่ UTF-32 แทนที่จะเป็น UTF-8 แต่เป็นความเห็นของคุณ แต่สำหรับฉันมันเป็นเรื่องที่ไม่เกี่ยวข้องเลย
tchrist

2
@tchrist ฉันจะให้คุณสตริงที่มีการประมวลผลตามลำดับเกือบทุกครั้งถ้าคุณรวมการทำซ้ำย้อนกลับเป็น "ลำดับ" และยืดที่เปรียบเทียบเพิ่มเติมเล็กน้อยของปลายท้ายของสตริงกับสตริงที่รู้จักกัน สถานการณ์ที่พบบ่อยมากสองสถานการณ์กำลังตัดทอนช่องว่างออกจากจุดสิ้นสุดของสตริงและตรวจสอบนามสกุลไฟล์ที่ส่วนท้ายของพา ธ
Andy Dent

11

เพิ่มลงในรายการ:

สถานการณ์ที่นำเสนอนั้นง่าย (ยิ่งง่ายกว่าที่ฉันจะนำเสนอที่นี่มากกว่าเดิม!): 1. กล่องข้อความ WinForms ตั้งอยู่บนแบบฟอร์มเปล่า แต่ก็มี MaxLength ตั้งค่าเป็น20

2. ผู้ใช้พิมพ์ลงในกล่องข้อความหรืออาจวางข้อความลงไป

3. ไม่ว่าคุณจะพิมพ์หรือวางลงในกล่องข้อความคุณถูก จำกัด ไว้ที่ 20 แม้ว่ามันจะส่งเสียงบี๊บเกินกว่า 20 ข้อความ (เห็นนี่ YMMV ฉันเปลี่ยนรูปแบบเสียงของฉันเพื่อให้ผลนั้น!)

4. ข้อความขนาดเล็กจะถูกส่งไปที่อื่นเพื่อเริ่มการผจญภัยที่น่าตื่นเต้น

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

ฉันตั้งชื่อรูปแบบMagic Carpet Rideเพื่อช่วยเยียวยาความเบื่อหน่าย

สิ่งนี้ไม่ได้ผลสำหรับสิ่งที่คุ้มค่า

ดังนั้นฉันจึงป้อน ตัวอักษร20ตัวต่อไปนี้ลงในแบบฟอร์มMagic Carpet Rideของฉัน:

0123401234012340123 𠀀

เอ่อโอ้.

ตัวละครตัวสุดท้ายนั่นคือ U + 20000 ซึ่งเป็นตัวเสริมนามสกุล B ตัวแรกของ Unicode (หรือที่ U + d840 U + dc00 ให้กับเพื่อนสนิทของเขาซึ่งเขาไม่ละอายที่จะถูก disrobed เหมือนตอนก่อน) ....

ป้อนคำอธิบายรูปภาพที่นี่

และตอนนี้เรามีเกมบอล

เพราะเมื่อTextBox.MaxLengthพูดถึง

รับหรือตั้งค่าจำนวนอักขระสูงสุดที่สามารถป้อนด้วยตนเองลงในกล่องข้อความ

ความหมายจริงๆคืออะไร

รับหรือตั้งค่าจำนวนสูงสุดของหน่วยรหัส UTF-16 LE ที่สามารถป้อนด้วยตนเองลงในกล่องข้อความด้วยตนเองและจะตัดทอนอึชีวิตอย่างไร้ความปราณีออกจากสตริงใด ๆ ที่พยายามเล่นเกม cutesy ด้วยความคิดเชิงภาษาที่มีเพียงคนที่หลงไหล เพื่อนชาวแคปแลนจะรู้สึกไม่พอใจ (เขาต้องออกไปอีก!)

ฉันจะลองและดูเกี่ยวกับการอัปเดตเอกสาร ....
ผู้อ่านปกติที่จำชุดUCS-2 ถึง UTF-16ของฉันจะสังเกตความไม่พอใจของฉันด้วยความคิดที่เรียบง่ายของTextBox.MaxLengthและวิธีจัดการกับกรณีนี้ให้น้อยที่สุด ที่ซึ่งพฤติกรรมแบบ draconian ของมันสร้างลำดับที่ผิดกฎหมายหนึ่งที่ส่วนอื่น ๆ ของ. Net Framework อาจโยน

  • System.Text.EncoderFallbackException: ไม่สามารถแปลอักขระ Unicode \ uD850 ที่ดัชนี 0 ไปยังหน้ารหัสที่ระบุ *

ข้อยกเว้นถ้าคุณส่งสตริงนี้ที่อื่นใน. Net Framework (ขณะที่ Dan Thompson เพื่อนร่วมงานของฉันกำลังทำอยู่)

ตอนนี้ก็โอเคบางทีUCS-2 ถึง UTF-16 ซีรีส์แบบเต็มนั้นก็ไม่สามารถเข้าถึงได้มากมาย
แต่มันไม่สมเหตุสมผลที่จะคาดหวังว่า TextBox Text จะไม่สร้าง System.Stringที่จะไม่ทำให้ส่วนอื่นของ. Net Framework โยน? ฉันหมายความว่ามันไม่เหมือนมีโอกาสในรูปแบบของเหตุการณ์บางอย่างในตัวควบคุมที่บอกคุณถึงการตัดทอนที่กำลังจะเกิดขึ้นซึ่งคุณสามารถเพิ่มการตรวจสอบความถูกต้องได้อย่างชาญฉลาด - การตรวจสอบว่าตัวควบคุมไม่สนใจ ฉันจะบอกว่าการควบคุมพังค์นี้เป็นการละเมิดสัญญาความปลอดภัยซึ่งอาจนำไปสู่ปัญหาด้านความปลอดภัยหากคุณสามารถเรียนทำให้เกิดข้อยกเว้นที่ไม่คาดคิดในการยกเลิกแอปพลิเคชันเป็นการปฏิเสธบริการ ทำไมกระบวนการ WinForms หรือวิธีการหรืออัลกอริทึมหรือเทคนิคใด ๆ ควรให้ผลลัพธ์ที่ไม่ถูกต้อง

ที่มา: Michael S. Kaplan MSDN Blog


ขอบคุณลิงค์ดีมาก! ฉันได้เพิ่มไปยังรายการปัญหาในคำถาม

9

ฉันไม่จำเป็นต้องพูดว่า UTF-16 เป็นอันตราย มันไม่ได้สวยงาม แต่มันมีจุดประสงค์ในการใช้งานร่วมกันได้กับ UCS-2 เช่นเดียวกับ GB18030 กับ GB2312 และ UTF-8 ทำกับ ASCII

แต่การเปลี่ยนแปลงโครงสร้างพื้นฐานของ Unicode ในช่วงกลางคันหลังจาก Microsoft และ Sun ได้สร้าง API ขนาดใหญ่รอบตัวอักษร 16 บิตเป็นอันตราย ความล้มเหลวในการแพร่กระจายการรับรู้ของการเปลี่ยนแปลงเป็นอันตรายมากขึ้น


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

1
แน่นอนว่าในทางเทคนิค UTF-16 ไม่ใช่ superset ของ UCS-2 แต่เมื่อใดที่ U + D800 ถึง U + DFFF เคยใช้กับอะไรเลยยกเว้น UTF-16 surrogates?
dan04

2
ไม่เป็นไร การประมวลผลใด ๆ นอกจากการผ่าน bytestream นั้นคุณต้องถอดรหัสคู่ตัวแทนแทนซึ่งคุณไม่สามารถทำได้ถ้าคุณปฏิบัติต่อมันเหมือน UCS-2

6

UTF-16 คือการประนีประนอมที่ดีที่สุดระหว่างการจัดการและพื้นที่และนั่นเป็นเหตุผลว่าทำไมแพลตฟอร์มหลักส่วนใหญ่ (Win32, Java, .NET) จึงใช้เพื่อการแสดงสตริงภายใน


31
-1 เนื่องจาก UTF-8 มีแนวโน้มที่จะเล็กลงหรือไม่แตกต่างกันอย่างมีนัยสำคัญ สำหรับสคริปต์เอเชียบางตัว UTF-8 มีสามไบต์ต่อ glyph ในขณะที่ UTF-16 มีเพียงสองเท่านั้น แต่สิ่งนี้มีความสมดุลโดย UTF-8 มีเพียงหนึ่งไบต์สำหรับ ASCII (ซึ่งมักปรากฏในภาษาเอเชียในชื่อผลิตภัณฑ์คำสั่งและเช่นนั้น สิ่ง) นอกจากนี้ในภาษาดังกล่าว glyph จะสื่อข้อมูลมากกว่าตัวอักษรละตินดังนั้นจึงมีเหตุผลที่จะต้องใช้พื้นที่มากขึ้น

32
ฉันจะไม่เรียกรวมด้านที่เลวร้ายที่สุดของตัวเลือกทั้งสองประนีประนอมที่ดี

18
มันไม่ง่ายกว่า UTF-8 มันยาวเกินไป
luiscubal

36
การออกจากการอภิปรายเกี่ยวกับประโยชน์ของ UTF-16: สิ่งที่คุณอ้างถึงไม่ใช่เหตุผลสำหรับ Windows, Java หรือ. NET โดยใช้ UTF-16 Windows และ Java ย้อนกลับไปถึงช่วงเวลาที่ Unicode เป็นการเข้ารหัสแบบ 16 บิต UCS-2 เป็นตัวเลือกที่สมเหตุสมผลตั้งแต่นั้นมา เมื่อ Unicode กลายเป็นการเข้ารหัสแบบ 21 บิตที่ย้ายไปยัง UTF-16 เป็นตัวเลือกที่ดีที่สุดสำหรับแพลตฟอร์มที่มีอยู่ นั่นไม่เกี่ยวอะไรกับความง่ายในการจัดการหรือลดทอนพื้นที่ มันเป็นเพียงเรื่องของมรดก
Joey

10
.NET สืบทอดมรดกของ Windows ที่นี่
Joey

6

ฉันไม่เคยเข้าใจประเด็นของ UTF-16 หากคุณต้องการตัวแทนที่ประหยัดพื้นที่มากที่สุดให้ใช้ UTF-8 หากคุณต้องการให้ข้อความเป็นความยาวคงที่ให้ใช้ UTF-32 หากคุณไม่ต้องการให้ใช้ UTF-16 ที่แย่ไปกว่านั้นเนื่องจากตัวอักษรธรรมดา (ระนาบหลายภาษาพื้นฐาน) ใน UTF-16 นั้นพอดีในจุดรหัสเดียวจุดบกพร่องที่คิดว่า UTF-16 นั้นมีความยาวคงที่จะละเอียดและหายากในขณะที่ถ้าคุณพยายามทำ ด้วย UTF-8 รหัสของคุณจะล้มเหลวอย่างรวดเร็วและดังทันทีที่คุณพยายามทำให้เป็นสากล


6

เนื่องจากฉันยังไม่สามารถแสดงความคิดเห็นได้ฉันโพสต์สิ่งนี้เป็นคำตอบเนื่องจากดูเหมือนว่าฉันไม่สามารถติดต่อผู้เขียนutf8everywhere.orgได้ มันเป็นความอัปยศที่ฉันไม่ได้รับสิทธิ์ความคิดเห็นโดยอัตโนมัติเนื่องจากฉันมีชื่อเสียงเพียงพอในการแลกเปลี่ยนสแต็กอื่น ๆ

นี่เป็นความเห็นต่อความคิดเห็น: ใช่ UTF-16 ควรได้รับการพิจารณาคำตอบที่เป็นอันตราย

แก้ไขเพียงเล็กน้อย:

เพื่อป้องกันไม่ให้คนหนึ่งบังเอิญผ่าน UTF-8 char*เข้ามาในรุ่น ANSI-สตริงของฟังก์ชั่นที่ใช้ Windows API หนึ่งควรกำหนดไม่ได้UNICODE ฟังก์ชั่นแผนที่เหมือนจะไม่ได้ไป แต่การกำหนดจะดูแลหลัง เพื่อพิสูจน์สิ่งนี้มาจากส่วนหัวของ MS Visual Studio 2005 :_UNICODE_UNICODE_tcslenwcslenMessageBoxMessageBoxWUNICODEWinUser.h

#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE

utf8everywhere.orgที่ต่ำมากข้อผิดพลาดนี้ควรได้รับการแก้ไขใน

ข้อเสนอแนะ:

บางทีคู่มือควรมีตัวอย่างของการใช้โครงสร้างข้อมูลแบบ Wide-string เพื่อทำให้พลาด / ลืมได้ง่ายขึ้น การใช้โครงสร้างข้อมูลแบบ Wide-string ด้านบนของการใช้ฟังก์ชั่น Wide-string ทำให้มีความเป็นไปได้น้อยที่หนึ่งจะเรียกรุ่น ANSI-string ของฟังก์ชันดังกล่าวโดยไม่ตั้งใจ

ตัวอย่างของตัวอย่าง:

WIN32_FIND_DATAW data; // Note the W at the end.
HANDLE hSearch = FindFirstFileW(widen("*.txt").c_str(), &data);
if (hSearch != INVALID_HANDLE_VALUE)
{
    FindClose(hSearch);
    MessageBoxW(nullptr, data.cFileName, nullptr, MB_OK);
}

ตกลงกัน; ขอบคุณ! เราจะอัพเดทเอกสาร เอกสารยังต้องการการพัฒนาและการเพิ่มข้อมูลเกี่ยวกับฐานข้อมูล เรามีความสุขที่ได้รับผลงานของคำพูด
Pavel Radzivilovsky

@PavelRadzivilovsky _UNICODEยังคงอยู่ที่นั่น :(
cubuspl42

ขอบคุณสำหรับการเตือน cubus, Jelle, คุณต้องการให้ผู้ใช้เป็น SVN ของเราหรือไม่?
Pavel Radzivilovsky

@Pavel แน่นอนว่าจะขอบคุณมัน!
Jelle Geerts

@JelleGeerts: ฉันขอโทษสำหรับความล่าช้านี้ คุณสามารถติดต่อเราทางอีเมลของเรา (เชื่อมโยงจากรายการ) หรือ Facebook เราหาง่าย แม้ว่าฉันเชื่อว่าเราได้แก้ไขปัญหาที่คุณนำมาที่นี่ (และฉันให้เครดิตคุณที่นั่น) การอภิปราย UTF-8 กับ UTF-16 ทั้งหมดยังคงเกี่ยวข้อง หากคุณมีส่วนร่วมมากขึ้นโปรดติดต่อเราผ่านช่องส่วนตัวเหล่านั้น
ybungalobill

5

บางคนกล่าวว่า UCS4 และ UTF-32 เหมือนกัน ไม่เช่นนั้น แต่ฉันรู้ว่าคุณหมายถึงอะไร หนึ่งในนั้นคือการเข้ารหัสของอีกอันหนึ่ง ฉันหวังว่าพวกเขาจะคิดว่าจะระบุ endianness จากครั้งแรกดังนั้นเราจะไม่ได้ต่อสู้ endianess ต่อสู้ที่นี่เช่นกัน พวกเขาไม่เห็นว่ามาหรือไม่ อย่างน้อย UTF-8 จะเหมือนกันทุกที่ (ยกเว้นบางคนกำลังติดตามข้อมูลจำเพาะดั้งเดิมที่มีขนาด 6 ไบต์)

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

ข้อมูลจำเพาะฉบับร่างปัจจุบันของ C ++ บอกว่า UTF-32 และ UTF-16 สามารถมีรูปแบบที่แตกต่างกันเล็กน้อย, แบบปลายใหญ่และแบบไม่ระบุรายละเอียด จริงๆ? ถ้า Unicode ได้ระบุว่าทุกคนต้องทำตัวเล็ก ๆ น้อย ๆ ตั้งแต่ต้นมันก็จะง่ายขึ้น (ฉันจะได้ดีกับ big-endian เช่นกัน.) บางคนใช้มันในทางหนึ่งอื่น ๆ และตอนนี้เรากำลังติดอยู่กับความโง่เขลาสำหรับอะไร บางครั้งมันก็น่าอายที่เป็นวิศวกรซอฟต์แวร์


endianess ที่ไม่ระบุจะต้องรวม BOM เป็นอักขระตัวแรกใช้สำหรับกำหนดวิธีที่ควรอ่านสตริง ปัจจุบัน UCS-4 และ UTF-32 เหมือนกันเช่นค่า UCS ตัวเลขระหว่าง 0 ถึง 0x10FFFF ที่เก็บไว้ในจำนวนเต็ม 32 บิต

5
@Tronic: เทคนิคนี้ไม่เป็นความจริง แม้ว่า UCS-4 สามารถเก็บจำนวนเต็ม 32 บิตใด ๆ ได้ แต่ UTF-32 จะถูกห้ามมิให้เก็บคะแนนโค้ดที่ไม่ใช่อักขระที่ผิดกฎหมายสำหรับการแลกเปลี่ยนเช่น 0xFFFF, 0xFFFE และตัวแทนทั้งหมด UTF เป็นการเข้ารหัสการส่งผ่านไม่ใช่รหัสภายใน
tchrist

ปัญหา Endianness ไม่สามารถหลีกเลี่ยงได้ตราบใดที่ตัวประมวลผลที่แตกต่างกันยังคงใช้คำสั่งไบต์ที่แตกต่างกัน อย่างไรก็ตามมันอาจจะดีถ้ามีคำสั่ง "ที่ต้องการ" สำหรับการจัดเก็บไฟล์ของ UTF-16
Qwertie

แม้ว่า UTF-32 ความกว้างคงที่สำหรับจุดรหัสก็ไม่ได้ความกว้างคงที่สำหรับตัวละคร (ได้ยินสิ่งที่เรียกว่า "การรวมตัวละคร"?) ดังนั้นคุณไม่สามารถไปที่ตัวละคร N'th เพียงแค่สร้างดัชนี 4N ลงในอาร์เรย์ไบต์
musiphil

2

ฉันไม่คิดว่ามันจะเป็นอันตรายหากนักพัฒนามีความระมัดระวังเพียงพอ
และพวกเขาควรยอมรับการแลกเปลี่ยนนี้หากพวกเขารู้ดีเช่นกัน

ในฐานะนักพัฒนาซอฟต์แวร์ชาวญี่ปุ่นฉันพบว่า UCS-2 มีขนาดใหญ่พอและ จำกัด พื้นที่ได้ง่ายกว่าตรรกะและลดหน่วยความจำรันไทม์ดังนั้นการใช้ utf-16 ภายใต้ข้อ จำกัด UCS-2 ก็ถือว่าดีพอ

มีระบบไฟล์หรือแอพพลิเคชั่นอื่น ๆ ที่ถือว่า codepoints และ bytes เป็นสัดส่วนดังนั้นจำนวน codepoint แบบดิบสามารถรับประกันได้ว่าจะพอดีกับที่เก็บข้อมูลขนาดคงที่

ตัวอย่างหนึ่งคือNTFS และ VFAT ที่ระบุ UCS-2เป็นการเข้ารหัสที่เก็บข้อมูลชื่อไฟล์

หากตัวอย่างเหล่านั้นต้องการขยายเพื่อรองรับ UCS-4 ฉันสามารถตกลงใช้ utf-8 สำหรับทุกสิ่งได้ แต่ความยาวคงที่มีจุดที่ดีเช่น:

  1. สามารถรับประกันขนาดตามความยาว (ขนาดข้อมูลและความยาว codepoint เป็นสัดส่วน)
  2. สามารถใช้หมายเลขการเข้ารหัสสำหรับการค้นหาแฮช
  3. ข้อมูลที่ไม่บีบอัดมีขนาดที่สมเหตุสมผล (เมื่อเทียบกับ utf-32 / UCS-4)

ในอนาคตเมื่อหน่วยความจำ / กำลังการประมวลผลราคาถูกแม้ในอุปกรณ์ฝังใด ๆ เราอาจยอมรับว่าอุปกรณ์ช้าลงเล็กน้อยสำหรับการแคชที่ผิดพลาดหรือการผิดพลาดของหน้าและการใช้หน่วยความจำเพิ่มเติม แต่สิ่งนี้จะเกิดขึ้นในอนาคตอันใกล้นี้


3
สำหรับผู้ที่อ่านความคิดเห็นนี้มันน่าสังเกตว่า UCS-2 นั้นไม่เหมือนกับ UTF-16 โปรดค้นหาความแตกต่างที่จะเข้าใจ
mikebabcock

1

"หนึ่งในการเข้ารหัสที่นิยมมากที่สุด UTF-16 ถือว่าเป็นอันตรายหรือไม่"

ค่อนข้างจะเป็นไปได้ แต่ทางเลือกไม่ควรถูกมองว่าดีกว่ามากนัก

ปัญหาพื้นฐานคือมีแนวคิดที่แตกต่างกันมากมายเกี่ยวกับ: ร่ายมนตร์, อักขระ, codepoints และลำดับไบต์ การทำแผนที่ระหว่างสิ่งเหล่านี้ไม่ใช่เรื่องไร้สาระแม้แต่ความช่วยเหลือของไลบรารีการทำให้เป็นมาตรฐาน (ตัวอย่างเช่นตัวละครบางตัวในภาษายุโรปที่เขียนด้วยสคริปต์แบบละตินไม่ได้เขียนด้วยรหัส Unicode ตัวเดียวและนั่นคือจุดสิ้นสุดของความซับซ้อนที่เรียบง่ายกว่า!) สิ่งนี้หมายความว่าการได้รับทุกอย่างถูกต้องค่อนข้างน่าอัศจรรย์ ยาก; คาดว่าจะมีข้อผิดพลาดที่แปลกประหลาด (และแทนที่จะส่งเสียงครวญครางที่นี่ให้ผู้ดูแลระบบของซอฟต์แวร์ที่เกี่ยวข้องทราบ)

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

ฉันหันคำถามของคุณไปที่หัวแล้วบอกว่า Shebang ทั้งหมดของ Unicode ควรได้รับการพิจารณาว่าเป็นอันตรายและทุกคนควรใช้การเข้ารหัสแบบ 8 บิตยกเว้นว่าฉันเคยเห็น (มากกว่า 20 ปีที่ผ่านมา) ที่นำไปสู่: น่ากลัว ความสับสนเกี่ยวกับการเข้ารหัส ISO 8859 ต่างๆรวมถึงชุดทั้งหมดที่ใช้สำหรับ Cyrillic และชุด EBCDIC และ ... ดี Unicode สำหรับความผิดพลาดทั้งหมดของมัน หากเพียง แต่มันก็ไม่ได้เป็นการประนีประนอมที่น่ารังเกียจระหว่างความเข้าใจผิดของประเทศต่างๆ


รู้โชคของเราในอีกไม่กี่ปีเราจะพบว่าตัวเองขาดพื้นที่ใน UTF-16 Meh
Donal Fellows

3
ปัญหาพื้นฐานคือข้อความนั้นหลอกลวงยาก ไม่มีวิธีในการแสดงข้อมูลนั้นในรูปแบบดิจิตอลที่ไม่ซับซ้อน มันเป็นเหตุผลเดียวกันกับวันที่ยากปฏิทินยากเวลายากชื่อส่วนตัวยากที่อยู่ทางไปรษณีย์ยาก: เมื่อใดก็ตามที่เครื่องดิจิทัลตัดกับโครงสร้างทางวัฒนธรรมของมนุษย์ความซับซ้อนจะปะทุขึ้น มันเป็นความจริงของชีวิต มนุษย์ไม่สามารถใช้กับตรรกะดิจิตอลได้
อริสโตเติล Pagaltzis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.