java - remove - Elimina las marcas diacríticas(ń ň ň ň ň ň from from from from from from from ȵ ȵ ȵ ȵ ȵ ȵ) de los caracteres Unicode
remove special characters java (12)
Estoy mirando un algoritmo que puede mapear entre caracteres con signos diacríticos ( tilde , circumflex , caret , umlaut , caron ) y su carácter "simple".
Por ejemplo:
ń ǹ ň ñ ṅ ņ ṇ ṋ ṉ ̈ ɲ ƞ ᶇ ɳ ȵ --> n
á --> a
ä --> a
ấ --> a
ṏ --> o
Etc.
Quiero hacer esto en Java, aunque sospecho que debería ser algo Unicode-y y debería ser factible de forma razonable en cualquier idioma.
Propósito: permitir buscar fácilmente palabras con signos diacríticos. Por ejemplo, si tengo una base de datos de jugadores de tenis, y Björn_Borg está inscrito, también mantendré Bjorn_Borg para que pueda encontrarlo si alguien entra en Bjorn y no en Björn.
Algo a considerar: si sigue la ruta de tratar de obtener una sola "traducción" de cada palabra, puede perder algunas posibles alternativas.
Por ejemplo, en alemán, al reemplazar el "s-set", algunas personas podrían usar "B", mientras que otros podrían usar "ss". O bien, reemplazar un umlauted o con "o" o "oe". Cualquier solución que se te ocurra, idealmente, creo que debería incluir ambas.
El paquete core java.text fue diseñado para abordar este caso de uso (haciendo coincidir cadenas sin importar los signos diacríticos, mayúsculas y minúsculas, etc.).
Configure un Collator
para ordenar las diferencias PRIMARY
en los caracteres. Con eso, crea una CollationKey
para cada cadena. Si todo su código está en Java, puede usar CollationKey
directamente. Si necesita almacenar las claves en una base de datos u otro tipo de índice, puede convertirlo a una matriz de bytes .
Estas clases utilizan los datos de plegado de casos estándar de Unicode para determinar qué caracteres son equivalentes y admiten diversas estrategias de decomposition .
Collator c = Collator.getInstance();
c.setStrength(Collator.PRIMARY);
Map<CollationKey, String> dictionary = new TreeMap<CollationKey, String>();
dictionary.put(c.getCollationKey("Björn"), "Björn");
...
CollationKey query = c.getCollationKey("bjorn");
System.out.println(dictionary.get(query)); // --> "Björn"
Tenga en cuenta que los collaters son específicos de la configuración regional. Esto se debe a que el "orden alfabético" es diferente entre las configuraciones regionales (e incluso a lo largo del tiempo, como ha sido el caso con el español). La clase Collator
alivia de tener que seguir todas estas reglas y mantenerlas al día.
En Windows y .NET, simplemente convierto usando codificación de cadena. De esa manera evito el mapeo manual y la codificación.
Intenta jugar con la codificación de cadena.
En el caso de alemán, no se quiere eliminar diacríticos de Umlauts (ä, ö, ü). En su lugar, son reemplazados por una combinación de dos letras (ae, oe, ue) Por ejemplo, Björn debe escribirse como Bjoern (no Bjorn) para tener una pronunciación correcta.
Para eso, preferiría un mapeo codificado, donde puedes definir la regla de reemplazo individualmente para cada grupo de caracteres especial.
Es parte de Apache Commons Lang a partir de ver. 3.1.
org.apache.commons.lang3.StringUtils.stripAccents("Añ");
devuelve An
Hay un borrador del informe sobre el plegado de personajes en el sitio web Unicode que tiene mucho material relevante. Ver específicamente la Sección 4.1. "Algoritmo plegable".
Aquí hay una discusión e implementación de la eliminación del marcador diacrítico usando Perl.
Estas preguntas SO existentes están relacionadas:
La forma más fácil (para mí) sería simplemente mantener una matriz de asignación dispersa que simplemente cambie los puntos de código Unicode en cadenas visualizables.
Como:
start = 0x00C0
size = 23
mappings = {
"A","A","A","A","A","A","AE","C",
"E","E","E","E","I","I","I", "I",
"D","N","O","O","O","O","O"
}
start = 0x00D8
size = 6
mappings = {
"O","U","U","U","U","Y"
}
start = 0x00E0
size = 23
mappings = {
"a","a","a","a","a","a","ae","c",
"e","e","e","e","i","i","i", "i",
"d","n","o","o","o","o","o"
}
start = 0x00F8
size = 6
mappings = {
"o","u","u","u","u","y"
}
: : :
El uso de una matriz dispersa le permitirá representar sustitutos de manera eficiente incluso cuando estén en secciones ampliamente espaciadas de la tabla Unicode. Los reemplazos de cadena permitirán que secuencias arbitrarias reemplacen tus signos diacríticos (como el æ
grapheme que se convierte en ae
).
Esta es una respuesta independiente del idioma, por lo tanto, si tiene un lenguaje específico en mente, habrá mejores formas (aunque de todos modos, todos bajarán a esto en los niveles más bajos).
Lo he hecho recientemente en Java:
public static final Pattern DIACRITICS_AND_FRIENDS
= Pattern.compile("[//p{InCombiningDiacriticalMarks}//p{IsLm}//p{IsSk}]+");
private static String stripDiacritics(String str) {
str = Normalizer.normalize(str, Normalizer.Form.NFD);
str = DIACRITICS_AND_FRIENDS.matcher(str).replaceAll("");
return str;
}
Esto hará como usted especificó:
stripDiacritics("Björn") = Bjorn
pero fallará, por ejemplo, Białystok, porque el carácter ł
no es diacrítico.
Si desea tener un simplificador de cadenas completo, necesitará una segunda ronda de limpieza, para algunos personajes especiales que no son diacríticos. Es este mapa, he incluido los caracteres especiales más comunes que aparecen en los nombres de nuestros clientes. No es una lista completa, pero le dará la idea de cómo extenderla. ImmutableMap es solo una clase simple de google-collections.
public class StringSimplifier {
public static final char DEFAULT_REPLACE_CHAR = ''-'';
public static final String DEFAULT_REPLACE = String.valueOf(DEFAULT_REPLACE_CHAR);
private static final ImmutableMap<String, String> NONDIACRITICS = ImmutableMap.<String, String>builder()
//Remove crap strings with no sematics
.put(".", "")
.put("/"", "")
.put("''", "")
//Keep relevant characters as seperation
.put(" ", DEFAULT_REPLACE)
.put("]", DEFAULT_REPLACE)
.put("[", DEFAULT_REPLACE)
.put(")", DEFAULT_REPLACE)
.put("(", DEFAULT_REPLACE)
.put("=", DEFAULT_REPLACE)
.put("!", DEFAULT_REPLACE)
.put("/", DEFAULT_REPLACE)
.put("//", DEFAULT_REPLACE)
.put("&", DEFAULT_REPLACE)
.put(",", DEFAULT_REPLACE)
.put("?", DEFAULT_REPLACE)
.put("°", DEFAULT_REPLACE) //Remove ?? is diacritic?
.put("|", DEFAULT_REPLACE)
.put("<", DEFAULT_REPLACE)
.put(">", DEFAULT_REPLACE)
.put(";", DEFAULT_REPLACE)
.put(":", DEFAULT_REPLACE)
.put("_", DEFAULT_REPLACE)
.put("#", DEFAULT_REPLACE)
.put("~", DEFAULT_REPLACE)
.put("+", DEFAULT_REPLACE)
.put("*", DEFAULT_REPLACE)
//Replace non-diacritics as their equivalent characters
.put("/u0141", "l") // BiaLystock
.put("/u0142", "l") // Bialystock
.put("ß", "ss")
.put("æ", "ae")
.put("ø", "o")
.put("©", "c")
.put("/u00D0", "d") // All Ð ð from http://de.wikipedia.org/wiki/%C3%90
.put("/u00F0", "d")
.put("/u0110", "d")
.put("/u0111", "d")
.put("/u0189", "d")
.put("/u0256", "d")
.put("/u00DE", "th") // thorn Þ
.put("/u00FE", "th") // thorn þ
.build();
public static String simplifiedString(String orig) {
String str = orig;
if (str == null) {
return null;
}
str = stripDiacritics(str);
str = stripNonDiacritics(str);
if (str.length() == 0) {
// Ugly special case to work around non-existing empty strings
// in Oracle. Store original crapstring as simplified.
// It would return an empty string if Oracle could store it.
return orig;
}
return str.toLowerCase();
}
private static String stripNonDiacritics(String orig) {
StringBuffer ret = new StringBuffer();
String lastchar = null;
for (int i = 0; i < orig.length(); i++) {
String source = orig.substring(i, i + 1);
String replace = NONDIACRITICS.get(source);
String toReplace = replace == null ? String.valueOf(source) : replace;
if (DEFAULT_REPLACE.equals(lastchar) && DEFAULT_REPLACE.equals(toReplace)) {
toReplace = "";
} else {
lastchar = toReplace;
}
ret.append(toReplace);
}
if (ret.length() > 0 && DEFAULT_REPLACE_CHAR == ret.charAt(ret.length() - 1)) {
ret.deleteCharAt(ret.length() - 1);
}
return ret.toString();
}
/*
Special regular expression character ranges relevant for simplification -> see http://docstore.mik.ua/orelly/perl/prog3/ch05_04.htm
InCombiningDiacriticalMarks: special marks that are part of "normal" ä, ö, î etc..
IsSk: Symbol, Modifier see http://www.fileformat.info/info/unicode/category/Sk/list.htm
IsLm: Letter, Modifier see http://www.fileformat.info/info/unicode/category/Lm/list.htm
*/
public static final Pattern DIACRITICS_AND_FRIENDS
= Pattern.compile("[//p{InCombiningDiacriticalMarks}//p{IsLm}//p{IsSk}]+");
private static String stripDiacritics(String str) {
str = Normalizer.normalize(str, Normalizer.Form.NFD);
str = DIACRITICS_AND_FRIENDS.matcher(str).replaceAll("");
return str;
}
}
Para referencia futura, aquí hay un método de extensión C # que elimina acentos.
public static class StringExtensions
{
public static string RemoveDiacritics(this string str)
{
return new string(
str.Normalize(NormalizationForm.FormD)
.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) !=
UnicodeCategory.NonSpacingMark)
.ToArray());
}
}
static void Main()
{
var input = "ŃŅŇ ÀÁÂÃÄÅ ŢŤţť Ĥĥ àáâãäå ńņň";
var output = input.RemoveDiacritics();
Debug.Assert(output == "NNN AAAAAA TTtt Hh aaaaaa nnn");
}
Puedes usar la clase Normalizer de java.text
:
System.out.println(new String(Normalizer.normalize("ń ǹ ň ñ ṅ ņ ṇ ṋ", Normalizer.Form.NFKD).getBytes("ascii"), "ascii"));
Pero todavía hay algo de trabajo por hacer, ya que Java hace cosas extrañas con caracteres Unicode inconvendibles (no los ignora, y no arroja una excepción). Pero creo que podrías usar eso como punto de partida.
Tenga en cuenta que no todas estas marcas son solo "marcas" en algunos caracteres "normales", que puede eliminar sin cambiar el significado.
En sueco, å ä y ö son caracteres verdaderos y propios de primera clase, no una "variante" de algún otro personaje. Suenan diferentes a todos los demás personajes, clasifican diferente y hacen que las palabras cambien de significado ("mätt" y "mate" son dos palabras diferentes).
Unicode tiene caracteres diagráficos específicos (que son caracteres compuestos) y se puede convertir una cadena para separar el carácter y la diatria. Entonces, puedes eliminar los diatríticos de la cadena y básicamente has terminado.
Para obtener más información sobre normalización, descomposiciones y equivalencia, consulte El estándar Unicode en la página de inicio de Unicode .
Sin embargo, la forma en que realmente puede lograr esto depende del marco / OS / ... en el que está trabajando. Si usa .NET, puede usar el método String.Normalize que acepta la enumeración System.Text.NormalizationForm .