Regex: ¿qué es InCombiningDiacriticalMarks?

5 minutos de lectura

avatar de usuario
marcolopes

El siguiente código es muy conocido para convertir caracteres acentuados en texto sin formato:

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

Reemplacé mi método “hecho a mano” por este, pero necesito entender la parte “regex” de replaceAll

1) ¿Qué es “InCombiningDiacriticalMarks”?
2) ¿Dónde está la documentación de la misma? (¿y similares?)

Gracias.

  • Consulte también stackoverflow.com/a/29111105/32453 aparentemente hay más “marcas combinadas” en Unicode que solo las diacríticas, solo como una nota.

    – rogerdpack

    18/03/2015 a las 21:42

avatar de usuario
cristo

\p{InCombiningDiacriticalMarks} es una propiedad de bloque Unicode. En JDK7, podrá escribirlo usando la notación de dos partes \p{Block=CombiningDiacriticalMarks}, que puede ser más claro para el lector. esta documentado aquí en UAX#44: “La base de datos de caracteres Unicode”.

Lo que significa es que el punto de código se encuentra dentro de un rango particular, un bloque, que se ha asignado para usar con las cosas con ese nombre. Este es un mal enfoque, porque no hay garantía de que el punto de código en ese rango sea o no una cosa en particular, ni que los puntos de código fuera de ese bloque no sean esencialmente del mismo carácter.

Por ejemplo, hay letras latinas en el \p{Latin_1_Supplement} bloque, como é, U+00E9. Sin embargo, hay cosas que son no Letras latinas allí también. Y, por supuesto, también hay letras latinas por todas partes.

Los bloques casi nunca son lo que quieres.

En este caso, sospecho que es posible que desee utilizar la propiedad \p{Mn}alias \p{Nonspacing_Mark}. Todos los puntos de código en el bloque Combining_Diacriticals son de ese tipo. También hay (a partir de Unicode 6.0.0) 1087 Nonspacing_Marks que son no en ese bloque.

Eso es casi lo mismo que verificar \p{Bidi_Class=Nonspacing_Mark}pero no del todo, porque ese grupo también incluye las marcas de cierre, \p{Me}. Si quieres ambos, podrías decir [\p{Mn}\p{Me}] si está utilizando un motor de expresiones regulares de Java predeterminado, ya que solo da acceso a la propiedad General_Category.

Tendría que usar JNI para acceder a la biblioteca de expresiones regulares ICU C ++ de la forma en que lo hace Google para acceder a algo como \p{BC=NSM}porque ahora mismo solo ICU y Perl dan acceso a todos Propiedades Unicode. La biblioteca de expresiones regulares de Java admite solo un par de propiedades estándar de Unicode. En JDK7 aunque hay voluntad ser compatible con la propiedad Unicode Script, que es casi infinitamente preferible a la propiedad Block. Por lo tanto, puede escribir en JDK7 \p{Script=Latin} o \p{SC=Latin}o el atajo \p{Latin}, para llegar a cualquier carácter de la escritura latina. Esto lleva a la muy comúnmente necesario [\p{Latin}\p{Common}\p{Inherited}].

¡Tenga en cuenta que eso no eliminará lo que podría considerar marcas de “acento” de todos los caracteres! Hay muchos para los que no hará esto. Por ejemplo, no puede convertir DJ a D o ø a o de esa manera. Para ello, debe reducir los puntos de código a aquellos que coincidan con la misma intensidad de intercalación principal en la tabla de intercalación Unicode.

Otro lugar donde el \p{Mn} lo que falla es, por supuesto, encerrar marcas como \p{Me}obviamente, pero también hay \p{Diacritic} caracteres que no son marcas. Lamentablemente, necesita soporte completo de propiedad para eso, lo que significa JNI para ICU o Perl. Java tiene muchos problemas con la compatibilidad con Unicode, me temo.

Oh, espera, veo que eres portugués. Entonces no debería tener ningún problema si solo está tratando con texto en portugués.

Sin embargo, apuesto a que realmente no desea eliminar los acentos, sino que desea poder hacer coincidir las cosas “insensibles a los acentos”, ¿verdad? Si es así, puede hacerlo utilizando el Clase de intercalador ICU4J (ICU para Java). Si compara en la fuerza principal, los acentos no contarán. Hago esto todo el tiempo porque a menudo proceso texto en español. Tengo un ejemplo de cómo hacer esto para español sentado por aquí en alguna parte si lo necesitas.

  • Por lo tanto, debo suponer que el método proporcionado en la web (e incluso aquí en SO) no es el recomendado para “DeAccent” una palabra. Hice uno directo solo para portugués, pero vi este enfoque extraño (y como dijiste, funciona para mi propósito, ¡pero también lo hizo mi último método!). Entonces, ¿hay un mejor enfoque “bien implementado” que cubra la mayoría de los escenarios? Un ejemplo sería muy bueno. Gracias por tu tiempo.

    – marcolopes

    18 de abril de 2011 a las 4:11

  • @Marcolopes: he estado dejando los datos intactos y usando el algoritmo de intercalación Unicode para hacer comparaciones de fuerza primaria. De esa manera, solo compara letras, pero ignora tanto las mayúsculas como los acentos. También deja cosas que debería ser la misma letra ser la misma letra, a la que quitando los acentos no es más que una pálida e insatisfactoria aproximación. Además, es más limpio no borrar los datos si puede trabajar con ellos de una manera que haga lo que quiere pero no lo requiera.

    – cristo

    19 de abril de 2011 a las 1:06


  • Bastante buena respuesta, una pregunta, sin embargo, ¿puedo usar Normalizer en Java y usar InCombiningDiacriticalMarks pero excluir algunos caracteres como ü para que no se conviertan en u?

    – AlexCon

    24 de marzo de 2014 a las 15:18

  • sí, entendí perfectamente todo esto

    – Donal

    19/09/2014 a las 20:34

Me tomó un tiempo, pero los pesqué todos:

Aquí está la expresión regular eso debería incluir todos los caracteres zalgo, incluidos los omitidos en el rango ‘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])

Espero que esto te ahorre algo de tiempo.

¿Ha sido útil esta solución?