Java'da “vekil çifti” nedir?


149

StringBufferÖzellikle reverse () yöntemi için belgeleri okuyordum . Bu belgeler vekil çiftler hakkında bir şeyden bahsediyor . Bu bağlamda bir vekil çift nedir? Ve düşük ve yüksek taşıyıcılar nelerdir?


3
UTF-16 terminolojisi, burada açıklanmıştır: download.oracle.com/javase/6/docs/api/java/lang/…
wkl

1
Bu yöntem hatalıdır: tam karakterleri ᴀᴋᴀ kod noktalarını tersine çevirmelidir - ayrı parçaları değil , units kod birimleri. Hata şu ki, belirli eski yöntem kod noktaları yerine yalnızca bireysel char birimleri üzerinde çalışır, bu da sadece char birimleri değil, oluşmasını istediğiniz şeydir String. Çok kötü Java bunu düzeltmek için OO kullanmanıza izin vermez, ancak hem Stringsınıf hem de StringBuffersınıflar finalizlenir. Söylesene, bu öldürülen için bir örtmece değil mi? :)
tchrist

2
@tchrist Dokümantasyon (ve kaynak) bir kod noktası dizesi olarak tersine döndüğünü söylüyor. (Muhtemelen 1.0.2 bunu yapmadı ve bu günlerde asla böyle bir davranış değişikliği elde etmeyeceksiniz.)
Tom Hawtin -

Yanıtlar:


127

"Yedek çift" terimi, UTF-16 kodlama şemasında yüksek kod noktalarına sahip Unicode karakterleri kodlama yöntemini ifade eder.

Unicode karakter kodlamasında, karakterler 0x0 ile 0x10FFFF arasındaki değerlerle eşlenir.

Dahili olarak Java, Unicode metin dizelerini saklamak için UTF-16 kodlama şemasını kullanır. UTF-16'da 16 bit (iki bayt) kod birimleri kullanılır. 16 bit yalnızca 0x0 ile 0xFFFF arasındaki karakterleri içerebileceğinden, bu aralığın üzerindeki değerleri (0x10000 - 0x10FFFF) saklamak için bazı ek karmaşıklıklar kullanılır. Bu, surrogatlar olarak bilinen kod birimleri çiftleri kullanılarak yapılır.

Yedek kod birimleri, iki kod birimi dizisinin başlangıcında veya sonunda izin verilip verilmemesine bağlı olarak "yüksek taşıyıcılar" ve "düşük taşıyıcılar" olarak bilinen iki aralıktadır.


4
bu en çok oyu alır ancak tek bir kod örneği sağlamaz. Bu cevapların hiçbiri aslında nasıl kullanılacağına da sahip değildir. Bu yüzden aşağı indiriliyor.
George Xavier

57

İlk Java sürümleri, 16 bitlik char veri türü kullanılarak Unicode karakterleri temsil ediyordu. Bu tasarım o zaman mantıklıydı, çünkü tüm Unicode karakterlerin değerleri 65.535'ten (0xFFFF) düşüktü ve 16 bit olarak gösterilebilir. Ancak daha sonra Unicode maksimum değeri 1.114.111'e (0x10FFFF) yükseltti. 16 bit değerler Unicode sürüm 3.1'deki tüm Unicode karakterleri temsil etmek için çok küçük olduğundan, UTF-32 kodlama şeması için 32 bit değerler - kod noktaları adı verilen - kabul edildi. Ancak verimli bellek kullanımı için 32 bit değerlere göre 16 bit değerler tercih edilir, bu nedenle Unicode, 16 bit değerlerin sürekli kullanımına izin vermek için yeni bir tasarım tanıttı. UTF-16 kodlama şemasında benimsenen bu tasarım, 16-bit yüksek taşıyıcılara (U + D800 - U + DBFF aralığında) 1.024 değer ve 16-bit düşük taşıyıcılara (U + DC00 aralığında) 1.024 değer atar U + DFFF).


7
Unicode 3.1'in 1024 + 1024 (yüksek + düşük) değerlerin orijinal 65535'ten nasıl ayrıldığını açıkladığı için, ayrıştırıcıların başında başlayan ek bir gereksinim olmadan 1024 * 1024 yeni değerler elde etmeyi açıklar. dize.
Eric Hirst

1
UTF-16'nın bellek açısından en verimli Unicode kodlaması olduğunu ima etmek için bu cevabı sevmiyorum. UTF-8 var ve yok iki bayt olarak en metin oluşturmak. UTF-16 günümüzde çoğunlukla kullanılmaktadır, çünkü Microsoft UTF-32'den önce onu seçti, bellek verimliliği için değil. Sadece zamanı aslında ediyorum istediğiniz Windows üzerinde dosya uğraşmayı yapıyoruz UTF-16 ve bu nedenle okuma hem ve onu çok yazma. Aksi takdirde, yüksek hız için UTF-32 (b / c sabit ofsetleri) veya düşük bellek için UTF-8 (b / c minimum 1 bayt)
Monica'nın Davası

23

Belgelerin söylediği, geçersiz UTF-16 dizelerinin reverseyöntemi çağırdıktan sonra geçerli dizelerin tersi olabileceği için geçerli olabileceğidir. Bir vekil çift ( burada tartışılmıştır ) UTF-16'da tek bir Unicode kod noktasını kodlayan bir çift 16 bitlik değerdir; düşük ve yüksek taşıyıcılar bu kodlamanın iki yarısıdır.


6
Açıklama. Bir dize, "doğru" karakterlerde ("grafik öğeleri" veya "metin öğeleri" olarak da bilinir) ters çevrilmelidir. Tek bir "karakter" kod noktası, bir veya iki "karakter" parçası (vekil çifti) olabilir ve bir grafik, bu kod noktalarından biri veya daha fazlası olabilir (yani, bir temel karakter kodu artı bir veya daha fazla birleştiren karakter kodu olabilir; bir veya iki 16 bitlik yığın veya "karakter" uzunluğunda olabilir). Yani tek bir grafik her iki "karakter" uzun, toplam 6 "karakter" üç karakter birleştirilebilir. Tüm karakter dizisini tersine çevirirken 6 "karakter" in tümü birlikte tutulmalıdır (ters çevrilmemelidir).
Triynko

4
Bu nedenle "char" veri türü oldukça yanıltıcıdır. "karakter" gevşek bir terimdir. "Char" tipi gerçekten sadece UTF16 yığın boyutudur ve vekil çiftlerin göreli nadir olması nedeniyle karakter olarak adlandırırız (yani genellikle bir tam karakter kod noktasını temsil eder), bu yüzden "karakter" gerçekten tek bir unicode kod noktasına işaret eder , ancak daha sonra birleştirilen karakterlerle, tek bir "karakter / grafik / metin öğesi" olarak görüntülenen bir karakter dizisine sahip olabilirsiniz. Bu roket bilimi değil; kavramlar basit, ancak dil kafa karıştırıcı.
Triynko

Java geliştirilirken Unicode henüz emekleme dönemindeydi. Java, Unicode'un vekil çiftleri almadan önce yaklaşık 5 yıl boyunca, bu yüzden 16 bitlik bir char o zamanlar oldukça iyi uyuyordu. Artık UTF-8 ve UTF-32'yi UTF-16'dan çok daha iyi durumdasınız.
Jonathan Baldwin

23

Yukarıdaki cevaplara biraz daha bilgi eklemek bu yazı.

Java-12'de test edilmiştir, 5'in üzerindeki tüm Java sürümlerinde çalışmalıdır.

Burada belirtildiği gibi: https://stackoverflow.com/a/47505451/2987755 ,
hangi karakter (Unicode'u U + FFFF'nin üstünde), Java'nın bir çift char değeri olarak depoladığı bir vekil çift olarak temsil edilir, yani tek Unicode karakteri iki bitişik Java karakteri olarak gösterilir.
Aşağıdaki örnekte gördüğümüz gibi.
1. Uzunluk:

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

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

2. Eşitlik: Aşağıdaki gibi
Unicode kullanarak Dizeye "🌉" yazın \ud83c\udf09ve eşitliği kontrol edin.

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

Java UTF-32'yi desteklemez

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

3. Unicode karakterini Java String'e dönüştürebilirsiniz

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

4. String.substring (), ek karakterleri dikkate almaz

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

Bunu çözmek için kullanabiliriz String.offsetByCodePoints(int index, int codePointOffset)

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

5. yineleme Unicode dizesi BreakIterator
Unicode ile 6. Sıralama Strings java.text.Collator
7. Karakter en toUpperCase(), toLowerCase()yöntemler kullanılmamalıdır, bunun yerine, kullanımı dize büyük harf ve özellikle yerel ayarları küçük harf.
8. Karakter sınıfındaki her bir yöntem için Character.isLetter(char ch), daha iyi kullanılanları desteklemez, bunların türü ek karakterleri işleyebilir. 9. Bayt'tan Dize'ye dönüştürerek karakter kümesini belirtin ,Character.isLetter(int codePoint)methodName(char ch)methodName(int codePoint)
String.getBytes()InputStreamReaderOutputStreamWriter

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

Örnek hakkında daha fazla bilgi image1 image2 Keşfetmeye
değer diğer terimler: Normalleştirme , BiDi


2
Bu cevaba oy vermek için özel olarak oturum açtım (yani pencereyi gizli moddan normal olana değiştirdim: P). Bir çaylak için en iyi açıklama
N-JOY

1
Teşekkür ederim !, Yardımcı olduğuna sevindim, ancak orijinal yazı yazarı tüm takdirleri hak ediyor.
dkb

Harika örnekler! Ben de yukarı oy için giriş yaptım :) Ve tekrar, gerçekten (neden) Java neden kodları KNOWN hata canlı tutar gerçekten anlamıyorum düşünüyorum. Tamamen saygı var onlar mevcut kodu kırmak istemiyorum, ama hadi ... bu hatalar üzerinde çalışırken kaç saat kayboldu? Eğer kırılırsa, düzeltin, kahretsin!
Franz D.


6

Küçük önsöz

  • Unicode kod noktalarını temsil eder. Her kod noktası Unicode standardına göre 8-, 16, veya 32 bit bloklar halinde kodlanabilir.
  • Sürüm 3.1'den önce, çoğunlukla kullanımda UTF-8 olarak bilinen 8 bit koruma ve UCS-2 veya “2 oktet kodlu Evrensel Karakter Seti” olarak bilinen 16 bit kodlama kullanılmıştır. UTF-8, Unicode noktalarını 1 baytlık bloklar halinde kodlarken, UCS-2 her zaman 2 bayt alır:

    A = 41 - UTF-8 ile 8 bitlik bir blok
    A = 0041 - UCS-2
    CE = CE A9 ile 16 bitlik bir blok - UTF-8
    Ω = 03A9 ile 8 bitlik iki blok - bir blok UCS-2 ile 16 bit

Sorun

Konsorsiyum, 16 bitin okunabilir herhangi bir dili kapsaması için yeterli olacağını düşündü ve bu da 2 ^ 16 = 65536 olası kod değeri verdi. Bu, bugün 65536 kod noktasının 55.445'ini içeren BPM veya Temel Çok Dilli Düzlem olarak da bilinen Düzlem 0 için geçerlidir. BPM, Çin-Japon-Kore sembolleri (CJK) dahil olmak üzere dünyadaki hemen hemen tüm insan dillerini kapsamaktadır.

Zaman geçti ve yeni Asya karakter setleri eklendi, Çin sembolleri sadece 70.000'den fazla puan aldı. Şimdi, standart 😺'nın bir parçası olarak Emoji noktaları bile var . Yeni 16 "ek" Uçak eklendi. UCS-2 odası, Plane-0'dan daha büyük bir şeyi kapsayacak kadar yeterli değildi.

Unicode kararı

  1. Unicode'u 17 düzlemle sınırlayın × Düzlem başına 65536 karakter = 1 114 112 maksimum puan.
  2. Her biri UCS-4 olarak bilinen UTF-32'yi her bir kod noktası için 32 bit tutmak ve tüm düzlemleri kapsamak üzere sunar.
  3. UTF-8'i dinamik kodlama olarak kullanmaya devam edin, her bir kod noktası için UTF-8'i maksimum 4 bayt ile sınırlandırın, yani nokta başına 1 ila 4 bayt.
  4. Kullanımdan kaldırıldı UCS-2
  5. UCS-2'ye dayalı UTF-16 oluşturun. UTF-16'yı dinamik yapın, böylece nokta başına 2 bayt veya 4 bayt alır. UTF-16'ya 1024 puan U + D800 – U + DBFF, Yüksek Suretler olarak atayın; UTF-16'ya Düşük Suretler adı verilen 1024 U + DC00 – U + DFFF sembolü atayın.

    Bu değişikliklerle BPM, UTF-16'da 16 bitlik 1 blok ile kaplanırken, tüm "Ek karakterler", her biri 16 bitlik 2 blok sunan Toplam 1024x1024 = 1 048 576 puan olan Yedek Çiftlerle kaplıdır .

    Yüksek bir vekil düşük bir vekilden önce gelir . Bu kuraldan sapma kötü kodlama olarak kabul edilir. Örneğin, çifti olmayan bir vekil yanlıştır, yüksek bir vekilden önce duran düşük vekil yanlıştır.

    𝄞, 'MÜZİK SEMBOLÜ G CLEF', UTF-16'da bir çift taşıyıcı 0xD834 0xDD1E (2'ye 2 bayt),
    UTF-8'de 0xF0 0x9D 0x84 0x9E (4'e 1 bayt),
    UTF-32'de 0x0001D11E (1 x 4 bayt).

Mevcut durum

  • Her ne kadar standarda göre taşıyıcılar sadece UTF-16'ya atanmış olsa da, tarihsel olarak bazı Windows ve Java uygulamaları şu anda yedek aralığa ayrılmış UTF-8 ve UCS-2 noktalarını kullanıyordu.
    Yanlış UTF-8 / UTF-16 kodlamalı eski uygulamaları desteklemek için yeni bir standart WTF-8 , Titreşimsiz Dönüşüm Biçimi oluşturuldu. Eşlenmemiş vekil veya yanlış bir dizi gibi keyfi vekil noktaları destekler. Bugün, bazı ürünler standarda uymuyor ve UTF-8'e WTF-8 olarak davranıyor.
  • Yedek çözüm , farklı kodlamalar arasında dönüşümde birçok güvenlik problemi açtı , çoğu iyi işlendi.

Birçok tarihi detay ⚖ konusunu takip etmek için bastırıldı.
En son Unicode Standardını http://www.unicode.org/versions/latest adresinde bulabilirsiniz.


3

Bir vekil çift UTF-16'da bir 'kod noktası' oluşturan iki 'kod birimidir'. Java belgeleri, bu 'kod noktalarının' tersine doğru, 'kod birimleri' doğru bir şekilde sipariş edildiğinde geçerli olacağını belirtmektedir. Ayrıca iki eşleştirilmemiş vekil kod biriminin ters çevrilebileceğini ve geçerli bir vekil çifti oluşturabileceğini belirtir. Bu, eşleştirilmemiş kod birimleri varsa, tersi tersinin aynı olmayabileceği anlamına gelir!

Bununla birlikte, belgeler Grafik Kodları hakkında hiçbir şey söylemez - bunlar birden fazla kod noktası birleştirilmiştir. Yani e ve onunla birlikte gelen aksan yine de değiştirilebilir, böylece aksanı e'nin önüne yerleştirebilirsiniz. Bu, e'den önce başka bir sesli harf olması durumunda, e'nin üzerindeki aksanı alabilir.

Olmadı!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.