“ คู่ตัวแทน” ใน Java คืออะไร?


149

ผมอ่านเอกสารประกอบการStringBufferโดยเฉพาะอย่างยิ่งย้อนกลับ ()วิธีการ เอกสารที่กล่าวถึงสิ่งที่เกี่ยวกับคู่ตัวแทน คู่ตัวแทนในบริบทนี้คืออะไร? และตัวแทนสำรองต่ำและสูงคืออะไร?


3
มันเป็นคำศัพท์ UTF-16 อธิบายได้ที่นี่: download.oracle.com/javase/6/docs/api/java/lang/…
wkl

1
วิธีการนั้นเป็นรถ: มันควรย้อนกลับอักขระเต็ม points รหัสจุด - ไม่แยกชิ้นส่วนของพวกเขาᴀᴋᴀรหัสหน่วย ข้อผิดพลาดคือวิธีการแบบดั้งเดิมนั้นทำงานได้เฉพาะกับหน่วยถ่านแต่ละตัวแทนที่จะเป็นรหัสจุดซึ่งเป็นสิ่งที่คุณต้องการ Stringให้สร้างขึ้นไม่ใช่หน่วยงานถ่าน น่าเสียดายที่ Java ไม่อนุญาตให้คุณใช้ OO เพื่อแก้ไขปัญหาดังกล่าว แต่ทั้งStringคลาสและStringBufferคลาสได้รับการปรับfinalขนาดแล้ว บอกว่านั่นคือคำสละสลวยสำหรับฆ่า? :)
tchrist

2
@tchrist เอกสาร (และแหล่งที่มา) บอกว่ามันจะย้อนกลับเป็นสตริงของรหัสจุด (สมมุติว่า 1.0.2 ไม่ได้ทำอย่างนั้นและคุณจะไม่มีวันเปลี่ยนแปลงพฤติกรรมเช่นนี้)
Tom Hawtin - tackline

คำตอบ:


127

คำว่า "คู่ตัวแทน" หมายถึงวิธีการเข้ารหัสอักขระ Unicode ที่มีรหัสจุดสูงในรูปแบบการเข้ารหัส UTF-16

ในการเข้ารหัสอักขระ Unicode อักขระจะถูกแมปกับค่าระหว่าง 0x0 และ 0x10FFFF

ภายใน Java ใช้รูปแบบการเข้ารหัส UTF-16 เพื่อเก็บสตริงของข้อความ Unicode ใน UTF-16 จะใช้หน่วยรหัส 16 บิต (สองไบต์) เนื่องจาก 16 บิตสามารถมีช่วงของอักขระจาก 0x0 ถึง 0xFFFF เท่านั้นจึงมีความซับซ้อนเพิ่มเติมบางอย่างที่ใช้ในการจัดเก็บค่าเหนือช่วงนี้ (0x10000 ถึง 0x10FFFF) สิ่งนี้ทำได้โดยใช้คู่ของรหัสหน่วยที่เรียกว่าตัวแทน

หน่วยรหัสตัวแทนตั้งอยู่ในสองช่วงที่เรียกว่า "ตัวแทนตัวแทนสูง" และ "ตัวแทนตัวแทนต่ำ" ขึ้นอยู่กับว่าได้รับอนุญาตในตอนเริ่มต้นหรือสิ้นสุดของลำดับรหัสสองหน่วย


4
สิ่งนี้มีการลงคะแนนมากที่สุด แต่ก็ไม่ได้ให้ตัวอย่างรหัสเดียว และไม่มีคำตอบใด ๆ ในวิธีการใช้งานจริง นั่นคือสาเหตุที่สิ่งนี้กำลังถูกลดระดับลง
George Xavier

57

Java เวอร์ชันก่อนหน้าแสดงอักขระ Unicode โดยใช้ชนิดข้อมูลถ่าน 16 บิต การออกแบบนี้มีเหตุผลในเวลานั้นเนื่องจากอักขระ Unicode ทั้งหมดมีค่าน้อยกว่า 65,535 (0xFFFF) และสามารถแสดงใน 16 บิต อย่างไรก็ตามในภายหลัง Unicode เพิ่มค่าสูงสุดเป็น 1,114,111 (0x10FFFF) เนื่องจากค่า 16 บิตมีขนาดเล็กเกินไปที่จะแสดงถึงอักขระ Unicode ทั้งหมดใน Unicode รุ่น 3.1 ค่า 32- บิต - เรียกว่าจุดรหัส - ถูกนำมาใช้สำหรับรูปแบบการเข้ารหัส UTF-32 แต่ค่า 16 บิตนั้นต้องการมากกว่า 32 บิตสำหรับการใช้หน่วยความจำที่มีประสิทธิภาพดังนั้น Unicode จึงนำเสนอการออกแบบใหม่เพื่อให้สามารถใช้งานค่า 16 บิตได้อย่างต่อเนื่อง การออกแบบนี้มีการนำมาใช้ในรูปแบบการเข้ารหัส UTF-16 กำหนดค่า 1,024 ให้กับตัวแทนเสมือนสูง 16 บิต (ในช่วง U + D800 ถึง U + DBFF) และอีก 1,024 ค่าเป็นตัวแทนต่ำ 16 บิต (ในช่วง U + DC00 ถึง U + DFFF)


7
ฉันชอบสิ่งนี้ดีกว่าคำตอบที่ยอมรับเนื่องจากอธิบายว่า Unicode 3.1 สงวนค่า 1024 + 1024 (สูง + ต่ำ) จากค่าเดิม 65535 เพื่อรับค่าใหม่ 1024 * 1024 โดยไม่มีข้อกำหนดเพิ่มเติมที่ parsers เริ่มต้นที่จุดเริ่มต้นของ เชือก
Eric Hirst

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

23

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


6
การอธิบาย สตริงต้องย้อนกลับด้วยอักขระ "จริง" (aka "graphemes" หรือ "องค์ประกอบข้อความ") จุดรหัส "อักขระ" เดี่ยวอาจเป็นหนึ่งหรือสองชิ้น (คู่ตัวแทน) และกราฟอาจเป็นหนึ่งหรือหลายจุดรหัสเหล่านั้น (เช่นรหัสอักขระพื้นฐานบวกหนึ่งหรือมากกว่ารวมรหัสอักขระซึ่งแต่ละอัน อาจเป็นชิ้นหนึ่งหรือสองแบบ 16 บิตหรือ "ตัวอักษร" ยาว) ดังนั้นหนึ่งกราไฟท์อาจเป็นสามตัวอักษรรวมกันในแต่ละ "chars" ยาวรวม 6 "chars" 6 "chars" ทั้งหมดจะต้องถูกเก็บไว้ด้วยกันตามลำดับ (เช่นไม่ได้ย้อนกลับ) เมื่อย้อนกลับสตริงทั้งหมดของตัวละคร
Triynko

4
ดังนั้นประเภทข้อมูล "ถ่าน" จึงค่อนข้างทำให้เข้าใจผิด "ตัวละคร" เป็นคำที่หลวม ประเภท "char" เป็นเพียงขนาดก้อน UTF16 และเราเรียกมันว่าตัวละครเพราะความหายากของคู่ตัวแทนตัวแทนที่เกิดขึ้น (เช่นมักจะหมายถึงจุดรหัสอักขระทั้งหมด) ดังนั้น "ตัวละคร" จริงๆหมายถึงจุดรหัสยูนิโค้ดเดียว แต่ด้วยการรวมอักขระคุณสามารถมีลำดับของอักขระที่แสดงเป็น "character / grapheme / องค์ประกอบข้อความ" เดียว นี่ไม่ใช่วิทยาศาสตร์จรวด แนวคิดนั้นง่าย แต่ภาษากำลังสับสน
Triynko

ในขณะที่ Java ได้รับการพัฒนา Unicode อยู่ในวัยเด็กของมัน Java มีอายุประมาณ 5 ปีก่อนที่ Unicode จะมีคู่ที่เป็นตัวแทนดังนั้นตัวละคร 16 บิตจึงเหมาะสมในเวลานั้น ตอนนี้คุณสามารถใช้ UTF-8 และ UTF-32 ได้ดีกว่า UTF-16
Jonathan Baldwin

23

เพิ่มข้อมูลเพิ่มเติมบางคำตอบข้างต้นจากนี้โพสต์

ทดสอบใน Java-12 ควรทำงานกับ Java เวอร์ชันทั้งหมดที่สูงกว่า 5

เป็นที่กล่าวถึงที่นี่: https://stackoverflow.com/a/47505451/2987755 ,
แล้วแต่จำนวนใดตัวอักษร (Unicode ซึ่งอยู่เหนือ U + FFFF) จะแสดงเป็นคู่ตัวแทนซึ่งร้านค้า Java เป็นคู่ของค่าถ่านคือ Unicode เดียว อักขระถูกแสดงเป็นอักขระ Java สองตัวที่อยู่ติดกัน
อย่างที่เราเห็นในตัวอย่างต่อไปนี้
1. ความยาว:

"🌉".length()  //2, Expectations was it should return 1

"🌉".codePointCount(0,"🌉".length())  //1, To get the number of Unicode characters in a Java String  

2. Equality:
แสดง "🌉" เป็น String โดยใช้ Unicode \ud83c\udf09ดังนี้และตรวจสอบความเท่าเทียมกัน

"🌉".equals("\ud83c\udf09") // true

Java ไม่รองรับ UTF-32

"🌉".equals("\u1F309") // false  

3. คุณสามารถแปลงอักขระ Unicode เป็น Java String

"🌉".equals(new String(Character.toChars(0x0001F309))) //true

4. String.substring () ไม่พิจารณาอักขระเสริม

"🌉🌐".substring(0,1) //"?"
"🌉🌐".substring(0,2) //"🌉"
"🌉🌐".substring(0,4) //"🌉🌐"

เพื่อแก้ปัญหานี้เราสามารถใช้ String.offsetByCodePoints(int index, int codePointOffset)

"🌉🌐".substring(0,"🌉🌐".offsetByCodePoints(0,1) // "🌉"
"🌉🌐".substring(2,"🌉🌐".offsetByCodePoints(1,2)) // "🌐"

สตริง 5. ทำซ้ำ Unicode กับBreakIterator
6. คัดแยกสายกับ Unicode java.text.Collator
7. ตัวละครtoUpperCase(), toLowerCase()วิธีไม่ควรนำมาใช้แทนการใช้ตัวพิมพ์ใหญ่และตัวพิมพ์เล็กสตริงของสถานที่โดยเฉพาะอย่างยิ่ง
8. Character.isLetter(char ch)ไม่รองรับใช้งานได้ดีกว่าCharacter.isLetter(int codePoint)สำหรับแต่ละmethodName(char ch)วิธีในคลาสอักขระจะมีประเภทmethodName(int codePoint)ที่สามารถจัดการอักขระเสริมได้
9. ระบุชุดอักขระในString.getBytes()แปลงจากไบต์เป็นสตริงInputStreamReader ,OutputStreamWriter

Ref:
https://coolsymbol.com/emojis/emoji-for-copy-and-paste.html#objects
https://www.online-toolz.com/tools/text-unicode-entities-convertor.php
https: //www.ibm.com/developerworks/library/j-unicode/index.html
https://www.oracle.com/technetwork/articles/javaee/supplementary-142654.html

ข้อมูลเพิ่มเติมเกี่ยวกับตัวอย่างimage1 image2
คำอื่น ๆ ที่ควรค่าแก่การสำรวจ: การทำให้เป็นมาตรฐาน , BiDi


2
ลงชื่อเข้าใช้เป็นพิเศษเพื่อให้คะแนนคำตอบนี้ (ฉันหมายถึงเปลี่ยนหน้าต่างจากไม่ระบุตัวตนเป็นแบบปกติ: P) คำอธิบายที่ดีที่สุดสำหรับ noob
N-JOY

1
ขอบคุณ! ฉันดีใจที่ได้ช่วย แต่ผู้เขียนโพสต์ต้นฉบับสมควรได้รับการชื่นชมทั้งหมด
dkb

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

6

คู่ตัวแทนแทนหมายถึงวิธีการเข้ารหัสอักขระบางตัวของ UTF-16 ดูที่http://en.wikipedia.org/wiki/UTF-16/UCS-2#Code_points_U.2B10000..U.2B10FFFF


11
"ตัวละคร" เป็นคำที่โหลด
Triynko

1
ไม่มีอักขระใน Unicode แต่มี codepoints codepoint แต่ละรายการสามารถแสดงผลเป็นศูนย์ถึงหลายอักขระ
Nick Volynkin

6

คำนำขนาดเล็ก

  • Unicode แสดงถึงจุดรหัส รหัสจุดแต่ละจุดสามารถเข้ารหัสในบล็อก 8-, 16, - หรือ 32- บิตตามมาตรฐาน Unicode
  • ก่อนหน้า Version 3.1 การใช้งานส่วนใหญ่คือการเข้ารหัส 8 บิตรู้จักกันในชื่อ UTF-8 และการเข้ารหัส 16 บิตหรือที่รู้จักกันในชื่อ UCS-2 หรือ "ชุดอักขระสากลที่เข้ารหัสใน 2 octets" UTF-8 เข้ารหัสจุด Unicode เป็นลำดับของบล็อกขนาด 1 ไบต์ในขณะที่ UCS-2 ใช้เวลา 2 ไบต์เสมอ:

    A = 41 - หนึ่งบล็อก 8 บิตพร้อม UTF-8
    A = 0041 - หนึ่งบล็อก 16 บิตด้วย UCS-2
    Ω = CE A9 - สองบล็อก 8 บิตพร้อม UTF-8
    Ω = 03A9 - หนึ่งบล็อกของ 16 บิตด้วย UCS-2

ปัญหา

สมาคมคิดว่า 16 บิตจะเพียงพอที่จะครอบคลุมภาษาที่มนุษย์อ่านได้ซึ่งให้2 ^ 16 = 65536ค่ารหัสที่เป็นไปได้สิ่งนี้เป็นจริงสำหรับเครื่องบิน 0 หรือที่รู้จักกันในชื่อ BPM หรือเครื่องบินหลายภาษาขั้นพื้นฐานซึ่งประกอบด้วย 55,445 จาก 65536 รหัสคะแนนในวันนี้ BPM ครอบคลุมภาษามนุษย์เกือบทุกภาษาในโลกรวมถึงสัญลักษณ์จีน - ญี่ปุ่น - เกาหลี (CJK)

เวลาผ่านไปและชุดตัวละครเอเชียใหม่ถูกเพิ่มเข้ามาสัญลักษณ์จีนได้รับมากกว่า 70,000 คะแนนเพียงอย่างเดียว ตอนนี้มีแม้แต่คะแนน Emojiเป็นส่วนหนึ่งของมาตรฐาน😺 มีการเพิ่มเครื่องบิน 16 "เพิ่มเติม" ใหม่ ห้อง UCS-2 ไม่เพียงพอที่จะครอบคลุมทุกสิ่งที่ใหญ่กว่า Plane-0

การตัดสินใจ Unicode

  1. จำกัด Unicode ให้กับ 17 เครื่องบิน× 65 536 ตัวอักษรต่อเครื่องบิน = 1 114 112 คะแนนสูงสุด
  2. แสดง UTF-32 เดิมชื่อ UCS-4 เพื่อเก็บ 32- บิตสำหรับแต่ละจุดรหัสและครอบคลุมเครื่องบินทั้งหมด
  3. ใช้ UTF-8 ต่อไปเป็นการเข้ารหัสแบบไดนามิก จำกัด UTF-8 ถึง 4 ไบต์สูงสุดสำหรับแต่ละจุดรหัสเช่นจาก 1 ถึง 4 ไบต์ต่อจุด
  4. เลิกใช้ UCS-2
  5. สร้าง UTF-16 ตาม UCS-2 สร้าง UTF-16 แบบไดนามิกดังนั้นใช้ 2 ไบต์หรือ 4 ไบต์ต่อจุด กำหนด 1024 คะแนน U + D800 – U + DBFF เรียก High Surrogates เป็น UTF-16 กำหนด 1024 สัญลักษณ์ U + DC00 – U + DFFF เรียกว่า Low Surrogates ถึง UTF-16

    ด้วยการเปลี่ยนแปลงเหล่านั้น BPM ถูกปกคลุมด้วย 1 บล็อก 16 บิตใน UTF-16 ในขณะที่ "อักขระเสริม" ทั้งหมดจะถูกปกคลุมด้วยคู่ตัวแทนตัวแทนนำเสนอ 2 บล็อก 16 บิตแต่ละ 1024x1024 = 1 048 576 คะแนน

    ตัวแทนสูงแจ๋วตัวแทนต่ำ การเบี่ยงเบนจากกฎนี้ถือว่าเป็นการเข้ารหัสที่ไม่ดี ตัวอย่างเช่นตัวแทนที่ไม่มีคู่ไม่ถูกต้องสถานะตัวแทนที่ต่ำก่อนที่ตัวแทนที่สูงจะไม่ถูกต้อง

    𝄞 'สัญลักษณ์ทางดนตรี G CLEF' ถูกเข้ารหัสใน UTF-16 เป็นคู่ของตัวแทน 0xD834 0xDD1E (2 คูณ 2 ไบต์)
    ใน UTF-8 เป็น 0xF0 0x9D 0x84 0x9E (4 คูณ 1 ไบต์)
    ในรูปของ UTF-16 0x0001D11E (1 by 4 bytes)

สถานการณ์ปัจจุบัน

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

รายละเอียดทางประวัติศาสตร์หลายคนถูกระงับการติดตามหัวข้อ⚖
สามารถดู Unicode Standard ล่าสุดได้ที่http://www.unicode.org/versions/latest


3

คู่ตัวแทนคือ 'หน่วยรหัส' สองชุดใน UTF-16 ที่รวมเป็นหนึ่ง 'รหัสจุด' เอกสาร Java ระบุว่า 'คะแนนรหัส' เหล่านี้จะยังคงถูกต้องโดยสั่ง 'หน่วยรหัส' อย่างถูกต้องหลังจากย้อนกลับ กล่าวเพิ่มเติมอีกว่าหน่วยรหัสตัวแทนเสมือนที่ไม่มีการจับคู่สองคู่นั้นอาจถูกสลับกลับและสร้างคู่ตัวแทนตัวแทนที่ถูกต้อง ซึ่งหมายความว่าหากมีหน่วยรหัส unpaired แล้วมีโอกาสที่การย้อนกลับของการย้อนกลับอาจไม่เหมือนกัน!

แม้ว่าการสังเกตเห็นว่าเอกสารนั้นไม่ได้เกี่ยวกับ Graphemes - ซึ่งเป็น codepoints หลายตัวรวมกัน ซึ่งหมายถึง e และสำเนียงที่ไปพร้อมกับมันยังคงสามารถเปลี่ยนได้ดังนั้นให้วางสำเนียงก่อน e ซึ่งหมายความว่าหากมีเสียงสระอีกตัวหนึ่งอยู่ข้างหน้า e อาจจะได้สำเนียงที่อยู่บน e

อ๊ะ!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.