Limpiar acentos y otros diacríticos en .NET 2.0
Hazlo con Regular Expressions y Normalización UNICODE.

Fecha: 19/Feb/2005 (18/02/2005)
Autor: Germán Fernández Torres

 


En ciertas ocasiones un texto introducido por un usuario (que contendrá acentos, eñes, diéresis, cedillas etc.) debe ser pasado a un sistema/función/rutina que no soporta más que texto ASCII. Hay que limpiar acentos, la tilde de la ñ, acentos circunflejos (palabras francesas), diéresis (común en el alemán) etc.

Por ejemplo, algunos sistemas de reservas de vuelos sólo aceptan el nombre de los pasajeros sin ningún tipo de carácter regional y hay que buscarse la vida para guardar el nombre del cliente/pasajero, sin faltas de ortografia, en nuestra base de datos favorita y pasarle al sistema de reservas lo que quiere: caracteres soportados en el ASCII..

La solución que propongo consta de dos partes: Normalización o descomposición Unicode y una simple expresión regular para quitar todo menos el ASCII. El método está inspirado en un tutorial para hacer lo mismo en PERL. Lo único que puedo aportar es la adaptación de la filosofía al entorno .NET.

Bibliografía, inspiración y referencia
http://www.ahinea.com/en/tech/accented-translate.html
http://www.joelonsoftware.com/articles/Unicode.html

Normalización Unicode

No soy experto en UNICODE, pero leyendo varios artículos en Internet he encontrado que un cierto carácter Unicode puede ser representado de varias maneras. Por ejemplo, la 'á' minúscula y acentuada puede ser representada en Unicode de varias maneras:

Por una parte 'á' es el código UNICODE 0x00E1 (LATIN SMALL LETTER A WITH ACUTE). En C#:
char c = 'á';
char c2 = '\u00e1';
Debug.Assert(c == c2); //ok, el mismo valor
Debug.WriteLine(c); //muestra á en la consola... Debug.WriteLine(c2); //muestra á en la consola...

Pero 'á' también se puede representar en UNICODE de forma canónica. En forma canónica se representa un carácter para la letra base y otro para lo que es el acento: De esta manera 'á' queda como un array de dos códigos: 0x0061 y 0x0301 (LATIN SMALL LETTER A + COMBINING ACUTE ACCENT). En C#:

char[] arrChrs = new char[] {'\u0061', '\u0301'};
string strNormalizado = new String(arrChrs);
Debug.WriteLine(strNormalizado); //muestra á en la consola...
Debug.WriteLine(strNormalizado.Length); //... aunque este string contiene ¡¡2 caracteres!!

Normalización Unicode en .NET 2.0

Antes de .NET 2.0, que yo sepa, no existía un método "administrado" para hacer conversiones de texto a la forma canónica, aunque Windows si que expone esta funcionalidad en su API FoldString:

int FoldString(DWORD dwMapFlags, LPCTSTR lpSrcStr, int cchSrc, LPTSTR lpDestStr, int cchDest);

En esta API dwMapFlags se le pasa el tipo de normalización que queremos. En el caso del valor MAP_COMPOSITE el resultado que obtenemos es el siguiente según la ayuda de MSDN.

MAP_COMPOSITE
Mapea caracteres acentuados a caracteres compuestos, donde el caracter base y el acento se representan como dos caracteres.

En .NET 1.x podemos hacer uso de Interop para llamar a este método nativo, pero en .NET 2.0 parece que la gente Microsoft ha decidido incluir un envoltorio de este API directamente en la librería de clases base (BCL). En .NET 2.0 (en beta todavía), se ha expuesto este API de Windows como método Normalize de la clase string.

string String.Normalize(NormalizationForm normalization);

NormalizationForm es un tipo enumerado que representa el tipo de normalización que queremos. En nuestro caso, para descomponer una cadena a su representación canónica necesitamos pasarle el varlor NormalizationForm.FormD que equivale al valor MAP_COMPOSITE que vimos antes.

Manos a la obra.

Ahora que tenemos todos los ingredientes, vamos a convertir un texto con acentos/diéresis/diacríticos en texto sólo caracteres ASCII.

string textoOriginal = "Mañana será otro día";

//transformación UNICODE
string textoNormalizado = textoOriginal.Normalize(NormalizationForm.FormD);
//coincide todo lo que no sean letras y números ascii o espacio //y lo reemplazamos por una cadena vacía. Regex reg = new Regex("[^a-zA-Z0-9 ]"); string textoSinAcentos = reg.Replace(textoNormalizado, "");
Debug.WriteLine(textoSinAcentos); //muestra 'Manana sera otro dia'

 


Espacios de nombres usados en el código de este artículo:

System.Text;
System.Text.RegularExpressions.
.NET 2.0
Visual Studio 2005 (Whidbey)

 


ir al índice