Apa itu "pasangan pengganti" di Jawa?

149

Saya membaca dokumentasi untuk StringBuffer, khususnya metode reverse () . Dokumentasi itu menyebutkan sesuatu tentang pasangan pengganti . Apakah pasangan pengganti dalam konteks ini? Dan apakah pengganti rendah dan tinggi ?

Raymond
sumber
3
Ini terminologi UTF-16, dijelaskan di sini: download.oracle.com/javase/6/docs/api/java/lang/…
wkl
1
Metode itu buggy: harus membalik karakter penuh ᴀᴋᴀ titik kode - bukan bagian yang terpisah, ᴀᴋᴀ unit kode. Bugnya adalah bahwa metode legacy tertentu hanya bekerja pada unit char individual dan bukan pada titik kode, yang Anda inginkan String terdiri dari, bukan hanya unit char. Sayang sekali Java tidak memungkinkan Anda untuk menggunakan OO untuk memperbaikinya, tetapi Stringkelas dan StringBufferkelas telah finaldiubah. Katakan, bukankah itu eufemisme untuk terbunuh? :)
tchrist
2
@tchrist Dokumentasi (dan sumber) mengatakan bahwa ia membalikkan sebagai serangkaian titik kode. (Agaknya 1.0.2 tidak melakukan itu, dan Anda tidak akan pernah mendapatkan perubahan perilaku hari ini.)
Tom Hawtin - tackline

Jawaban:

127

Istilah "pasangan pengganti" mengacu pada cara pengkodean karakter Unicode dengan poin kode tinggi dalam skema pengkodean UTF-16.

Dalam pengkodean karakter Unicode, karakter dipetakan ke nilai antara 0x0 dan 0x10FFFF.

Secara internal, Java menggunakan skema pengkodean UTF-16 untuk menyimpan string teks Unicode. Dalam UTF-16, unit kode 16-bit (dua byte) digunakan. Karena 16 bit hanya dapat berisi rentang karakter dari 0x0 hingga 0xFFFF, beberapa kompleksitas tambahan digunakan untuk menyimpan nilai di atas rentang ini (0x10000 hingga 0x10FFFF). Ini dilakukan dengan menggunakan pasangan unit kode yang dikenal sebagai pengganti.

Unit kode pengganti ada dalam dua rentang yang dikenal sebagai "pengganti tinggi" dan "pengganti rendah", tergantung pada apakah mereka diperbolehkan di awal atau akhir urutan dua kode-unit.

Jeffrey L Whitledge
sumber
4
ini memiliki suara terbanyak namun tidak memberikan contoh kode tunggal. Juga tidak ada jawaban ini tentang cara menggunakannya. Itu sebabnya ini sedang downvoting.
George Xavier
57

Versi Java awal mewakili karakter Unicode menggunakan tipe data char 16-bit. Desain ini masuk akal pada saat itu, karena semua karakter Unicode memiliki nilai kurang dari 65.535 (0xFFFF) dan dapat direpresentasikan dalam 16 bit. Namun, kemudian, Unicode meningkatkan nilai maksimum menjadi 1.114.111 (0x10FFFF). Karena nilai 16-bit terlalu kecil untuk mewakili semua karakter Unicode dalam Unicode versi 3.1, nilai 32-bit - disebut titik kode - diadopsi untuk skema pengkodean UTF-32. Tetapi nilai 16-bit lebih disukai daripada nilai 32-bit untuk penggunaan memori yang efisien, sehingga Unicode memperkenalkan desain baru untuk memungkinkan penggunaan berkelanjutan nilai 16-bit. Desain ini, yang diadopsi dalam skema pengkodean UTF-16, memberikan nilai 1.024 untuk pengganti tinggi 16-bit (dalam kisaran U + D800 hingga U + DBFF) dan 1,024 nilai lain untuk pengganti rendah 16-bit (dalam kisaran U + DC00 ke U + DFFF).

ibrahem shabban
sumber
7
Saya suka ini lebih baik daripada jawaban yang diterima, karena ini menjelaskan bagaimana Unicode 3.1 dicadangkan 1024 + 1024 (tinggi + rendah) nilai dari 65535 asli untuk mendapatkan 1024 * 1024 nilai baru, tanpa persyaratan tambahan yang parser mulai pada awal tali.
Eric Hirst
1
Saya tidak suka jawaban ini karena menyiratkan UTF-16 adalah pengkodean Unicode yang paling hemat memori. UTF-8 ada, dan tidak membuat sebagian besar teks sebagai dua byte. UTF-16 sebagian besar digunakan hari ini karena Microsoft mengambilnya sebelum UTF-32, bukan untuk efisiensi memori. Satu-satunya saat Anda benar - benar menginginkan UTF-16 adalah ketika Anda melakukan banyak penanganan file pada Windows, dan karenanya banyak membaca dan menulis. Jika tidak, UTF-32 untuk kecepatan tinggi (offset konstan b / c) atau UTF-8 untuk memori rendah (b / c minimum 1 byte)
Gugatan Dana Monica
23

Apa yang dikatakan oleh dokumentasi adalah bahwa string UTF-16 yang tidak valid dapat menjadi valid setelah memanggil reversemetode karena mereka mungkin merupakan kebalikan dari string yang valid. Pasangan pengganti (dibahas di sini ) adalah pasangan nilai 16-bit dalam UTF-16 yang menyandikan titik kode Unicode tunggal; pengganti rendah dan tinggi adalah dua bagian dari pengkodean itu.

Jeremiah Willcock
sumber
6
Klarifikasi. String harus dibalik pada karakter "benar" (alias "grapheme" atau "elemen teks"). Satu titik kode "karakter" dapat berupa satu atau dua potongan "char" (pasangan pengganti), dan sebuah grafem dapat menjadi satu atau lebih dari titik kode tersebut (yaitu kode karakter dasar ditambah satu atau lebih menggabungkan kode karakter, masing-masing bisa satu atau dua chunks 16-bit atau "karakter" panjang). Jadi satu grapheme bisa tiga karakter menggabungkan masing-masing dua "karakter" panjang, berjumlah 6 "karakter". Semua 6 "karakter" harus disimpan bersama, dalam urutan (yaitu tidak terbalik), ketika membalikkan seluruh rangkaian karakter.
Triynko
4
Oleh karena itu tipe data "char" agak menyesatkan. "karakter" adalah istilah yang longgar. Tipe "char" sebenarnya hanya ukuran potongan UTF16 dan kami menyebutnya karakter karena kelangkaan relatif dari pasangan pengganti yang terjadi (yaitu biasanya mewakili titik kode karakter keseluruhan), jadi "karakter" benar-benar merujuk pada satu titik kode unicode tunggal , tetapi kemudian dengan menggabungkan karakter, Anda dapat memiliki urutan karakter yang ditampilkan sebagai "karakter / grapheme / elemen teks" tunggal. Ini bukan ilmu roket; konsepnya sederhana, tetapi bahasanya membingungkan.
Triynko
Pada saat Java sedang dikembangkan, Unicode masih dalam masa pertumbuhan. Java sudah ada sekitar 5 tahun sebelum Unicode mendapatkan pasangan pengganti, jadi char 16-bit cukup pas pada saat itu. Sekarang, Anda jauh lebih baik menggunakan UTF-8 dan UTF-32 daripada UTF-16.
Jonathan Baldwin
23

Menambahkan beberapa info lagi ke jawaban di atas dari pos ini .

Diuji di Java-12, harus bekerja di semua versi Java di atas 5.

Seperti yang disebutkan di sini: https://stackoverflow.com/a/47505451/2987755 ,
karakter mana saja (yang Unicode-nya di atas U + FFFF) diwakili sebagai pasangan pengganti, yang Java simpan sebagai sepasang nilai char, yaitu Unicode tunggal karakter direpresentasikan sebagai dua karakter Jawa yang berdekatan.
Seperti yang bisa kita lihat pada contoh berikut.
1. Panjang:

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

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

2. Kesetaraan:
Mewakili "🌉" ke String menggunakan Unicode \ud83c\udf09seperti di bawah ini dan periksa kesetaraan.

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

Java tidak mendukung UTF-32

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

3. Anda dapat mengonversi karakter Unicode ke Java String

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

4. String.substring () tidak mempertimbangkan karakter tambahan

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

Untuk mengatasi ini kita bisa menggunakan String.offsetByCodePoints(int index, int codePointOffset)

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

5. Iterasi Unicode string dengan BreakIterator
6. Sorting Strings dengan Unicode java.text.Collator
7. Karakter ini toUpperCase(), toLowerCase(), metode tidak boleh digunakan, sebagai gantinya, huruf besar penggunaan String dan huruf kecil dari daerah tertentu.
8. Character.isLetter(char ch)tidak mendukung, lebih baik digunakan Character.isLetter(int codePoint), untuk setiap methodName(char ch)metode di kelas Karakter akan ada jenis methodName(int codePoint)yang dapat menangani karakter tambahan.
9. Tentukan charset di String.getBytes(), konversi dari Bytes ke String 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

Info lebih lanjut tentang contoh image1 image2
Istilah lain yang perlu dijelajahi: Normalisasi , BiDi

dkb
sumber
2
masuk secara khusus untuk memilih jawaban ini (maksud saya mengubah jendela dari penyamaran menjadi yang normal: P). Penjelasan terbaik untuk noob
N-JOY
1
Terima kasih !, Saya senang itu membantu, tetapi penulis posting asli layak menerima semua penghargaan.
dkb
Contoh yang bagus! Saya login untuk meng-upgrade-nya juga :) Dan lagi, itu membuat saya berpikir (lagi) bahwa saya benar-benar tidak mengerti mengapa Java membuat bug TAHU tetap hidup dalam kode mereka. Saya sangat menghargai mereka tidak ingin memecahkan kode yang ada, tetapi ayolah ... berapa jam telah hilang untuk mengatasi bug ini? Jika rusak, perbaiki, sialan!
Franz D.
6

Kata pengantar kecil

  • Unicode mewakili poin kode. Setiap titik kode dapat dikodekan dalam blok 8-, 16, - atau 32-bit sesuai dengan standar Unicode.
  • Sebelum Versi 3.1, sebagian besar yang digunakan adalah pengkodean 8-bit, yang dikenal sebagai pengkodean UTF-8, dan 16-bit, yang dikenal sebagai UCS-2 atau “Universal Character Set yang dikodekan dalam 2 oktet”. UTF-8 mengkodekan poin Unicode sebagai urutan blok 1-byte, sementara UCS-2 selalu mengambil 2 byte:

    A = 41 - satu blok 8-bit dengan UTF-8
    A = 0041 - satu blok 16-bit dengan UCS-2
    Ω = CE A9 - dua blok 8-bit dengan UTF-8
    Ω = 03A9 - satu blok dari 16-bit dengan UCS-2

Masalah

Konsorsium berpikir bahwa 16 bit akan cukup untuk mencakup bahasa yang dapat dibaca manusia, yang memberikan 2 ^ 16 = 65536 nilai kode yang mungkin. Ini berlaku untuk Plane 0, juga dikenal sebagai BPM atau Basic Multilingual Plane, yang mencakup 55.445 dari 65.536 poin kode saat ini. BPM mencakup hampir setiap bahasa manusia di dunia, termasuk simbol Cina-Jepang-Korea (CJK).

Waktu berlalu dan set karakter Asia baru ditambahkan, simbol Cina mengambil lebih dari 70.000 poin saja. Sekarang, bahkan ada poin Emoji sebagai bagian dari standar 😺. 16 Pesawat "tambahan" baru telah ditambahkan. Ruang UCS-2 tidak cukup untuk menutupi sesuatu yang lebih besar dari Plane-0.

Keputusan Unicode

  1. Batasi Unicode ke 17 pesawat × 65 536 karakter per pesawat = 1 114 112 poin maksimum.
  2. Hadir UTF-32, sebelumnya dikenal sebagai UCS-4, untuk menampung 32-bit untuk setiap titik kode dan mencakup semua pesawat.
  3. Terus menggunakan UTF-8 sebagai pengkodean dinamis, batasi maksimum UTF-8 hingga 4 byte untuk setiap titik kode, yaitu dari 1 hingga 4 byte per titik.
  4. Jelek UCS-2
  5. Buat UTF-16 berdasarkan UCS-2. Jadikan UTF-16 dinamis, sehingga dibutuhkan 2 byte atau 4 byte per titik. Tetapkan 1024 poin U + D800 – U + DBFF, yang disebut High Surrogate, ke UTF-16; menetapkan 1024 simbol U + DC00 – U + DFFF, disebut Low Surrogates, ke UTF-16.

    Dengan perubahan itu, BPM ditutupi dengan 1 blok 16 bit dalam UTF-16, sementara semua "karakter tambahan" ditutupi dengan Pasangan Pengganti yang masing-masing menghadirkan 2 blok dengan 16 bit, total 1024x1024 = 1 048 576 poin.

    Seorang pengganti tinggi mendahului seorang pengganti rendah . Setiap penyimpangan dari aturan ini dianggap sebagai penyandian yang buruk. Misalnya, pengganti tanpa pasangan salah, posisi pengganti rendah sebelum pengganti tinggi salah.

    𝄞, 'MUSICAL SIMBOL G CLEF', dikodekan dalam UTF-16 sebagai pasangan pengganti 0xD834 0xDD1E (2 dengan 2 byte),
    dalam UTF-8 sebagai 0xF0 0x9D 0x84 0x9E (4 dengan 1 byte),
    dalam UTF-32 sebagai 0x0001D11E (1 kali 4 byte).

Situasi saat ini

  • Meskipun menurut standar, para pengganti secara khusus ditugaskan hanya untuk UTF-16, secara historis beberapa aplikasi Windows dan Java menggunakan poin-poin UTF-8 dan UCS-2 dicadangkan sekarang untuk kisaran pengganti.
    Untuk mendukung aplikasi lawas dengan pengkodean UTF-8 / UTF-16 yang salah, WTF-8 standar baru , Format Transformasi Wobbly, telah dibuat. Ini mendukung titik pengganti sewenang-wenang, seperti pengganti yang tidak berpasangan atau urutan yang salah. Saat ini, beberapa produk tidak mematuhi standar dan memperlakukan UTF-8 sebagai WTF-8.
  • Solusi pengganti membuka banyak masalah keamanan dalam konversi antara pengkodean yang berbeda, kebanyakan dari mereka ditangani dengan baik.

Banyak detail historis ditekan untuk mengikuti topik ⚖.
Standar Unicode terbaru dapat ditemukan di http://www.unicode.org/versions/latest

Artru
sumber
3

Pasangan pengganti adalah dua 'unit kode' di UTF-16 yang membentuk satu 'titik kode'. Dokumentasi Java menyatakan bahwa 'titik kode' ini akan tetap valid, dengan 'unit kode' mereka dipesan dengan benar, setelah kebalikannya. Lebih lanjut menyatakan bahwa dua unit kode pengganti tidak berpasangan dapat dibalik dan membentuk pasangan pengganti yang valid. Yang berarti bahwa jika ada unit kode tidak berpasangan, maka ada kemungkinan bahwa kebalikan dari kebalikannya mungkin tidak sama!

Namun, perhatikan bahwa dokumentasi tidak mengatakan apa pun tentang Grapheme - yang merupakan gabungan beberapa codepoint. Yang berarti e dan aksen yang menyertainya mungkin masih diaktifkan, sehingga menempatkan aksen di depan e. Yang berarti jika ada vokal lain sebelum e mungkin mendapatkan aksen yang ada di e.

Astaga!

Gerard ONeill
sumber