Regex: InCombiningDiacriticalMarks nedir?


86

Aşağıdaki kodun aksanlı karakterleri düz Metne dönüştürdüğü çok iyi bilinmektedir:

Normalizer.normalize(text, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");

"El yapımı" yöntemimi bununla değiştirdim, ancak replaceAll'ın "regex" bölümünü anlamam gerekiyor

1) "InCombiningDiacriticalMarks" nedir?
2) Dokümantasyonu nerede? (ve benzerleri?)

Teşekkürler.


Ayrıca bkz. Stackoverflow.com/a/29111105/32453, görünüşe göre unicode'da sadece aksan işaretlerinden daha fazla "birleştirme işaretleri" var, tıpkı bir not olarak.
rogerdpack

Yanıtlar:


74

\p{InCombiningDiacriticalMarks}bir Unicode blok özelliğidir. JDK7'de, \p{Block=CombiningDiacriticalMarks}okuyucu için daha açık olabilecek iki bölümlü notasyonu kullanarak yazabileceksiniz . Burada UAX # 44: "Unicode Karakter Veritabanı" nda belgelenmiştir .

Bunun anlamı, kod noktasının belirli bir aralığa, o ada sahip şeyler için kullanılmak üzere tahsis edilmiş bir blok içinde yer almasıdır. Bu kötü bir yaklaşımdır, çünkü bu aralıktaki kod noktasının belirli bir şey olup olmadığına ya da bu bloğun dışındaki kod noktalarının esasen aynı karakterde olmadığına dair hiçbir garanti yoktur.

Örneğin, \p{Latin_1_Supplement}blokta é, U + 00E9 gibi Latin harfleri vardır . Ancak orada Latin harfleri olmayan şeyler de var. Ve tabii ki her yerde Latin harfleri de var.

Bloklar neredeyse asla istediğiniz gibi değildir.

Bu durumda, sana özelliğini kullanmak isteyebilirsiniz şüpheli \p{Mn}aka \p{Nonspacing_Mark}. Combining_Diacriticals bloğundaki tüm kod noktaları bu türdendir. Ayrıca (Unicode 6.0.0 itibariyle) 1087 Nonspacing_Marks bu blokta yer almıyor .

Bu neredeyse kontrol aynıdır \p{Bidi_Class=Nonspacing_Mark}, ama tamamen değil, bu grup da kapsayan işaretleri içerdiğinden, \p{Me}. Her ikisini de istiyorsanız, [\p{Mn}\p{Me}]yalnızca General_Category özelliğine erişim sağladığı için varsayılan bir Java normal ifade motoru kullanıp kullanmadığınızı söyleyebilirsiniz .

JNI'yi, ICU C ++ regex kitaplığına Google'ın yaptığı gibi bir şeye erişmek için kullanmak zorunda kalacaksınız \p{BC=NSM}, çünkü şu anda yalnızca ICU ve Perl tüm Unicode özelliklerine erişim sağlıyor . Normal Java regex kitaplığı yalnızca birkaç standart Unicode özelliğini destekler. JDK7'de Unicode Script özelliği için destek olacak , ki bu Block özelliğine neredeyse sonsuz derecede tercih edilebilir. Böylece JDK7 yazma içinde olabilir \p{Script=Latin}ya da \p{SC=Latin}, ya da kısa kesilmiş \p{Latin}, Latin harflerinin herhangi karakterde olsun. Bu, çok ihtiyaç duyulan şeylere yol açar [\p{Latin}\p{Common}\p{Inherited}].

Bunun "aksan" işaretleri olarak düşünebileceğiniz şeyleri tüm karakterlerden kaldırmayacağının farkında olun! Bunun için yapmayacağı çok kişi var. Örneğin, dönüştürmek olamaz DJ için D veya ø için o şekilde söyledi. Bunun için, kod noktalarını Unicode Harmanlama Tablosundaki aynı birincil harmanlama gücü ile eşleşenlere indirmeniz gerekir.

Bir \p{Mn}şeyin başarısız olduğu başka bir yer \p{Me}, tabii ki, gibi işaretleri içine almaktır, ama aynı zamanda \p{Diacritic}işaret olmayan karakterler de vardır . Ne yazık ki, bunun için tam mülk desteğine ihtiyacınız var, bu da JNI'yi ICU veya Perl'e ifade ediyor. Java'nın Unicode desteğiyle ilgili pek çok sorunu var, korkarım.

Oh bekle, Portekizlisin görüyorum. Sadece Portekizce metinle uğraşıyorsanız hiçbir sorun yaşamazsınız.

Bununla birlikte, bahse girerim, aksanları gerçekten kaldırmak istemezsiniz, bunun yerine şeyleri “aksan-duyarsız” olarak eşleştirebilmeyi istersiniz, değil mi? Öyleyse, bunu ICU4J (Java için ICU) harmanlayıcı sınıfını kullanarak yapabilirsiniz . Birincil güçte karşılaştırırsanız, aksan işaretleri sayılmaz. Bunu her zaman yapıyorum çünkü İspanyolca metinleri sıklıkla işliyorum. İhtiyacın olursa buralarda bir yerde oturan İspanyolca için bunu nasıl yapacağına dair bir örneğim var.


Bu yüzden, web'de (ve burada SO'da bile) verilen yöntemin bir kelime "DeAccent" için önerilen yöntem olmadığını varsaymalıyım. Sadece Portekizce için düz bir yaklaşım yaptım, ama bu garip yaklaşımı gördüm (ve dediğin gibi, amacım için çalışıyor, ama son yöntemim de öyle! Öyleyse, çoğu senaryoyu kapsayacak daha iyi bir "iyi uygulanmış" yaklaşım var mı? Bir örnek çok güzel olurdu. Zaman ayırdığınız için teşekkürler.
marcolopes

1
@Marcolopes: Verileri olduğu gibi bırakıyorum ve birincil güç karşılaştırmaları yapmak için Unicode Harmanlama Algoritmasını kullanıyorum. Bu şekilde sadece harfleri karşılaştırır, ancak hem büyük / küçük harf hem de aksan işaretlerini göz ardı eder. Ayrıca , aynı harf olması gereken şeylerin aynı harf olmasına izin verir ; aksanları kaldırmak sadece soluk ve yetersiz bir yaklaşımdır. Ayrıca, verilerle istediğinizi yapacak, ancak bunu gerektirmeyecek şekilde çalışabiliyorsanız, verileri değiştirmemek daha temizdir.
tchrist

Oldukça iyi cevap, Yine de bir soru: Normalizer'ı java'da ve InCombiningDiacriticalMarks'ı kullanabilir miyim, ancak ü gibi bazı karakterleri u'ya dönüştürmekten hariç tutabilir miyim?
AlexCon

6
evet, tüm bunları tamamen anladım
Dónal

4

Biraz zaman aldı ama hepsini avladım:

İşte 'normal' aralıkta atlananlar dahil tüm zalgo karakterlerini içermesi gereken normal ifade.

([\u0300–\u036F\u1AB0–\u1AFF\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F\u0483-\u0486\u05C7\u0610-\u061A\u0656-\u065F\u0670\u06D6-\u06ED\u0711\u0730-\u073F\u0743-\u074A\u0F18-\u0F19\u0F35\u0F37\u0F72-\u0F73\u0F7A-\u0F81\u0F84\u0e00-\u0eff\uFC5E-\uFC62])

Umarım bu size biraz zaman kazandırır.

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.