Regex: cos'è InCombiningDiacriticalMarks?


86

Il codice seguente è molto noto per convertire i caratteri accentati in testo normale:

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

Ho sostituito il mio metodo "fatto a mano" con questo, ma ho bisogno di capire la parte "regex" di replaceAll

1) Che cos'è "InCombiningDiacriticalMarks"?
2) Dov'è la documentazione di esso? (e simili?)

Grazie.


Vedi anche stackoverflow.com/a/29111105/32453 apparentemente ci sono più "segni di combinazione" in Unicode che solo quelli diacritici, proprio come una nota.
rogerdpack

Risposte:


74

\p{InCombiningDiacriticalMarks}è una proprietà di blocco Unicode. In JDK7, sarai in grado di scriverlo usando la notazione in due parti \p{Block=CombiningDiacriticalMarks}, che potrebbe essere più chiara per il lettore. È documentato qui in UAX # 44: "The Unicode Character Database" .

Ciò che significa è che il punto di codice rientra in un intervallo particolare, un blocco, che è stato assegnato per essere utilizzato per le cose con quel nome. Questo è un cattivo approccio, perché non vi è alcuna garanzia che il punto di codice in quell'intervallo sia o non sia una cosa particolare, né che i punti di codice al di fuori di quel blocco non abbiano essenzialmente lo stesso carattere.

Ad esempio, ci sono lettere latine nel \p{Latin_1_Supplement}blocco, come é, U + 00E9. Tuttavia, ci sono anche cose che non sono lettere latine. E naturalmente ci sono anche lettere latine dappertutto.

I blocchi non sono quasi mai quello che vuoi.

In questo caso, sospetto che potresti voler utilizzare la proprietà \p{Mn}, alias \p{Nonspacing_Mark}. Tutti i punti di codice nel blocco Combining_Diacriticals sono di questo tipo. Ci sono anche (a partire da Unicode 6.0.0) 1087 Nonspacing_Marks che non sono in quel blocco.

Questa è quasi la stessa di controllare per \p{Bidi_Class=Nonspacing_Mark}, ma non del tutto, perché questo gruppo comprende anche i marchi di cinta, \p{Me}. Se vuoi entrambi, potresti dire [\p{Mn}\p{Me}]se stai utilizzando un motore regex Java predefinito, poiché dà accesso solo alla proprietà General_Category.

Dovresti usare JNI per accedere alla libreria regex C ++ ICU come fa Google per accedere a qualcosa di simile \p{BC=NSM}, perché al momento solo ICU e Perl danno accesso a tutte le proprietà Unicode. La normale libreria di espressioni regolari Java supporta solo un paio di proprietà Unicode standard. In JDK7 però ci sarà il supporto per la proprietà Unicode Script, che è quasi infinitamente preferibile alla proprietà Block. Quindi in JDK7 puoi scrivere \p{Script=Latin}o \p{SC=Latin}, o la scorciatoia \p{Latin}, per ottenere qualsiasi carattere dalla scrittura latina. Questo porta a ciò che è molto comunemente necessario [\p{Latin}\p{Common}\p{Inherited}].

Tieni presente che ciò non rimuoverà ciò che potresti considerare come segni di "accento" da tutti i caratteri! Ce ne sono molti per i quali non lo farà. Ad esempio, non è possibile convertire Đ in D o ø in o in questo modo. Per questo, è necessario ridurre i punti di codice a quelli che corrispondono alla stessa forza di confronto primaria nella tabella di confronto Unicode.

Un altro punto in cui la \p{Mn}cosa fallisce è ovviamente racchiudere segni come \p{Me}, ovviamente, ma ci sono anche \p{Diacritic}caratteri che non sono segni. Purtroppo, è necessario il supporto completo delle proprietà per questo, il che significa JNI per ICU o Perl. Java ha molti problemi con il supporto Unicode, temo.

Oh aspetta, vedo che sei portoghese. Allora non dovresti avere problemi se hai a che fare solo con testo portoghese.

Tuttavia, scommetto che non vuoi davvero rimuovere gli accenti, ma piuttosto vuoi essere in grado di abbinare le cose in modo "insensibile agli accenti", giusto? In tal caso, è possibile farlo utilizzando la classe di raccolta ICU4J (ICU per Java) . Se confronti la forza primaria, gli accenti non contano. Lo faccio sempre perché elaboro spesso il testo spagnolo. Ho un esempio di come farlo per lo spagnolo seduto qui da qualche parte se ne hai bisogno.


Quindi, devo presumere che il metodo fornito in tutto il web (e anche qui a SO) non è quello consigliato per "DeAccent" una parola. Ne ho fatto uno dritto solo per il portoghese, ma ho visto questo strano approccio (e come hai detto tu, funziona per il mio scopo, ma così ha fatto il mio ultimo metodo!). Quindi, esiste un approccio "ben implementato" migliore che coprirà la maggior parte degli scenari? Un esempio sarebbe molto carino. Grazie per il tuo tempo.
marcolopes

1
@ Marcolopes: ho lasciato intatti i dati e ho utilizzato l'algoritmo di confronto Unicode per fare confronti tra le forze primarie. In questo modo confronta solo le lettere, ma ignora sia le maiuscole che gli accenti. Permette anche alle cose che dovrebbero essere la stessa lettera di essere la stessa lettera, che rimuovere gli accenti è solo un'approssimazione pallida e insoddisfacente. Inoltre è più pulito non eliminare i dati se puoi lavorarci in un modo che fa quello che vuoi ma non lo richiede.
tchrist

Risposta abbastanza buona, Una domanda però, posso usare Normalizer in java e usare InCombiningDiacriticalMarks ma escludere alcuni caratteri come ü dalla conversione in u?
AlexCon

6
sì, ho capito perfettamente tutto questo
Dónal

4

Mi ci è voluto un po ', ma li ho ripescati tutti:

Ecco un'espressione regolare che dovrebbe includere tutti i caratteri zalgo compresi quelli bypassati nell'intervallo "normale".

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

Spero che questo ti faccia risparmiare tempo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.