Normalleştirilmiş UTF-8 nedir?


129

Yoğun bakım projesi (aynı zamanda artık bir olan PHP kütüphanesi ) ararken daha kolay değerleri karşılaştırmak için yapmak yardım normalleştirmek UTF-8 dizeleri için gerekli sınıfları içerir.

Ancak bunun uygulamalar için ne anlama geldiğini anlamaya çalışıyorum . Örneğin, hangi durumlarda "Uyumluluk eşdeğeri" yerine "Kanonik Eşdeğer" veya tam tersi olmasını isterim?


230
? Kim ̸͢k̵͟n̴͘ǫw̸̛s͘ w͘͢ḩ̵a҉̡͢t dehşet yalanına Dark kalbin Unicode ͞
ObscureRobot

@ObscureRobot Bu ekstra sembollerin durumları olup olmadığını gerçekten bilmek istiyorum
eonil

1
@Eonil - Unicode bağlamında durumun ne anlama geldiğinden emin değilim.
ObscureRobot

Örneğin @ObscureRobot, bu gibi bazı kod noktası: (begin curved line) (char1) (char2) … (charN) (end curved line)yerine bu: (curved line marker prefix) (char1) (curved line marker prefix) (char2) (curved line marker prefix) (char2). Başka bir deyişle, oluşturulabilen minimum birim?
eonil

2
Bu kendi başına iyi bir soru gibi geliyor.
ObscureRobot

Yanıtlar:


181

Unicode Normalleştirme Hakkında Bilmek İstemediğiniz Her Şey

Kanonik Normalleştirme

Unicode, bazı karakterleri, özellikle de aksanlı karakterleri kodlamanın birden çok yolunu içerir. Kanonik normalleştirme, kod noktalarını kanonik bir kodlama formuna dönüştürür. Ortaya çıkan kod noktaları, yazı tiplerindeki veya işleme motorundaki herhangi bir hatayı engelleyen orijinallerle aynı görünmelidir.

Ne Zaman Kullanılmalı

Sonuçlar aynı göründüğünden, sonucun bit ile girdiyle aynı bit olmamasına tahammül edebildiğiniz sürece, bir dizeyi saklamadan veya görüntülemeden önce kanonik normalleştirme uygulamak her zaman güvenlidir.

Kanonik normalleştirme 2 şekilde gelir: NFD ve NFC. İkisi, birinin bu iki form arasında kayıpsız olarak dönüştürülebilmesi anlamında eşdeğerdir. NFC altında iki dizeyi karşılaştırmak, her zaman onları NFD altında karşılaştırmakla aynı sonucu verecektir.

NFD

NFD, karakterleri tamamen genişletmiştir. Bu, hesaplanacak daha hızlı normalleştirme formudur, ancak daha fazla kod noktasıyla sonuçlanır (yani daha fazla alan kullanır).

Henüz normalize edilmemiş iki dizeyi karşılaştırmak istiyorsanız, uyumluluk normalleştirmesine ihtiyacınız olduğunu bilmediğiniz sürece bu tercih edilen normalleştirme biçimidir.

NFC

NFC, NFD algoritmasını çalıştırdıktan sonra mümkün olduğunda kod noktalarını yeniden birleştirir. Bu biraz daha uzun sürer, ancak daha kısa dizelerle sonuçlanır.

Uyumluluk Normalleştirme

Unicode, gerçekten ait olmayan ancak eski karakter kümelerinde kullanılan birçok karakteri de içerir. Unicode, bu karakter kümelerindeki metnin Unicode olarak işlenmesine ve ardından kayıpsız olarak geri dönüştürülmesine izin vermek için bunları ekledi.

Uyumluluk normalleştirme, bunları karşılık gelen "gerçek" karakter dizisine dönüştürür ve ayrıca kanonik normalleştirme gerçekleştirir. Uyumluluk normalizasyonunun sonuçları orijinallerle aynı görünmeyebilir.

Biçimlendirme bilgisi içeren karakterler, içermeyenlerle değiştirilir. Örneğin karakter dönüştürülür 9. Diğerleri biçimlendirme farklılıklarını içermez. Örneğin, roma rakamı karakteri normal harflere dönüştürülür IX.

Açıktır ki, bu dönüşüm gerçekleştirildikten sonra, orijinal karakter setine kayıpsız olarak geri dönmek artık mümkün değildir.

Ne zaman kullanılmalı

Unicode Konsorsiyumu, uyumluluk normalizasyonunu bir ToUpperCasedönüşüm gibi düşünmeyi önerir . Bu, bazı durumlarda faydalı olabilecek bir şeydir, ancak bunu ister istemez uygulamamalısınız.

Mükemmel bir kullanım senaryosu, muhtemelen 9eşleşecek bir arama isteyeceğiniz için bir arama motoru olacaktır .

Muhtemelen yapmamanız gereken bir şey, kullanıcıya uyumluluk normalleştirmesinin sonucunu göstermektir.

NFKC / NFKD

Uyumluluk normalleştirme formu, NFKD ve NFKC olmak üzere iki şekilde gelir. NFD ve C arasındaki ilişkiyle aynıdırlar.

NFKC'deki herhangi bir dize, doğası gereği NFC'dedir ve NFKD ve NFD için aynıdır. Böylece NFKD(x)=NFD(NFKC(x))ve NFKC(x)=NFC(NFKD(x))vb.

Sonuç

Şüpheniz varsa, kanonik normalleştirme ile gidin. Uygulanabilir alan / hız dengesine göre veya birlikte çalıştığınız bir şeyin gerektirdiklerine göre NFC veya NFD'yi seçin.


42
Kısaltmaların ne anlama geldiğini hatırlamak için hızlı bir referans: NF = normalleştirilmiş biçim D = ayrıştır (sıkıştırmayı kaldır) , C = oluştur (sıkıştır) K = uyumluluk ("C" alındığından beri).
Mike Spross

12
Her zaman ilk olarak girişteki tüm dizeleri NFD'yi ve en son şey olarak tüm dizeleri NFC'nin çıktısını almak istersiniz. Bu çok iyi bilinmektedir.
tchrist

3
@tchrist: Çıktının hiçbir değişiklik yapılmadığında girdiye özdeş bayt için bayt olmasını istediğiniz nadir durumlar dışında, bu genellikle iyi bir tavsiyedir. Bellekte NFC veya diskte NFD olmasını istediğiniz başka durumlar da vardır, ancak bunlar kuraldan ziyade istisnadır.
Kevin Cathcart

@Kevin: Evet, NFD girişi ve NFC çıkışı singletonları yok edecek. Bunları kimsenin umursadığından emin değilim, ama muhtemelen.
tchrist

2
Bunu düşünebilirsiniz, ancak ekten: "Bir Unicode dizesini belirli bir Unicode Normalleştirme Formuna dönüştürmek için ilk adım dizeyi tamamen ayrıştırmaktır". Bu nedenle, NFC'yi çalıştırdığımızda bile, Q-Caron ilk olarak Q + Caron olacaktı ve kararlılık kuralları yeni kompozisyon eşlemesinin eklenmesini yasakladığından yeniden oluşturamazdı. NFC, etkin bir şekilde NFC(x)=Recompose(NFD(x)).
Kevin Cathcart

40

Bazı karakterler, örneğin aksanlı bir harf (örneğin é) iki şekilde temsil edilebilir - tek bir kod noktası U+00E9veya düz harf ve ardından birleşik aksan işareti U+0065 U+0301. Sıradan normalleştirme, her zaman onu temsil etmesi için bunlardan birini seçecektir (NFC için tek kod noktası, NFD için birleştirme formu).

Birden fazla temel karakter dizisi ve birleştirme işaretleri ile temsil edilebilen karakterler için (örneğin, "s, alt nokta, üst nokta" yerine önce nokta sonra aşağı nokta koyma veya noktalardan birine sahip bir temel karakter kullanma), NFD ayrıca bunlardan birini seçin (aşağıda olduğu gibi önce)

Uyumluluk ayrıştırmaları, "gerçekten karakter olmaması gereken", ancak eski kodlamalarda kullanıldıkları için olan birkaç karakter içerir. Sıradan normalleştirme bunları birleştirmez (gidiş-dönüş bütünlüğünü korumak için - bu birleştirme formları için bir sorun değildir çünkü hiçbir eski kodlama [bir avuç Vietnamca kodlama dışında] her ikisini de kullanmaz), ancak uyumluluk normalizasyonu olacaktır. Bazı Doğu Asya kodlamalarında (veya yarı genişlik / tam genişlik katakana ve alfabesinde) görünen "kg" kilogram işareti veya MacRoman'daki "fi" ligatürü gibi düşünün.

Daha fazla ayrıntı için http://unicode.org/reports/tr15/ adresine bakın.


1
Bu gerçekten doğru cevap. Bazı eski karakter kümelerinden kaynaklanan metin üzerinde yalnızca kurallı normalleştirme kullanırsanız, sonuç kayıpsız olarak bu karakter kümesine geri dönüştürülebilir. Uyumluluk ayrıştırma kullanırsanız, herhangi bir uyumluluk karakteri kalmazsınız, ancak artık kayıpsız orijinal karakter kümesine geri dönüştürmek mümkün değildir.
Kevin Cathcart

13

Normal formlar (veritabanlarının değil, Unicode'un) öncelikli olarak (yalnızca?) Aksan işaretleri olan karakterlerle ilgilenir. Unicode, U + 00C0, "Yazılı Latin Büyük A" gibi "yerleşik" aksan işaretleri içeren bazı karakterler sağlar. Aynı karakter, "Birleştirilmiş Mezar Vurgusu" (U + 0300) ile bir "Latin Büyük A" (U + 0041) 'den oluşturulabilir. karşılaştırma onları tamamen farklı olarak gösterecektir.

Normalleştirme, bununla başa çıkma girişimidir. Normalleştirme, tüm karakterlerin aynı şekilde kodlanmasını sağlar (veya en azından dener) - ya tümü gerektiğinde ayrı bir aksan işareti kullanarak ya da mümkün olan yerlerde tek bir kod noktası kullanarak. Karşılaştırma açısından bakıldığında, sizin seçtiğiniz bir sürü önemli değil - hemen hemen her normalleştirilmiş dizge, başka bir normalleştirilmiş dizge ile doğru bir şekilde karşılaştırılacaktır.

Bu durumda, "uyumluluk", bir kod noktasının bir karaktere eşit olduğunu varsayan kodla uyumluluk anlamına gelir. Böyle bir kodunuz varsa, muhtemelen uyumluluk normal formunu kullanmak istersiniz. Doğrudan ifade edildiğini hiç görmemiş olsam da, normal formların isimleri, Unicode konsorsiyumunun ayrı ayrı aksan işaretleri kullanmanın tercih edildiğini düşündüğünü gösteriyor. Bu, bir dizedeki gerçek karakterleri saymak için daha fazla zeka gerektirir (ayrıca bir dizeyi akıllıca kırmak gibi şeyler), ancak daha çok yönlüdür.

Yoğun bakımdan tam olarak yararlanıyorsanız, standart normal formu kullanmak isteyebilirsiniz. Kendi başınıza (örneğin) bir kod noktasının bir karaktere eşit olduğunu varsayan bir kod yazmaya çalışıyorsanız, muhtemelen bunu olabildiğince sık gerçekleştiren uyumluluk normal formunu istersiniz.


İşte o zaman Grapheme Fonksiyonlarının geldiği kısım budur . Karakter ASCII'den daha fazla bayt olmakla kalmaz, aynı zamanda birden çok dizi tek bir karakter olabilir, değil mi? ( MB dizesi işlevlerinin aksine .)
Xeoncross

4
Hayır, 'bir kod noktası bir karakterdir' kabaca NFC'ye karşılık gelir (birleştirme işaretli olan NFD'dir ve hiçbiri "uyumluluk" değildir) - Uyumluluk normalleştirmeleri NFKC / NFKD farklı bir sorundur; örneğin yunanca mu ve 'mikro' için ayrı karakterlere sahip olan eski kodlamalar için uyumluluk (veya yokluğu) ("uyumluluk" sürümü Latin 1 bloğunda yer aldığı için ortaya çıkarması eğlenceli bir sürümdür)
Random832

@ Random832: Oops, çok doğru. Son bir veya iki yıldır onunla çalışmadığım halde, hafızamdan gitmem gerektiğini daha iyi bilmeliyim.
Jerry Coffin

@ Random832 Bu doğru değil. Senin "kabaca" çok dışarıda. Ō̲̃ ve ȭ̲ olmak üzere iki grafiği düşünün. Bunların her birini yazmanın birçok yolu vardır, bunların her biri tam olarak biri NFC ve biri NFD'dir, ancak diğerleri de mevcuttur. Durum, tek bir kod noktası değildir. İlki için NFD "o\x{332}\x{303}\x{304}"ve NFC'dir "\x{22D}\x{332}". An için NFD olduğunu "o\x{332}\x{304}\x{303}"ve NFC olduğunu "\x{14D}\x{332}\x{303}". Bununla birlikte, kanonik olarak bunlara eşdeğer olan birçok kanonik olmayan olasılık mevcuttur. Normalleştirme, kanonik olarak eşdeğer grafiklerin ikili karşılaştırmasına izin verir.
tchrist

5

İki unicode dizgisi kurallı olarak eşdeğer ise dizeler gerçekten aynıdır, yalnızca farklı unicode dizileri kullanır. Örneğin Ä, Ä karakteri veya A ve ◌̈ kombinasyonu kullanılarak temsil edilebilir.

Dizeler yalnızca uyumluluk eşdeğeri ise dizeler mutlaka aynı değildir, ancak bazı bağlamlarda aynı olabilirler. Örneğin ff, ff ile aynı kabul edilebilir.

Öyleyse, dizeleri karşılaştırıyorsanız, kanonik eşdeğerliği kullanmalısınız, çünkü uyumluluk eşdeğerliği gerçek eşdeğerlik değildir.

Ancak, bir dizi dizeyi sıralamak istiyorsanız, neredeyse aynı olduğu için uyumluluk eşdeğerliğini kullanmak mantıklı olabilir.


5

Bu aslında oldukça basit. UTF-8 aslında aynı "karakterin" birkaç farklı temsiline sahiptir. (Karakterleri bayt olarak farklı oldukları için tırnak içinde kullanıyorum, ama pratikte aynılar). Bağlantılı belgede bir örnek verilmiştir.

"Ç" karakteri, 0xc387 bayt dizisi olarak temsil edilebilir. Ancak bir C(0x43) ve ardından 0xcca7 bayt dizisi ile de temsil edilebilir. Yani 0xc387 ve 0x43cca7'nin aynı karakter olduğunu söyleyebilirsiniz. Çalışmasının nedeni, 0xcca7'nin birleşik bir işaret olmasıdır; yani karakteri ondan önce alır ( Cburada a) ve onu değiştirir.

Şimdi, kanonik denklik ile uyumluluk denkliği arasındaki farka gelince, genel olarak karakterlere bakmamız gerekiyor.

Değer aracılığıyla anlam aktaran ve başka bir karakteri alıp onu değiştiren 2 tür karakter vardır . 9 anlamlı bir karakterdir. Bir süper senaryo ⁹ bu anlamı alır ve sunumla değiştirir. Yani kanonik olarak farklı anlamları var ama yine de temel karakteri temsil ediyorlar.

Kanonik eşdeğerlik, bayt dizisinin aynı karakteri aynı anlamla oluşturduğu yerdir. Uyumluluk denkliği, bayt dizisinin aynı temel anlama sahip farklı bir karakteri oluşturmasıdır (değiştirilebilir olsa bile). 9 ve ⁹, her ikisi de "9" anlamına geldiğinden uyumluluk eşdeğeridir, ancak aynı gösterime sahip olmadıkları için kanonik olarak eşdeğer değildirler.


@tchrist: Cevabı tekrar okuyun. Aynı kod noktasını temsil etmenin farklı yollarından hiç bahsetmedim. Aynı basılı karakteri temsil etmenin birden fazla yolu olduğunu söyledim (birleştiriciler ve birden çok karakter aracılığıyla). Hem UTF-8 hem de Unicode için geçerlidir. Bu nedenle, olumsuz oyunuz ve yorumunuz söylediklerime gerçekten hiç uygulanmıyor. Aslında, temelde buradaki en üstteki posterin yaptığı noktaya
değindim

4

Kanonik eşdeğerliğin mi yoksa uyumluluk eşdeğerinin mi sizin için daha uygun olduğu, uygulamanıza bağlıdır. Dize karşılaştırmaları hakkındaki ASCII düşünme şekli kabaca kanonik eşdeğerlikle eşleşir, ancak Unicode birçok dili temsil eder. Unicode'un tüm dilleri tıpkı Batı Avrupa ASCII'si gibi davranmanıza izin verecek şekilde kodladığını varsaymanın güvenli olduğunu sanmıyorum.

Şekil 1 ve 2 , iki eşdeğerlik türünün iyi örneklerini sağlar. Uyumluluk denkliği altında, alt ve süper yazı biçimindeki aynı sayı eşit karşılaştırılacak gibi görünüyor. Ama el yazısı arapça veya döndürülmüş karakterlerle aynı sorunu çözdüğünden emin değilim.

Unicode metin işlemenin zor gerçeği, uygulamanızın metin işleme gereksinimleri hakkında derinlemesine düşünmeniz ve ardından bunları mevcut araçlarla elinizden geldiğince ele almanız gerektiğidir. Bu, doğrudan sorunuzu yanıtlamaz, ancak daha ayrıntılı bir yanıt, desteklemeyi beklediğiniz her dil için dil uzmanları gerektirir.


1

Dizeleri karşılaştırma sorunu : Çoğu uygulamanın amaçları için eşdeğer içeriğe sahip iki dizge farklı karakter dizileri içerebilir.

Unicode'un kurallı eşdeğerliğine bakın : karşılaştırma algoritması basitse (veya hızlı olması gerekiyorsa), Unicode eşdeğerliği gerçekleştirilmez. Bu sorun, örneğin, XML standart karşılaştırmasında ortaya çıkar, bkz. Http://www.w3.org/TR/xml-c14n

Bu sorunu önlemek için ... Hangi standart kullanılmalı? "genişletilmiş UTF8" mi yoksa "kompakt UTF8" mi?
"Ç" veya "c + ◌̧" kullanılsın mı?

W3C ve diğerleri (ör. Dosya adları ) "standart olarak oluşturulmuş" ("en kompakt" daha kısa dizelerin C'sini dikkate alın) kullanılmasını önerir ... Yani,

Standart C'dir ! şüphe durumunda NFC kullanın

Birlikte çalışabilirlik ve "yapılandırma üzerinde kural" seçenekleri için öneri, harici dizeleri "kanonize etmek" için NFC kullanılmasıdır . Kanonik XML depolamak için, örneğin "FORM_C" içinde saklayın. W3C'nin Web Çalışma Grubundaki CSV'si de NFC'yi tavsiye eder (bölüm 7.2).

Not: de "FORM_C" çoğu kitaplıkta varsayılan formdur . Ör. PHP'nin normalizer.isnormalized () dosyasında .


" Birleştirme formu" ( FORM_C) terimi, her ikisi için de "bir dizenin C-kanonik formda olduğunu" (bir NFC dönüşümünün sonucu) ve bir dönüştürme algoritmasının kullanıldığını söylemek için kullanılır ... Bkz. Http: //www.macchiato.com/unicode/nfc-faq

(...) aşağıdaki dizilerin her biri (ilk ikisi tek karakterli dizilerdir) aynı karakteri temsil eder:

  1. U + 00C5 (Å) LATİN BÜYÜK HARF A YUKARIDAKİ HALKALI
  2. U + 212B (Å) ANGSTROM İŞARETİ
  3. U + 0041 (A) LATİN BÜYÜK HARF A + U + 030A (̊) YUKARIDAKİ KOMBİNE HALKASI

Bu dizilere kanonik olarak eşdeğer denir. Bu formlardan ilki, C'nin kompostlama için olduğu Normalleştirme Formu C için NFC olarak adlandırılır . (...) S dizgisini NFC biçimine dönüştüren bir işlev olarak kısaltılabilirken toNFC(S), S'nin NFC'de olup olmadığını test eden işlev olarak kısaltılabilir isNFC(S).


Not: Küçük dizelerin (salt UTF-8 veya XML öğe referansları) normalizasyonunu test etmek için bu test / normalize çevrimiçi dönüştürücüyü kullanabilirsiniz .


Kafam karıştı. Bu çevrimiçi test sayfasına gittim ve oraya giriyorum: "TÖST MÉ pleasé." ve verilen normalleştirmelerin 4'ünü de deneyin - hiçbiri metnimi hiçbir şekilde değiştirmez, ancak bu karakterleri sunmak için kullanılan kodları değiştirmez. Yanlış bir şekilde "normalleştirme" nin "tüm aksan işaretlerini ve benzerlerini kaldır" anlamına geldiğini mi düşünüyorum ve bu aslında şu anlama geliyor - sadece aşağıdaki utf kodlamasını değiştirmek mi?
userfuser

Merhaba @userfuser, belki de uygulama hakkında bir pozisyona ihtiyacınız var: Metninizi karşılaştırmak mı yoksa standartlaştırmak mı? Buradaki yazım yalnızca uygulamaları "standartlaştırmak" hakkındadır. Not: tüm dünya standart kullandığında, karşılaştırma sorunu ortadan kalkar.
Peter Krauss
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.