Programación Orientada a Objetos 7 – Entrada/salida en C++ -1- 7 – Entrada y salida en C++ Introducción............................................................................................................................................................1 Streams....................................................................................................................................................................2 La biblioteca iostream.............................................................................................................................................3 Flujos predefinidos ..............................................................................................................................................3 Estructura de la biblioteca iostream ...................................................................................................................4 Operadores de inserción y de extracción ...............................................................................................................5 El operador de inserción <<...............................................................................................................................5 Funciones de salida de ostream: put y write .......................................................................................................6 El operador de extracción >>.............................................................................................................................7 Función get ..........................................................................................................................................................8 Función getline ..................................................................................................................................................10 Función ignore...................................................................................................................................................10 Función read......................................................................................................................................................11 Función putback ................................................................................................................................................12 Función peek......................................................................................................................................................12 Entrada/salida con formato..................................................................................................................................14 Clase ios.............................................................................................................................................................14 Ajustar la anchura de los campos: width ......................................................................................................14 Precisión: precision.......................................................................................................................................15 Relleno: fill ....................................................................................................................................................16 Indicadores de formato..................................................................................................................................18 Manipuladores...................................................................................................................................................22 Ejercicios resueltos ...............................................................................................................................................24 Introducción Todos los programadores que llegan al C++ desde C están acostumbrados a utilizar las instrucciones de entrada/salida definidas en el archivo cabecera stdio.h (printf, fprintf, fputs, puts, fwrite...). Esta biblioteca también está presente en C++, pero no parece ser la adecuada en un lenguaje como C++ en el que los nuevos tipos tienen un comportamiento completamente equivalente al de los tipos de datos estándar. El mecanismo de clases de C++ permite crear un sistema consistente y ampliable para los mecanismos de entrada/salida. Este sistema se conoce como biblioteca de flujos1. Estas clases sirven como mecanismo para manejar los tipos básicos, pero, además, se puede modificar ampliándolas para incorporar los tipos de datos definidos por el usuario, como ya se vio en capítulos anteriores cuando se trató la sobrecarga de los flujos de entrada/salida en C++. La eficiencia de las funciones de la biblioteca de C++ es otro punto a su favor, sobretodo si se las compara con las de la biblioteca C: El tipo del objeto es conocido en C++ de forma estática por el compilador, en lugar de tener que comprobar de forma dinámica los campos % como sucedía en C. Con C++ se tiene más rapidez. Por ejemplo printf utilizada en C, es básicamente un intérprete de un pequeño lenguaje constituido principalmente por campos %. 1 Flujo es la traducción de stream. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ -2- C++ ofrece un mecanismo extensible para los nuevos tipos de datos definidos por el usuario. En lenguaje C sería caótico si todo el mundo se pusiera a añadir de forma simultánea nuevos campos % incompatibles. En C++ los tipos definidos por el usuario (clases) se parecen y actúan como los tipos básicos. El sistema de entrada/salida de C++ es un sistema de clases. Esto significa que un usuario puede definir “algo nuevo” que parezca y se comporte como streams. Las sentencias de entrada/salida en C++ tienen mayor legibilidad que las de C. Streams El concepto de stream o flujo no es nuevo en C++. ANSI C utiliza este concepto para indicar un tipo de puerto abstracto a través del cual datos no estructurados pueden caminar de forma unidireccional o bidireccional2. Los flujos en ANSI C se consideran una construcción de entrada/salida de bajo nivel. Por su parte C++ fue diseñado para el manejo de clases, así los flujos de C++ se encuentran definidos mediante una jerarquía de clases completa que se distribuye con el compilador. Las versiones antiguas3 de C++ se distribuían con una jerarquía de clases de streams que actualmente es obsoleta. Las versiones C++ de AT&T desde la versión 2.0 utilizan la nueva biblioteca iostream. C++ da a los streams un significado más abstracto. Un programa C++ visualiza entrada o salida como un flujo de datos. En la entrada un programa extrae bytes de un flujo de entrada, y en la salida un programa inserta bytes en un flujo de salida. Así, un flujo se puede considerar como una secuencia lineal de bytes con un significado. Los bytes pueden formar una representación binaria de datos carácter o numéricos. Los bytes de entrada pueden venir del teclado, pero también pueden proceder de otro dispositivo de almacenamiento como un disco duro u otro programa. Y de forma análoga los bytes del flujo de salida pueden ir destinados a la pantalla, a una impresora, a un fichero, o a otro programa. Este enfoque permite que un programa C++ trate la entrada de un teclado de igual forma que se trata la entrada desde un fichero. El programa C++ examina simplemente el flujo de bytes, sin necesidad de conocer cuál es la procedencia de los mismos. Esto mismo ocurre en la salida, y ésta se podrá procesar de forma independiente del lugar a donde vayan destinados los datos. La gestión de entrada implica dos etapas: Asociación de un flujo con una entrada a un programa. Conexión del flujo a un archivo. Esto quiere decir que los flujos necesitan para funcionar dos conexiones, una en cada extremo. De modo similar la gestión de la salida implica: La conexión de un flujo de salida al programa. Asociar algún destino de salida con el flujo. 2 3 Los streams constituyen un concepto muy familiar para todos los programadores de C bajo UNIX. Hasta la versión 1.2. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ -3- Si los datos se transfieren sin modificarse entre un extremo y otro, la operación de entrada/salida se denomina binaria o sin formato. Si por el contrario los datos, que son una representación interna de la máquina (por ejemplo long), se modifican en la transferencia entre ambos extremos, y son una representación de caracteres de texto (por ejemplo ASCII), la operación de entrada/salida se denomina entrada/salida con formato. La biblioteca iostream soporta ambas operaciones y permite que el programador controle operaciones específicas de formato mediante el uso de lo que en C++ se conoce como manipuladores. La biblioteca iostream emplea para las operaciones de entrada/salida buffers. Recordar que un buffer es un bloque de memoria que se emplea como almacenamiento temporal o intermedio para la transferencia de información de un dispositivo a un programa o viceversa. Típicamente dispositivos como unidades de disco transfieren información en bloques de 512 bytes, 1 Kbyte, o incluso mayores. La biblioteca iostream La biblioteca iostream es la biblioteca de entrada/salida estándar en C++. El acceso a esta biblioteca se realiza mediante el archivo cabecera iostream.h4, que se ha venido utilizando de forma reiterada en todos los ejemplos que se han visto hasta el momento. El sistema de entrada/salida en C++, está definido para trabajar con diferentes dispositivos (al igual que ocurre en ANSI C). Cada dispositivo se convierte en un dispositivo lógico y se denomina flujo o stream. Los flujos forman el interfaz común entre el programa, el dispositivo y el usuario. A este sistema de entrada/salida se le conoce como sistema de archivos a través de buffers. La estructura de los archivos puede variar con los distintos dispositivos, pero los flujos se comportan siempre de la misma forma5. Flujos predefinidos Los siguientes flujos u objetos6 se abren de forma automática cuando se ejecuta un programa C++ y se ha incluido el fichero de cabecera iostream.h. Nombre del flujo cin cout cerr clog Tipo de flujo Flujo de entrada estándar Flujo de salida estándar Flujo de salida de error estándar no almacenado en el buffer. Se emplea para visualizar mensajes de error Flujo de error estándar a través del buffer. Este flujo es más adecuado para grandes cantidades de mensajes de error Clase a la que pertenece istream ostream ostream ostream Flujos predefinidos en C++ 4 Las versiones antiguas de la biblioteca de flujos utilizan el fichero cabecera stream.h. En aquellas versiones donde existan ambos archivos stream.h define el conjunto completo de facilidades, mientras que iostream.h define un subconjunto que es compatible con las bibliotecas de flujo antiguas. Las versiones modernas de C++ soportan sólo iostream.h. 5 En C y C++ los archivos son considerados como simples flujos de bytes. 6 Ya que un stream no es más que un objeto que hace de puente entre el resto de los objetos de una aplicación y el dispositivo en el que van a ser almacenados. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ -4- Estos flujos se asocian a la consola por defecto, pero pueden ser redirigidos a otros archivos o dispositivos. Estos cuatro flujos se abren de forma automática antes de que la función main se ejecute, y se cierran justo después que la función main ha terminado. Por lo tanto no es preciso preocuparse de abrir o cerrar estos flujos. cerr presenta una ventaja sobre clog, y es que los buffers de salida se limpian cada vez que se utiliza cerr, de modo que la salida está disponible de forma inmediata en el dispositivo externo, que por defecto es la pantalla. Estructura de la biblioteca iostream La biblioteca iostream es francamente potente, está formada por unas 250 funciones y por aproximadamente 20 clases. Esta biblioteca está compuesta por varios archivos cabecera debido a su gran tamaño. Los archivos cabecera de iostream son: Nombre archivo cabecera iostream.h Contenido / uso fstream.h strstream.h iomanip.h stdiostream.h Información básica requerida para todas las operaciones de entrada/salida de flujo. Contiene los objetos cin, cout, cerr y clog. Proporciona capacidades de entrada/salida con o sin formato. Contiene los manipuladores más comunes. Contiene la información necesaria para las operaciones de procesamiento de archivos controladas por el usuario. Contiene la información necesaria para realizar operaciones con cadenas. Contiene la información necesaria para realizar entrada/salida formateada con los manipuladores de flujo, o para crear unos propios y adecuar el formato de entrada/salida a unas necesidades particulares. Rara vez se utiliza éste archivo de cabecera cuando se están creando nuevos programas C++. Se suele emplear cuando se mejora un programa ya escrito en C con código escrito en C++. Archivos de cabecera de iostream Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ -5- Operadores de inserción y de extracción La entrada y salida en C++ se realiza mediante los operadores de inserción7 <<, y de extracción8 >>. Una característica muy importante del sistema de entrada/salida de C++ es que estos operadores se pueden sobrecargar de forma que se puedan insertar y extraer cualquier tipo de objetos en el flujo. El operador de inserción << El operador << se denomina operador inserción en lugar de operador de desplazamiento a izquierda. El operador de inserción está sobrecargado para reconocer todos los tipos básicos de C++: unsigned char unsigned short long double signed char int unsigned long long double short unsigned int float La lista no incluye al tipo char, ya que este tipo es sinónimo de unsigned char o de signed char, dependiendo del compilador. La clase ostream proporciona una definición de la función operator << para cada uno de los tipos anteriores. El operador de inserción es un operador binario que retorna una referencia a un objeto ostream. Un ejemplo del uso del operador inserción es: void Ejemplo (int edad, char *nombre) { cout << "Mi nombre es: " << nombre << " y tengo " << edad << "años.\n"; } Del ejemplo se puede deducir que el operador de inserción se puede encadenar. Como es natural el operador de inserción no realiza un salto de línea, esto hay que hacerlo de forma explícita, bien al estilo C mediante la secuencia de escape \n, bien al estilo C++ con el manipulador endl. La sentencia cout que aparece en la función Ejemplo se escribiría de la siguiente forma si se quisiera emplear el manipulador endl. cout << "Mi nombre es: " << nombre << " y tengo " << edad << "años." << endl; El manipulador endl tiene una ventaja sobre la secuencia de escape \n, ya que además de insertar una nueva línea en el flujo, también vacía el buffer de salida. Por lo tanto es equivalente a utilizar \n seguido de fflush. Algunas de las reglas del operador << son: La función operator << devuelve una referencia a un objeto ostream, así que el operador se puede encadenar El operador << está sobrecargado con lo cual se puede utilizar con todos los tipos básicos Se debe sobrecargar el operador << para que pueda actuar sobre los tipos definidos por el usuario 7 8 Introducir objetos dentro de un flujo se conoce como inserción. Sacar objetos de un stream se denomina extracción. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ -6- Funciones de salida de ostream: put y write A parte de las diferentes funciones operador de inserción, la clase ostream proporciona el método put para visualizar caracteres, y el método write para visualizar cadenas. La función miembro put inserta un carácter en el flujo. Su prototipo es: ostream & put(char); Ejemplo: // Curso Lenguaje C++ // Programa: Ejemplo de la utilización del método put de la clase ostream // Fichero: PUT.CPP #include <iostream.h> void main (void) { cout.put('L').put('U').put('I').put('S') << endl; cout.put(3) << endl; cout.put(67).put(82).put(85).put(90) << endl; } La salida del programa es: LUIS ♥ CRUZ Como se puede ver en el ejemplo la función miembro se puede utilizar con caracteres o con enteros, así cout.put(77); visualizaría una M. También es posible concatenar varias llamadas como sucedía con el operador <<, e incluso combinar en la misma sentencia el método put con el operador inserción. Por su parte el método write se utiliza para insertar una secuencia de caracteres en el flujo. Tiene dos prototipos: ostream & write (const signed char *, int); ostream & write (const unsigned char *, int); Ejemplo: // Curso Lenguaje C++ // Programa: Utilización del método write de la clase ostream // Fichero: WRITE.CPP #include <iostream.h> #include <string.h> void main (void) { char *c1 = "Hola", *c2 = "Mundo", *c3 = "Cruel", *c4 = "ABCDEFGHIJK"; Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ -7- // Ejemplo de concatenación cout.write(c1, strlen(c1)).put(32). write(c2, strlen(c2)).put(32). write(c3, strlen(c3)).put('\n'); // Saca 25 caracteres cout.write(c2, 25) << endl; // Sacará basura, pues la longitud de // la cadena c2 es menor que 25 // Saca 3 caracteres, es decir la cadena Hol cout.write(c1, 3) << endl; } La salida del programa es: Hola Mundo Cruel Mundo DFD ÿMA #EA vBA Ð Hol El operador de extracción >> El operador >> es denominado operador de extracción, y es el opuesto del operador de inserción <<. Su misión es leer datos de un flujo de entrada, generalmente cin. Un sencillo ejemplo es el siguiente fragmento de código que sirve para leer un carácter. char t; cin >> t; El símbolo >> señala la dirección del flujo, y lee los caracteres hasta que encuentra uno que no es parte del tipo requerido (habitualmente espacios, tabuladores o retorno de carro). Este operador se puede utilizar en cascada. int a, b, c, d, e; float n; cin >> a >> b >> c >> d >> e >> n; Los resultados de pasar tipos incorrectos en la entrada de datos son imprevisibles. A diferencia de scanf al operador de extracción no se le debe especificar la dirección de la variable, así la siguiente sentencia será un error. cin >> &j; Algunas de las reglas del operador >> son: La función operator >> devuelve una referencia a un objeto istream, así que el operador se puede encadenar El operador >> está sobrecargado con lo cual se puede utilizar con todos los tipos básicos Se debe sobrecargar el operador >> para que pueda actuar sobre los tipos definidos por el usuario Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ -8- Un método general de utilizar cin para introducir caracteres es colocarlo dentro de un bucle while, del cual se saldrá cuando se alcance el fin de fichero9. while ( cin >> j ) { cout << j; } El operador de entrada >> al igual que ocurre con scanf tiene un inconveniente que lo hace inadecuado cuando se trata de leer cadenas con espacios en blanco en su interior, ya que sólo lee una cadena hasta que encuentra la primera ocurrencia del espacio en blanco. Siempre el carácter nulo que marca el final de las cadenas en C es añadido al final de la cadena que se lea. Función get La clase istream contiene la función miembro get para la introducción de cadenas completas de caracteres, incluyendo espacios en blanco. Esta función no realiza conversiones, simplemente acepta una cadena de caracteres, y los sitúa en la variable adecuada. Realmente get es una familia de funciones sobrecargadas, alguno de los prototipos son: istream & get (char *cad, int longitud, char fin='\n'); donde: − cad es una variable de cadena de caracteres − longitud es la longitud máxima de la cadena de entrada − fin es un carácter específico que produce la terminación de la entrada. Por defecto es \n Así, esta función aceptará caracteres hasta que se alcance la longitud máxima o se introduzca el carácter de terminación. istream & get (unsigned char &); istream & get (signed char &); Los dos últimos prototipos sirven para introducir un carácter. Ejemplo: // Curso Lenguaje C++ // Programa: Utilización del método get de la clase istream // Fichero: GET1.CPP #include <iostream.h> void main (void) { char cad[12]; cout << "Introduce una cadena: "; cin.get (cad, 4); // 3 caracteres más el carácter nulo final cout << "\n--" << cad << "--\n"; } 9 Ctrl-Z en MSDOS, y Ctrl-D en UNIX. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ -9- El siguiente es un ejemplo de la ejecución del programa. En negrita las entradas del usuario. Introduce una cadena: 1234567 --123-El programa anterior leerá cadenas de tres caracteres, o hasta que se dé un retorno de carro, o hasta que se encuentre un fin de fichero válido. Así, en el ejemplo se introduce la cadena 1234567, y la variable cadena cad queda cargada sólo con el valor 123. Esta función puede llegar a ser bastante problemática si no se tiene un poco de cuidado, ya que get deja el carácter de terminación de la entrada en el buffer, y se toma en la siguiente sentencia de entrada de datos. Así el siguiente programa no funcionaría como debiera, ya que la segunda cadena nunca se llega a pedir, al tomarse en esta segunda entrada de datos el carácter de terminación de la primera (el carácter intro). Por ello en la segunda entrada de datos lo que se asume que se ha tecleado una cadena vacía. #include <iostream.h> void main (void) { char c1[80]; char c2[4]; cout << cin.get cout << cin.get "Introduce una cadena: "; (c1, 80); "Introduce una segunda cadena: "; (c2, 4); cout << "\n-" << c1 << "-\n-" << c2 << "-\n"; } El siguiente es un ejemplo de la ejecución del programa. En negrita las entradas del usuario. Introduce una cadena: Mi cadena Introduce una segunda cadena: -Mi cadena-- Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 10 - Función getline Esta función miembro de istream opera de forma análoga a la función get, siendo su prototipo: istream & getline (char *, int, char ='\n'); Su diferencia con la función miembro get es que el carácter de terminación se lee antes de que se añada el carácter '\0'. En consecuencia esta función elimina el carácter de terminación del buffer de entrada. Con lo que ahora se puede solucionar el problema que se había planteado antes. // Curso Lenguaje C++ // Programa: Utilización del método getline de la clase istream // Fichero: GETLINE.CPP #include <iostream.h> void main (void) { char c1[80]; char c2[4]; cout << "Introduce una cadena: "; cin.getline (c1, 80); cout << "Introduce una segunda cadena: "; cin.get (c2, 4); cout << "\n-" << c1 << "-\n-" << c2 << "-\n"; } El siguiente es un ejemplo de la ejecución del programa. En negrita las entradas del usuario. Introduce una cadena: abcdefghi Introduce una segunda cadena: 123456 -abcdefghi-123- Función ignore Esta es otra de las funciones miembro de la clase iostream. Su prototipo es: istream & ignore (int n=1, int delim=EOF); Su cometido es saltarse n caracteres del stream de entrada, o hasta que encuentre el carácter delim. Así la sentencia. cin.ignore (25, '\n'); Lee e ignora los siguientes 25 caracteres o hasta que se produzca el primer salto de línea. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 11 - Ejemplo: // Curso Lenguaje C++ // Programa: Utilización del método ignore de la clase istream // Fichero: IGNORE.CPP #include <iostream.h> const int L=25; void main (void) { char cadena[L]; char c; cout << "Introduce una cadena (getline): "; cin.getline(cadena, L, '%'); cout << "Cadena = " << cadena << endl; cin.get(c); cout << "El siguiente carácter de entrada es: " << c << endl << endl; cin.ignore(L, '\n'); // Se desprecia el resto de la línea cout << "Introduce una cadena (get): "; cin.get(cadena, L, '%'); cout << "Cadena = " << cadena << endl; cin.get(c); cout << "El siguiente carácter de entrada es: " << c << endl; } El siguiente es un ejemplo de la ejecución del programa. En negrita las entradas del usuario. Introduce una cadena (getline): abcdeg%78 Cadena = abcdeg El siguiente carácter de entrada es: 7 Introduce una cadena (get): 122345%09 Cadena = 122345 El siguiente carácter de entrada es: % Observar que mientras que getline descarta el carácter límite % en la entrada, get no lo hace. Función read Esta función lee un número dado de bytes, almacenándolos en la posición especificada. Sus prototipos son: istream & read (char *, int); istream & read (signed char *, int); istream & read (unsigned char *, int); Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 12 - Al contrario que get y getline, read no añade ningún carácter a la entrada, por lo que no convierte la entrada a formato cadena. El uso más típico de esta función es con archivos binarios. Función putback Esta función inserta un carácter de nuevo en la cadena de entrada. Dicho carácter se convierte en el primer carácter leído por la siguiente sentencia de entrada. Su prototipo es: istream & putback (char); Función peek Esta función devuelve el siguiente carácter de la entrada sin ser extraído de la cadena de entrada. Devuelve EOF si no hay más caracteres en el flujo. Su prototipo es: int peek(); Ejemplo: // Curso Lenguaje C++ // Programa: Ejemplo de la utilización de los métodos putback() y peek() // Fichero: PP.CPP #include <iostream.h> #include <process.h> void main (void) { char c, encontrado=0; // Se busca el carácter $ en la entrada cout << "Introduzca una cadena: " << endl; while (cin.get(c)) { if (c!='$') cout << c; // Si no es lo que buscamos lo mostramos else { // Si lo encontramos salimos del bucle encontrado=1; break; } } if (encontrado) { cin.get(c); cout << "\nEl siguiente carácter de la entrada es " << c << } else { cout << "Se ha llegado al final de la entrada." << endl; exit(0); } Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón endl; (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 13 - // Comprobamos ahora el comportamiento de putback // Se busca el carácter $ en la entrada cout << "Introduzca una segunda cadena: " << endl; while (cin.get(c)) { if (c!='$') cout << c; // Si no es lo que buscamos lo mostramos else { // Si lo encontramos lo volvemos a insertar cin.putback(c); // y salimos del bucle encontrado=1; break; } } if (encontrado) { cin.get(c); cout << "\nEl siguiente carácter de la entrada es " << c << } else { cout << "Se ha llegado al final de la entrada." << endl; exit(0); } endl; // Repetimos lo anterior con otra cadena de entrada, pero ahora // se utiliza peek cout << "Introduzca una tercera cadena: " << endl; while (cin.peek() != '$') { cin.get(c); cout << c; } cin.get(c); cout << "\nEl siguiente carácter de la entrada es " << c << endl; } El siguiente es un ejemplo de la ejecución del programa. En negrita las entradas del usuario. Introduzca una cadena: abcdefg$hijk abcdefg El siguiente carácter de la entrada es h Introduzca una segunda cadena: ijk 123456$7890 123456 El siguiente carácter de la entrada es $ Introduzca una tercera cadena: 7890 RSTUV$WXYZ RSTUV El siguiente carácter de la entrada es $ Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 14 - Entrada/salida con formato En C++ existen dos formas propias de manipular el formato de la salida: Emplear las funciones miembro de la clase ios. Emplear unas funciones especiales que reciben el nombre de manipuladores. Clase ios Dentro de esta clase se han definido los métodos para realizar una amplia gama de operaciones de formato con la salida. Así se tienen funciones para el control de la base, la anchura del campo, carácter de relleno, etc. Ajustar la anchura de los campos: width Para llevar a cabo esta acción se tiene la función miembro width. Esta función se ocupa de establecer la anchura del campo de la variable de estado. Tiene dos prototipos: int width(); int width(int i); El primero de ellos se limita a informar del estado actual de la anchura del campo, mientras que el segundo establece la anchura del campo a i caracteres, y devuelve el valor de la anchura anterior. Como width es un método tiene que utilizar un objeto, como pueden ser cout y cin. Cuando se emplea con cin tiene el sentido de limitar el número de caracteres que se van a leer. Por su parte cuando se utiliza con cout, si el contenido de la variable es menor que la anchura fijada del campo, se visualizará el contenido de dicha variable justificado a la derecha de un campo de longitud la que se haya fijado, y el resto del campo se rellenará con espacios. Si por el contrario la longitud del contenido de la variable es mayor que la longitud del campo, se ignora la configuración de width y se muestra el valor completo de la variable. Por tanto, nunca se pierde información. El valor por defecto para la anchura es 0. Esto no significa que no se tenga reservado espacio para la salida de la variable, sino que se utilice el mínimo número de caracteres necesarios. Después de cada inserción width se inicia a 0. Ejemplo: // Curso Lenguaje C++ // Programa: Ejemplo de la utilización de width // Fichero: WIDTH.CPP #include <iostream.h> void main (void) { float mat[] = {0.5561, 0.4, 0.1234567, 1.2, -1.0002}; int sz = sizeof(mat) / sizeof(float); Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 15 - cout << "Salida Normal sin utilizar width()\n"; for (register int i=0; i<sz; cout << mat[i++] << endl); cout << "\nSalida utilizando width(4)\n"; for (i=0; i<sz; ){ cout.width(4); cout << mat[i++] << endl; } cout << "\nSalida utilizando width(8)\n"; for (i=0; i<sz; ){ cout.width(8); cout << mat[i++] << endl; } } La salida del programa anterior es, después de haber sido compilado con Microsoft Visual C++ .NET: Salida Normal sin utilizar width() 0.5561 0.4 0.123457 1.2 -1.0002 Salida utilizando width(4) 0.5561 0.4 0.123457 1.2 -1.0002 Salida utilizando width(8) 0.5561 0.4 0.123457 1.2 -1.0002 Precisión: precision Para establecer el número de dígitos de coma flotante después del punto decimal se utiliza la función miembro precision. Sus prototipos son: int precision () int precision (int) El primer prototipo se corresponde con una función miembro que retorna el valor de la variable de estado de precisión. El segundo prototipo representa a la función que fija la precisión a un valor determinado. y devuelve el estado anterior. El valor por defecto es 0, y significa que los números en coma flotante se representan hasta con 6 dígitos decimales, y si el número tiene más decimales el último dígito se redondea. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 16 - Ejemplo: // Curso Lenguaje C++ // Programa: Ejemplo de la utilización de precision() // Fichero: PREC.CPP #include <iostream.h> void main (void) { double numero = 1.2345678901; cout << "La precisión por defecto es: " << cout.precision() << "\n"; for (register int i=0; i<=10; i++) { cout.precision(i); cout << "\tCon precisión "; cout.width(2); cout << cout.precision() << "\t\t" << numero << "\n"; } } La salida del programa es: La precisión por defecto es: 0 Con precisión 0 Con precisión 1 Con precisión 2 Con precisión 3 Con precisión 4 Con precisión 5 Con precisión 6 Con precisión 7 Con precisión 8 Con precisión 9 Con precisión 10 1.234568 1.2 1.23 1.235 1.2346 1.23457 1.234568 1.2345679 1.23456789 1.23456789 1.2345678901 Relleno: fill Las partes no utilizadas del campo de salida son rellenadas por el objeto cout con espacios en blanco. Para cambiar esta situación por defecto se puede usar la función miembro fill pudiendo fijar un carácter de relleno. Esta función tiene dos prototipos: char fill (); char fill (char); Como en los casos anteriores, el primer prototipo devuelve el valor de la variable de estado de relleno actual, y el segundo lo cambia y devuelve el anterior. Como ejemplo se va a modificar el programa anterior, de forma que cuando se muestre el valor de la precisión se haga siempre con dos dígitos, y de ser un número de un dígito, se rellene con ceros. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 17 - // Curso Lenguaje C++ // Programa: Ejemplo de la utilización de fill // Fichero: FILL.CPP #include <iostream.h> void main (void) { double numero = 1.2345678901; cout << "La precisión por defecto es: " << cout.precision() << "\n"; for (register int i=0; i<=10; i++) { cout.precision(i); cout << "\tCon precisión "; cout.width(2); cout.fill('0'); cout << cout.precision() << "\t\t" << numero << "\n"; } } La salida del programa es: La precisión por defecto es: 0 Con precisión 00 Con precisión 01 Con precisión 02 Con precisión 03 Con precisión 04 Con precisión 05 Con precisión 06 Con precisión 07 Con precisión 08 Con precisión 09 Con precisión 10 Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón 1.234568 1.2 1.23 1.235 1.2346 1.23457 1.234568 1.2345679 1.23456789 1.23456789 1.2345678901 (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 18 - Indicadores de formato Cada objeto derivado de ios contiene una variable de estado de indicadores. Estos indicadores o banderas controlan el formato de entrada y salida. Asociada a cada indicador existe una constante, nombre que representa una máscara de bits, que se emplea cuando se accede y cambia el indicador de una variable de estado específica. Indicador ios ios::skipws Valor numérico 0x0001 Significado Saltar sobre espacios en blanco, sólo entrada (1 - bit 1) ios::left 0x0002 Justificar a la izquierda (2 - bit 2) ios::right 0x0004 Justificar a la derecha (4 - bit 3) ios::internal 0x0008 Rellenar números con espacios después de los indicadores de base (8 - bit 4) ios::dec 0x0010 Formato en base 10. Conversión decimal (16 - bit 5) ios::oct 0x0020 Formato en base 8. Conversión octal (32 - bit 6) ios::hex 0x0040 Formato en base 16. Conversión hexadecimal (64 - bit 7) ios::showbase 0x0080 Visualizar indicador de base numérica. Sólo salida (128 - bit 8) ios::showpoint 0x0100 Visualizar el punto decimal. Salida float (256 - bit 9) ios::uppercase 0x0200 Visualizar dígitos hexadecimales en mayúsculas (512 - bit 10) ios::showpos 0x0400 Añade un signo + a los números positivos (1024 - bit 11) ios:scientific 0x0800 Utilizar notación científica para reales (2048 - bit 12) ios::fixed 0x1000 Utilizar notación coma fija para reales (4096 - bit 13) ios::unitbuf 0x2000 Limpia los flujos después de la inserción (8192 - bit 14) ios::stdio 0x4000 Limpia stdout y stderr después de la inserción (16384 - bit 15) Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 19 - Los anteriores indicadores se manejan por medio de las funciones siguientes: setf, unsetf, y flags. Prototipos de las funciones Significado long ios::flags() Informa de todos long ios::flags(long flags) Informa de todos, establece todos long ios::setf(long flags) Informa de todos, establece máscara long ios::setf(long bis, long flags) Informa de todos, establece un grupo (pone a 0 los primeros, después pone a 1 los segundos) long ios::unsetf(long flags) Informa de todos, limpia la máscara En la clase ios existen tres constantes que se utilizan como segundo parámetros de setf y sirven para seleccionar los grupos de indicadores. Constante Indicadores seleccionados ios::adjustifield Justificación izquierda, derecha e interna ios::basefield Indicadores de conversión decimal, octal y hexadecimal ios::floatfield Indicadores de notación científica y de coma fija Ejemplo 1: // Curso Lenguaje C++ // Programa: Ejemplo de la utilización de indicadores de formato // Fichero: INDI1.CPP #include <iostream.h> void main (void) { float e1=5.75, e2=6.25, e3=e1+e2; cout.setf(ios::showpoint); // Forzamos a que utilice el punto decimal // aunque la salida no lo necesite cout.setf(ios::showpos); // Si el número es positivo que saca un // signo + delante cout << e3 << endl; cout << 3 << endl; cout << -3.0 << endl; cout.unsetf(ios::showpoint); // // // // // Salida +12.000000 Salida +3 Salida -3.000000 Desactivamos la salida forzosa del punto decimal Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ cout << e3 << endl; cout.unsetf(ios::showpos); cout << 3 << endl; - 20 - // Salida +12 // Desactivamos la salida del + } La salida del programa es: +12.000000 +3 -3.000000 +12 3 Ejemplo 2: // Curso Lenguaje C++ // Programa: Ejemplo de la utilización de indicadores de formato // Fichero: INDI2.CPP #include <iostream.h> void main (void) { cout.setf(ios::showbase); // Forzamos a que se muestre el // indicador de base numérica cout.setf(ios::hex, ios::basefield); // Formato en hexadecimal cout << 13 << "\t" << 25 << "\t" << 3 << endl; cout.setf(ios::oct, ios::basefield); // Formato en octal cout << 13 << "\t" << 25 << "\t" << 3 << endl; cout.setf(ios::dec, ios::basefield); // Formato en decimal cout << 13 << "\t" << 25 << "\t" << 3 << endl; cout.unsetf(ios::showbase); // Ya no mostramos el indicador // de base numérica cout.setf(ios::hex, ios::basefield); // Formato en hexadecimal cout << 13 << "\t" << 25 << "\t" << 3 << endl; cout.setf(ios::oct, ios::basefield); // Formato en octal cout << 13 << "\t" << 25 << "\t" << 3 << endl; cout.setf(ios::dec, ios::basefield); // Formato en decimal cout << 13 << "\t" << 25 << "\t" << 3 << endl; } Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 21 - La salida del programa es: 0xd 015 13 d 15 13 0x19 031 25 19 31 25 0x3 03 3 3 3 3 Ejemplo 3: // Curso Lenguaje C++ // Programa: Ejemplo de la utilización de indicadores de formato. // Fichero: INDI3.CPP #include <iostream.h> void main (void) { double num=1.23455698; int i=102; cout << "i= " << i << "\tnum= " << num << endl; // Se guardan los valores originales de los indicadores long indi=cout.flags(); // Activación de algunos indicadores cout.setf(ios::hex, ios::basefield); cout << "i= " << i << "\tnum= " << num << endl; cout.setf(ios::showbase | ios::uppercase |ios::scientific); cout << "i= " << i << "\tnum= " << num << endl; // Se restauran los valores por defecto cout.flags(indi); cout << "i= " << i << "\tnum= " << num << endl; } La salida del programa es: i= i= i= i= 102 66 0X66 102 num= num= num= num= 1.23456 1.23456 1.234557E+000 1.23456 Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 22 - Manipuladores Hasta ahora se han visto varias formas de manejo del formato, bastante potentes, pero un poco engorrosas de emplear. C++ ofrece un enfoque más amigable con lo que se denominan manipuladores. Los manipuladores son funciones especiales que se pueden emplear con los operadores de inserción y extracción para formatear la entrada y la salida. Los manipuladores que existen se encuentran recogidos en la siguiente tabla: Manipulador Entrada/Salida Significado dec E/S endl S Envía carácter de nueva línea ends S Envía un nulo \0 flush S Vacía un flujo Formato de datos numéricos en decimal hex E/S Formato de datos numéricos en hexadecimal oct E/S Formato de datos numéricos en octal resetiosflags(long lg) E/S Desactiva los indicadores de lg setbase(int i) S Formato de los números en la base i setfill(char c) E/S Establece c como carácter de relleno setiosflags(long lg) E/S Activa los indicadores de lg setprecision(int i) E/S Establece i dígitos decimales setw(int i) E/S Establece i caracteres de anchura de campo ws E Ignora los caracteres en blanco iniciales Los manipuladores dec, hex, oct, ws, endl, ends, y flush se encuentran definidos en iostream.h, el resto lo están en iomanip.h. Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 23 - El uso de los manipuladores es muy sencillo. Se va a ver un ejemplo. Ejemplo: // // // // Curso Lenguaje C++ Programa: Ejemplo de la utilización de indicadores de formato Fichero: MANIP1.CPP Compilado con Microsoft Visual C++ .NET #include <iostream.h> #include <iomanip> using namespace std; void main (void) { // Justificación de la salida a la derecha // Establecer la anchura a 50 // Desactivar justificación a la derecha cout << setiosflags(ios::right) << setw(50) << "Esto va a la derecha" << resetiosflags(ios::right) << endl; cout << "Esto está en la izquierda" << endl; cout << "Ahora unos numeritos:" << endl; cout << hex << 10 << setw(5) << oct << 10 << setw(5) << dec << 10 << endl; cout << setfill('#') << hex << 16 << setw(5) << oct << 16 << setw(5) << dec << 16 << setfill(' ') << endl; } La salida de este programa es: Esto va a la derecha Esto está en la izquierda Ahora unos numeritos: a 12 10 10###20###16 Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005) Programación Orientada a Objetos 7 – Entrada/salida en C++ - 24 - Ejercicios resueltos 1. Crear un pequeño programa en el cual dependiendo de una variable de control, los mensajes salgan por la salida estándar (cout), o por la salida de error (cerr), pero empleando para ello la misma sentencia. // Curso Lenguaje C++ // Programa: Cambio en la dirección de la salida // Fichero: EJER7-1.CPP #include <iostream.h> void main (void) { ostream *s; int bandera=0; if (bandera) s=&cout; else s=&cerr; *s << "Hola" << endl; } Ingeniería Técnica en Informática de Sistemas (3er curso) Departamento de Informática y Automática – Universidad de Salamanca © Francisco José García Peñalvo y Juan Andrés Hernández Simón (v1.05 – Feb.2005)