Acentos y eñes no se muestran correctamente

Los ordenadores procesan texto utilizando tablas de codificación para convertir secuencias de bits en caracteres alfanuméricos y viceversa. Cuando desarrollamos aplicaciones, y muy especialmente aplicaciones distribuidas o aplicaciones web, tenemos que tener en cuenta la codificación (o enconding) con la que “ciframos” y “desciframos” los mensajes, dado que si utilizamos codificaciones distintas el mensaje resultante puede resultar incorrecto.

Antes de meternos de lleno en las consideraciones específicas de desarrollo, revisemos brevemente los encodings más comunes que puede ser interesante conocer.

ASCII

ASCII (American Standard Code for Information Interchange) se desarrolló en los años 60 y es una tabla de codificación que utiliza 7 bits para representar 128 caracteres, de los cuales 94 son caracteres legibles y 33 son caracteres de control y el espacio que se considera un carácter invisible. Esta codificación no sirve para representar la ‘ñ’ o los acentos, y hoy en día en muchos contextos se considera obsoleta.

Windows-1252 (Western European)

Históricamente era la codificación predeterminada en muchos contextos de Windows. Es una codificación de 8 bits para representar 256 caracteres, e incluye la mayoría de los caracteres utilizados en los alfabetos de Europa occidental.

ISO-8859-1 (Latin 1)

Esta codificación ISO de 8 bits fue publicada en 1985, y es un subconjunto de la codificación Windows-1252 compuesto por 191 caracteres. Es informalmente conocida como Latin 1.

ISO-8859-15 (Latin 9)

En 1999 se publicó esta codificación como una actualización de ISO-8859-1 para corregir algunas de sus limitaciones. Sigue utilizando 8 bits para representar cada carácter, y entre otros cambios se añadieron algunos caracteres del alfabeto fines (Š y Ž), y el símbolo del euro (€). Para poder acomodar estos cambios, se tuvieron que eliminar algunos otros caracteres de uso poco frecuente, entre los cuales se encontraban: ¤, ¦, ¨, ´, ¼, ½, y ¾.

UTF-8

UTF-8 (8-bit Unicode Transformation Format) es una codificación de longitud variable entre 1 y 4 bytes. La representación de un byte (8 bits) de UTF-8 se reserva exclusivamente a los 128 caracteres de la tabla ASCII lo cual lo hace compatible hacia atrás. Los siguientes 1920 caracteres de UTF-8 requieren dos bytes para ser codificados, entre los cuales se incluyen el alfabeto latino, griego, cirílico, hebreo, árabe, sirio, etc.

Por lo tanto, la moraleja de este breve resumen, es que aunque tanto Windows-1252 y UTF-8 sean capaces de representar el carácter ‘Ñ’, lo hacen de forma distinta. De hecho, para ser más precisos lo hacen así: 

Codificación

Glifo

Representación Binaria

Representación Hexadecimal

Windows-1252

Ñ

1101001

D1

UTF-8

Ñ

1100011 10010001

C3 91

Por lo tanto, queda claro que si codificamos un texto utilizando Windows-1252 y posteriormente lo decodificamos los bytes resultantes utilizando UTF-8, lo que inicialmente era un ‘Ñ’ va a pasar a ser cualquier otra cosa excepto una ‘Ñ’ al decodificarlo.

Ahora que ya tenemos más clara la problemática, os detallo algunas consideraciones concretas para ASP.NET:

1) Dado que ASP.NET nos permite configurar la codificación utilizada para generar las respuestas, y la codificación predeterminada para decodificar las peticiones, es importante asegurarse de que se utiliza la misma codificación en la otra parte implicada, el cliente. Esta es la configuración predeterminada de ASP.NET:

<configuration>
<system.web>
<globalization

      requestEncoding="utf-8"

      responseEncoding="utf-8" />
</system.web>
</configuration>

2) En una respuesta HTTP podemos especificar la codificación del contenido en dos sitios. En el encabezado HTTP Content-Type o en un meta tag dentro del propio documento HTML. En caso de conflicto entre ambos (como en el ejemplo), tiene preferencia la configuración especificada en el encabezado HTTP.

 

HTTP/1.1 200 OK

Date: Tue, 1 Dec 2009 10:23:34 GMT

Server: Microsoft-IIS/6.0

Content-Length: 48

Content-Type: text/html; charset=utf-8

Cache-control: private

<html xmlns="https://www.w3.org/1999/xhtml">

  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
</head>
<body>

</body>

</html>

Un problema frecuente es enviar respuestas HTTP sin especificar en ningún sitio la codificación utilizada. Cuando se da el caso, el cliente que recibe la respuesta tiene que determinar la codificación que utilizará para decodificar el contenido. Habitualmente, es tan sencillo como que el cliente utilizará la codificación predeterminada del sistema operativo, por lo que existe un elevado riesgo de que en algún cliente con una determinada configuración se muestre incorrectamente.

3) Otro problema con el que me he encontrado en varias ocasiones es relativo al dialogo de descarga de Internet Explorer. Cuando generamos una respuesta HTTP dinamicamente desde ASP.NET para enviar un fichero, podemos encontrarnos con que el dialogo de descarga muestra el nombre del fichero con caracteres incorrectos:

image

Este problema se debe a que ASP.NET (1.1 y 2.0) por defecto codifican los encabezados HTTP con UTF-8. En ASP.NET 1.1 es necesario instalar la siguiente actualización para poder especificar la codificación de los encabezados:

FIX: The response header will always be encoded as UTF-8 when you use the Response.Addheader method in ASP.NET

https://support.microsoft.com/default.aspx?scid=kb;EN-US;895262

Una vez instalada la actualización, podemos especificarla en el web.config de la aplicación:

<globalization responseHeaderEncoding="Windows-1252"/>

En ASP.NET 2.0 se puede cambiar la codificación de los encabezados HTTP programáticamente además de mediante la opción del web.config (igual que en ASP.NET 1.1):

protected void Page_Load(object sender, EventArgs e)

{

    //Leemos el fichero DOCX y lo almacenamos en un array de bytes

    string FileName = "Diseño gráfico.docx";

    string FilePath = MapPath(FileName);

    byte[] FileBytes = File.ReadAllBytes(FilePath);

   

    //ASP.NET codifica los encabezados HTTP con 'utf-8' de forma

    //predeterminada. Especificamos que utilice la codificación

    //ANSI predeterminada del OS, que en mi caso es 'Windows-1252'

    Response.HeaderEncoding = System.Text.Encoding.Default;   

    //Establecemos el MIME type de la respuesta y añadimos el

    //encabezado HTTP 'Content-Disposition'

    Response.ContentType = "application/docx; charset=utf-8";

    Response.AddHeader("Content-Disposition", "Inline; filename=" +

        FileName);

      

    //Envíamos el fichero en formato binario

    Response.BinaryWrite(FileBytes);

}

Hasta el próximo post

- Daniel Mossberg