Regex: apa itu InCombiningDiacriticalMarks?

86

Kode berikut sangat terkenal untuk mengubah karakter beraksen menjadi Teks biasa:

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

Saya mengganti metode "buatan tangan" saya dengan yang ini, tetapi saya perlu memahami bagian "regex" dari replaceAll

1) Apa itu "InCombiningDiacriticalMarks"?
2) Dimanakah dokumentasinya? (dan sejenisnya?)

Terima kasih.

marcolopes
sumber
Lihat juga stackoverflow.com/a/29111105/32453 ternyata ada lebih banyak "tanda kombinasi" di unicode daripada hanya tanda diakritik, hanya sebagai catatan.
rogerdpack

Jawaban:

74

\p{InCombiningDiacriticalMarks}adalah properti blok Unicode. Di JDK7, Anda akan dapat menulisnya menggunakan notasi dua bagian \p{Block=CombiningDiacriticalMarks}, yang mungkin lebih jelas bagi pembaca. Ini didokumentasikan di sini di UAX # 44: "The Unicode Character Database" .

Artinya adalah bahwa titik kode berada dalam kisaran tertentu, blok, yang telah dialokasikan untuk digunakan untuk hal-hal dengan nama itu. Ini adalah pendekatan yang buruk, karena tidak ada jaminan bahwa titik kode dalam rentang itu adalah atau bukan hal tertentu, atau bahwa titik kode di luar blok itu pada dasarnya tidak memiliki karakter yang sama.

Misalnya, ada huruf latin di \p{Latin_1_Supplement}blok, seperti é, U + 00E9. Namun, ada juga hal-hal yang bukan huruf Latin di sana. Dan tentu saja ada juga huruf latin dimana-mana.

Pemblokiran hampir tidak pernah Anda inginkan.

Dalam kasus ini, saya curiga Anda mungkin ingin menggunakan properti \p{Mn}alias \p{Nonspacing_Mark}. Semua poin kode di blok Combining_Diacriticals adalah seperti itu. Ada juga (pada Unicode 6.0.0) 1087 Nonspacing_Marks yang tidak ada di blok itu.

Itu hampir sama dengan memeriksa \p{Bidi_Class=Nonspacing_Mark}, tetapi tidak sepenuhnya, karena grup itu juga menyertakan tanda yang melampirkan , \p{Me}. Jika Anda menginginkan keduanya, Anda dapat mengatakan [\p{Mn}\p{Me}]jika Anda menggunakan mesin regex Java default, karena ini hanya memberikan akses ke properti General_Category.

Anda harus menggunakan JNI untuk mendapatkan pustaka regex ICU C ++ seperti yang dilakukan Google untuk mengakses sesuatu seperti \p{BC=NSM}, karena saat ini hanya ICU dan Perl yang memberikan akses ke semua properti Unicode. Pustaka regex Java normal hanya mendukung beberapa properti Unicode standar. Di JDK7 meskipun akan ada dukungan untuk properti Skrip Unicode, yang hampir jauh lebih disukai daripada properti Blokir. Dengan demikian Anda dapat menulis di JDK7 \p{Script=Latin}atau \p{SC=Latin}, atau jalan pintas \p{Latin}, untuk mendapatkan karakter apa pun dari skrip Latin. Ini mengarah pada kebutuhan yang sangat umum [\p{Latin}\p{Common}\p{Inherited}].

Sadarilah bahwa itu tidak akan menghapus apa yang mungkin Anda anggap sebagai tanda "aksen" dari semua karakter! Ada banyak yang tidak akan melakukan ini. Misalnya, Anda tidak dapat mengubah Đ ke D atau ø ke o dengan cara itu. Untuk itu, Anda perlu mengurangi poin kode ke poin kode yang cocok dengan kekuatan pemeriksaan utama yang sama di Tabel Penyusunan Unicode.

Tempat lain di mana \p{Mn}hal itu gagal tentu saja menyertakan tanda seperti \p{Me}, jelas, tetapi juga ada \p{Diacritic}karakter yang bukan tanda. Sayangnya, Anda memerlukan dukungan properti penuh untuk itu, yang berarti JNI ke ICU atau Perl. Saya khawatir Java memiliki banyak masalah dengan dukungan Unicode.

Oh tunggu, saya melihat Anda orang Portugis. Anda seharusnya tidak memiliki masalah sama sekali jika Anda hanya berurusan dengan teks Portugis.

Namun, Anda tidak benar-benar ingin menghapus aksen, saya yakin, tetapi Anda ingin bisa mencocokkan hal-hal yang "tidak peka aksen", bukan? Jika demikian, maka Anda dapat melakukannya menggunakan kelas collator ICU4J (ICU untuk Java) . Jika Anda membandingkan kekuatan utamanya, tanda aksen tidak akan dihitung. Saya melakukan ini sepanjang waktu karena saya sering memproses teks bahasa Spanyol. Saya punya contoh bagaimana melakukan ini untuk bahasa Spanyol sambil duduk di sekitar sini jika Anda membutuhkannya.

tchrist
sumber
Jadi, saya harus berasumsi bahwa metode yang diberikan di seluruh web (dan bahkan di sini di SO) tidak direkomendasikan untuk kata "DeAccent". Saya membuat yang langsung hanya untuk bahasa Portugis, tetapi melihat pendekatan yang aneh ini (dan seperti yang Anda katakan, ini berhasil untuk tujuan saya, tetapi metode terakhir saya berhasil!). Jadi, adakah pendekatan yang "diterapkan dengan baik" yang lebih baik yang akan mencakup sebagian besar skenario? Contoh akan sangat bagus. Terima kasih atas waktunya.
marcolopes
1
@Marcolopes: Saya telah membiarkan data tetap utuh dan menggunakan Unicode Collation Algorithm untuk melakukan perbandingan kekuatan utama. Dengan cara itu, ia hanya membandingkan huruf, tetapi mengabaikan tanda huruf besar dan aksen. Ini juga memungkinkan hal-hal yang seharusnya menjadi huruf yang sama menjadi huruf yang sama, yang menghilangkan aksennya hanyalah perkiraan yang pucat dan tidak memuaskan. Selain itu, lebih bersih untuk tidak menghapus data jika Anda dapat mengerjakannya dengan cara yang Anda inginkan tetapi tidak memerlukannya.
tchrist
Jawaban yang cukup bagus, Namun satu pertanyaan, Dapatkah saya menggunakan Normalizer di java dan menggunakan InCombiningDiacriticalMarks tetapi mengecualikan beberapa karakter seperti ü dari konversi ke u?
AlexCon
6
ya, saya benar-benar memahami semua ini
Dónal
4

Butuh waktu beberapa saat, tapi aku memancing semuanya:

Berikut regex yang harus menyertakan semua karakter zalgo termasuk yang dilewati dalam rentang 'normal'.

([\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])

Semoga ini menghemat waktu Anda.

Matas Vaitkevicius
sumber