35 Cadenas de caracteres
Windows trata las cadenas de caracteres de un modo algo distinto a como lo hacen las funciones estándar C. Esto se debe a que Windows maneja varios conjuntos de caracteres: ANSI, que son los que ya conocemos, como caracteres de ocho bits y Unicode, que son de dos bytes.
También puede manejar, diferentes formas de comparar y ordenar cadenas, diferentes configuraciones de idioma, que afectan a la forma de representar mayúsculas y minúsculas, o de comparar caracteres, etc. Por ejemplo, en español, la letra 'ñ' es mayor que la 'n' y menor que la 'o'. En inglés ni siquiera existe esa letra. Otro ejemplo, si intentamos obtener la mayúscula de la letra 'ñ' usando funciones estándar, el resultado no será la 'Ñ'.
Recursos de cadenas
Al igual que podemos crear recursos para mapas de bits, menús, iconos, etc, también podemos crearlos para almacenar cadenas y leerlas desde la aplicación. Esto tiene varias ventajas y aplicaciones.
Los recursos de una aplicación pueden ser modificados por un editor adecuado sin modificar la parte ejecutable de una aplicación. Esto permite traducir una aplicación a distintos idiomas sin tener que compilar la aplicación ni tener que compartir el código fuente.
Es más, podemos crear nuestras aplicaciones para que sean multilenguaje, de modo que usen las cadenas adecuadas según la configuración de la aplicación.
Fichero de recursos
Lo primero que debemos crear es una tabla de cadenas (stringtable) dentro del fichero de recursos, esto se hace mediante la sentencia STRINGTABLE:
STRINGTABLE BEGIN ID_TITULO, "Título de la aplicación" ID_SALUDO, "Hola, estoy preparado para empezar." ID_DESPEDIDA, "Gracias por usar esta aplicación." END
Como se ve, una tabla de cadenas empieza con la sentencia STRINGTABLE y a continuación, entre un bloque BEGIN-END una lista de identificadores y cadenas entre comillas, separadas con una coma. En este caso, como suele ser nuestra costumbre, los identificadores son etiquetas definidas en nuestro fichero de cabecera de identificadores, aunque podría tratarse de números enteros de 16 bits.
El objetivo es hacer nuestra aplicación tan independiente del idioma como sea posible. Esto significa que en la aplicación no deberían aparecer cadenas literales, sino que deben usar cadenas de recurso. De este modo, sólo con traducir las cadenas del fichero de recursos, todos los literales usados en la aplicación cambiarán de idioma. Esto evita tener que repasar todos los ficheros fuente buscando literales para sustituirlos, y tener que compilar la aplicación de nuevo.
Cargar cadenas desde recursos
Para obtener una cadena desde un recurso se usa la función LoadString. Esta función necesita cuatro parámetros. El primero es un manipulador de instancia, generalmente a la misma instancia de nuestra aplicación, aunque como veremos en capítulos más avanzados, también podemos obtener cadenas desde otros módulos, o desde DLLs. El segundo parámetro es el identificador de la cadena, el tercero el búfer donde se recibe la cadena leída, y el cuarto el tamaño de dicho búfer.
Por ejemplo:
static HINSTANCE hInstance; char mensaje[64]; char titulo[64]; ... case WM_CREATE: hInstance = ((LPCREATESTRUCT)lParam)->hInstance; LoadString(hInstance, ID_TITULO, titulo, 64); LoadString(hInstance, ID_SALUDO, mensaje, 64); MessageBox(hwnd, mensaje, titulo, MB_OK); break;
Funciones para cadenas
Algunas funciones estándar C tienen una versión repetida en el API de Windows. En el caso de las siguientes funciones, están mejoradas para manipular cadenas Unicode:
Por ejemplo, la versión Windows de strlen, lstrlen siempre calcula la longitud de una cadena en caracteres, independientemente de si los caracteres son de uno o dos bytes.
// Comparar cadenas: if(lstrcmp("Niño", "Ñape") < 0) lstrcpy(mensaje, "Niño es menor que Ñape"); else lstrcpy(mensaje, "Niño es mayor que Ñape"); TextOut(hdc, 10, 280, mensaje, strlen(mensaje)); if(lstrcmp("Ola", "Ñape") < 0) lstrcpy(mensaje, "Ola es menor que Ñape"); else lstrcpy(mensaje, "Ola es mayor que Ñape"); TextOut(hdc, 10, 300, mensaje, strlen(mensaje));
La salida de este fragmento de programa es la que cabría esperar:
Niño es menor que Ñape Ola es mayor que Ñape
Es decir, la función considera que la letra 'Ñ' está entre la 'N' y la 'O', como realmente ocurre en español. Siempre y cuando nuestro Windows esté instalado en español, o el usuario haya seleccionado ese idioma, claro.
Otros casos donde es necesario crear nuevas funciones es cuando necesitamos operar con caracteres dentro de cadenas. Como en Windows los caracteres pueden ser de uno o dos bytes, moverse a lo largo de una cadena puede no ser siempre tan directo como usando cadenas C estándar. Disponemos de dos funciones para movernos dentro de cadenas: CharNext para avanzar al siguiente carácter de una cadena, y CharPrev para retroceder al anterior.
Algo parecido pasa con la conversión de mayúsculas a minúsculas, y viceversa. En este caso disponemos de cuatro funciones:
Función | Descripción |
---|---|
CharLower | Convertir un carácter o una cadena a minúsculas. |
CharLowerBuff | Convertir un carácter o una cadena a minúsculas. |
CharUpper | Convertir un carácter o una cadena a mayúsculas. |
CharUpperBuff | Convertir un carácter o una cadena a mayúsculas. |
static char alfabeto[] = "abcdefghijklmnñopqrstuvwxyz áéíóúëü ç"; ... // Mayúsculas y minúsculas CharLower(alfabeto); TextOut(hdc, 10, 220, alfabeto, strlen(alfabeto)); CharUpper(alfabeto); TextOut(hdc, 10, 240, alfabeto, strlen(alfabeto));
También en este caso el resultado es el esperado, las letras se convierten a mayúscula y minúscula correctamente, aunque se trate de caracteres con acentos, diéresis o tildes:
abcdefghijklmnñopqrstuvwxyz áéíóúëü ç ABCDEFGHIJKLMNÑOPQRSTUVWXYZ ÁÉÍÓÚËÜ Ç
Otro grupo de funciones estándar que se ven afectadas por el modo de trabajar en Windows son las del grupo de "ctype". En este caso tenemos otras cuatro funciones:
Función | Descripción |
---|---|
IsCharAlpha | Verificar si un carácter es alfabético. |
IsCharAlphaNumeric | Verificar si un carácter es alfanumérico. |
IsCharLower | Verifica si un carácter está en minúscula. |
IsCharUpper | Verifica si un carácter está en mayúscula. |
Por último, tenemos un par de funciones que sustituyen a las funciones estándar sprintf y vsprintf. Se trata de wsprintf y wvsprintf.
for(i = 0; i < 10; i++) { wsprintf(mensaje, "Cadena formateada %d: valor %c", i+1, alfabeto[i]); TextOut(hdc, 10, i*20, mensaje, strlen(mensaje)); }
En Windows debemos usar las variantes del API, ya que están preparadas para trabajar con cadenas y caracteres Unicode, algo que las funciones estándar no pueden hacer.
Hay que tener en cuenta que estas funciones no aceptan las mismas cadenas de formato que sprintf o vsprintf, por ejemplo, no sirven para valores en punto flotante o punteros. Sin embargo, tienen más opciones para cadenas Unicode/ANSI.
Ejemplo 40
Nombre | Fichero | Fecha | Tamaño | Contador | Descarga |
---|---|---|---|---|---|
Ejemplo 40 | win040.zip | 2004-07-16 | 3318 bytes | 693 |