11. Más sobre entrada y salida en C++
Curso: Introducción a C++, por Nacho Cabanes (antiguo)
11. Más sobre entrada y salida en C++
Hemos visto la base de la escritura en pantalla y de la lectura desde teclado empleando C++: cout se usa para escribir en pantalla y cin para leer desde teclado. Los distintos textos que debe escribir "cout" se preceden con "<<", y las entradas de "cin" se preceden con ">>".
Pero en ese manejo básico han quedado muchas "lagunas", cosas que sabíamos hacer en C pero que no hemos visto cómo hacer en C++. Por ejemplo, no sabemos cómo mostrar un número con una cierta cantidad prefijada de cifras decimales, ni cómo leer una cadena de texto que contenga espacios en blanco, ni cómo acceder a ficheros.
Vayamos por partes:
Hemos usado dos "flujos" (streams) de datos: un flujo de entrada de datos (cin, que corresponderá al teclado) y un flujo de salida de datos (cout, la pantalla). Pero también existe otro flujo estándar, que no hemos empleado todavía, pero que es interesante conocer: es "cerr", que corresponde a la salida de los mensajes de error. Esta suele ser la pantalla (de modo que normalmente coincidirá con "cout"), pero en algunos sistemas operativos (como los Unix, entre ellos Linux) se pueden redirigir los mensajes de error a un fichero, de modo que se pueda analizar los errores independientemente de la salida habitual del programa por pantalla.
Por ejemplo, si manejamos bajo Unix una utilidad llamada "prueba", podemos hacer las siguientes cosas:
- Si tecleamos "prueba", la utilidad se pondrá en funcionamiento, y mostrará la información "normal" en pantalla, y los mensajes de error también.
- Si tecleamos "prueba > salida", la utilidad se pondrá en funcionamiento, pero la información que normalmente aparecería en pantalla no lo hará, sino que será "redirigida" a un fichero de texto llamado "salida".
- Si tecleamos "prueba 2> errores", la utilidad se pondrá en funcionamiento, y mostrará la información "normal" en pantalla, pero los mensajes de error serán "redirigidos" a un fichero de texto llamado "errores", que nosotros podríamos analizar después.
Este mismo funcionamiento estándar se puede conseguir desde los programas que nosotros creemos si enviamos los mensajes de error al flujo llamado "cerr" (eso sí, insisto en que esta característica debe permitirla el sistema operativo: lo permiten todos los Unix -Linux, FreeBSD, AIX, SCO, etc- pero no lo permite MsDos ni Windows).
Sigamos. La salida en pantalla se puede personalizar mediante una serie de indicadores ("flags", en inglés) o modificadores. Por ejemplo, uno de estos indicadores nos sirve para decir que queremos que los números enteros se muestren en hexadecimal: si escribimos
cout << 90 << "\n";
cout.setf(ios::hex);
cout << 90 << "\n";
el resultado de estas líneas de programa sería:
90
5A
En este caso hemos utilizado el indicador llamado "hex". Todos estos modificadores deberemos precederlos, como se ve en el ejemplo, por "ios::" (porque pertenecen a una clase llamada "ios", que es de la que derivan las clases istream y ostream que estamos usando "sin darnos cuenta" con cin y cout).
Los indicadores que tenemos disponibles son:
- dec: salida decimal para enteros (defecto).
- oct: salida octal para enteros.
- hex: salida hexadecimal al para enteros.
- left: la salida se alinea a la izquierda.
- rigth: la salida se alinea a la derecha.
- internal: se alinea el signo y los caracteres indicativos de la base por la izquierda y las cifras por la derecha.
- showpos: se muestra el signo (+) en los valores positivos.
- scientific: notación científica para coma flotante.
- fixed: notación normal para coma flotante.
- skipws: se descartan los blancos iniciales a la entrada.
- showbase: se muestra la base de los valores numéricos.
- showpoint: se muestra el punto decimal.
- uppercase: los caracteres de formato aparecen en mayúsculas.
- unitbuf: salida sin buffer (se vuelca cada operación).
Cuando queremos fijar uno de ellos, se hace como ya hemos visto:
cout.setf(ios::hex);
Si queremos fijar varios de ellos a la vez, podemos enlazarlos con la barra vertical (|) que representa la operación OR (suma a nivel de bits):
cout.setf(ios::left | ios::oct);
Esto es porque realmente cada uno de los flags corresponde a un bit de un cierto número entero:
enum {
skipws=0x0001, left=0x0002, rigth=0x0004, internal=0x0008,
dec=0x0010, oct=0x0020 hex=0x0040, showbase=0x0080,
showpoint= 0x0100 uppercase=0x0200, showpos=0x0400, scientific=0x800,
fixed=0x1000, unitbuf=0x2000
};
Nota: el valor que devuelve la función "setf" es la configuración que tenían anteriormente estos indicadores (un número de tipo "long"), de modo que podemos guardar dicho valor en una variable auxiliar, para después dejar las opciones de salida en pantalla tal y como estaban:
long configAnterior;
configAnterior = cout.setf(ios::hex); // Pasamos a hexadecimal, guardando config.
cout << 215; // Escribimos lo que queramos
...
cout.setf(configAnterior); // Finalmente, dejamos config. antigua
Para desactivar un modificador concreto, se usa "unsetf" en lugar de "setf":
cout.unsetf(ios::hex);
También se puede usar la función "flags" para ver la configuración actual (o para fijarla toda "de golpe", de modo que resulta más arriesgada que usar "setf"):
configActual = cout.flags();
Algunos de estos indicadores se pueden usar también en forma de manipuladores. Es el caso de hex:
cout << hex << 90 << "\n";
mientras que otros manipuladores disponibles no tienen correspondencia con ningún "flag". Por ejemplo, existe un manipulador llamado "endl" que da un salto de línea, de modo que la línea anterior también se podría haber escrito así:
cout << hex << 90 << endl;
Los manipuladores más habituales son:
- dec, hex y oct: fijar la base en que se mostrarán los enteros (decimal, hexadecimal u octal).
- ws: saltar los espacios en blancos iniciales.
- endl: saltar de línea ("˜\n"™) y vacíar el buffer de salida.
- flush: se vacía el buffer de salida.
También tenemos varias funciones miembro interesantes:
- "width" permite fijar la anchura mínima para un dato de salida (si el dato ocupa más, se toma mayor anchura automáticamente), y devuelve la anchura que estaba anteriormente prefijada. Esta anchura sólo es válida para el siguiente dato que se imprima.
- "precision" permite indicar el número de cifras de los datos númericos (por defecto son 6 cifras)y devuelve el valor de precisión anterior.
- "fill" elige el carácter de relleno para un dato de salida (que normalmente será un espacio en blanco) y devuelve el carácter de relleno anterior.
Un ejemplo de estas 3 funciones miembro sería:
cout << 123.43 << endl;
cout.width(12);
cout.precision(4);
cout.fill('@');
cout << 123.43 << endl;
que mostraría lo siguiente en pantalla
123.43
@@@@@@@123.4
Actualizado el: 04-06-2006 21:49