Acentos y eñes en programas de consola de Windows

Letra Ñ

Cuando creamos programas para consola de Windows hay algo que resulta muy frustrante: las eñes, cedillas y acentos no se muestran como debieran, y en su lugar se ven unos caracteres raros que dificultan la lectura de los textos y resultan muy antiestéticos.

Ante este problema se nos ocurren algunas soluciones, más o menos ingeniosas:

La primera, prescindir de esos caracteres: eliminamos los acentos y evitamos palabras con 'ñ'. Esto no siempre es posible, y como solución, sinceramente, resulta un intento bastante mediocre.

La segunda consiste en editar el código fuente desde un editor de consola. Haciendo esto, el editor usa el mismo conjunto de caracteres que el fichero ejecutable, y la salida del programa coincide con lo que hemos escrito antes. El resultado es mejor, pero resulta bastante incómodo usar varios editores para cada proyecto.

La tercera es incluso más incómoda. Consiste en escribir las cadenas en una consola, y a continuación cortar y pegar el texto en el editor. Y lo peor, a veces este truco ni siquiera funciona.

Pero, ¿por qué pasa esto?

Tal vez nos resultaría más sencillo solucionar el problema si supiéramos exactamente por qué se produce.

La raíz del problema es que Windows usa un código de caracteres (o código de página) diferente para el entorno GUI y para la consola. Para configuraciones de Windows en español, en GUI se usa el código 1252 y para la consola se usa el código 850.

Si usamos un editor de textos GUI para escribir el código de un programa para consola, se usarán códigos de página diferentes en la edición y en la ejecución, y el resultado será el galimatías al que ya estamos acostumbrados.

Soluciones más interesantes

Conociendo el origen del problema es más sencillo encontrar una solución, o al menos, debería serlo.

Una primera idea sería configurar el editor para que use el código de página 850. Desgraciadamente esto no es siempre posible. En las opciones del editor de Code::Blocks, por ejemplo, no está disponible el código de página 850.

Eso nos deja la solución final, que consiste en usar un par de funciones del API de Windows.

Primero, usaremos la función SetConsoleOutputCP. Que, como su propio nombre indica, sirve para asignar un código de página a la salida de consola. Usaremos el valor 1252, que es el código de página que queremos usar, ya que es el que hemos usado para escribir el código fuente.

#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
    SetConsoleOutputCP(1252);
    cout << "áéíóúñÑçÇüÜ" << endl;
    return 0;
}

Segundo, debemos asegurarnos de que nuestra consola usa fuentes Unicode, de otro modo, el código de página asignado será ignorado y no habremos conseguido nada. En la página de Microsoft hay un artículo titulado " SetConsoleOutputCP sólo es efectivo con fuentes Unicode":

Hacer esto es sencillo, basta seguir estos pasos:

  1. Abrir una consola Windows.
  2. Desplegar el menú del sistema (en el icono de la parte superior izquierda de la ventana) y seleccionar "Predeterminados".
  3. Activar la pestaña "Fuentes".
  4. Seleccionar una fuente TrueType, si está disponible, en lugar de "Fuentes de mapa de bits". (Yo tengo "Lucida console".)

De este modo, la configuración será la usada para todas las consolas a partir de este momento.

Si sólo están disponibles fuentes de mapas de bits, mucho me temo que esta solución no es viable.

Efectos colaterales

Si nuestro programa de consola tiene que leer cadenas nos encontraremos con otro problema inesperado. Las cadenas que introduzcamos por teclado, si contienen acentos o eñes se verán correctamente, pero si posteriormente las volvemos a mostrar en pantalla veremos que vuelven a aparecer caracteres extraños.

El motivo es simple, el código de página para consola de entrada y salida no tiene por qué ser el mismo. La función SetConsoleOutputCP sólo cambia el código de página para la salida, pero el de entrada sigue siendo el 850. Así, cuando introducimos una cadena, esta se codifica incorrectamente.

La función para cambiar el código de página de entrada no es, como podría esperarse, SetConsoleInputCP. Esa función no existe (a saber por qué), la función correcta es SetConsoleCP.

#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
    char cad[256];

    SetConsoleOutputCP(1252);
    SetConsoleCP(1252);
    cout << "áéíóúñÑçÇçüÜ" << endl;
    cout << "Introduce una cadena con acentos y eñes: ";
    cin >> cad;
    cout << "La cadena es: " << cad;
    return 0;
}

No es una solución ideal, lo sé, pero puede solucionar muchos problemas cuando programamos para consola.

Otra ventaja es que las cadenas leídas de este modo se codifican internamente usando el código de página de Windows GUI, de modo que es una forma de que cualquier dato leído de este modo se visualice correctamente en una aplicación GUI.