CONTENIDO DE LA LECCIÓN 8

71
MIGUEL Á. TOLEDO MARTÍNEZ FUNDAMENTOS – LECCIÓN 7 7-1 CONTENIDO DE LA LECCIÓN 8 INTRODUCCIÓN DE DATOS POR TECLADO 1. Introducción 3 2. Clase y objeto de entrada de flujo 3 3. Entrada de flujo 6 3.1. Operador de extracción de flujo 6 3.1.1. Ejemplos 7.1, 7.2, 7.3, 7.4, 7.5 6 3.2. Posibles errores 8 3.3. Lectura de tipos de datos mezclados 9 3.3.1. Ejemplo 7.6 9 3.4. Lectura de datos de un solo carácter 10 3.4.1. Ejemplo 7.7 10 3.5. Uso de la función miembro get() para leer datos de un solo carácter 12 3.5.1. Ejemplos 7.8, 7.9, 7.10 13 3.6. Lectura de cadena de caracteres 16 3.6.1. Ejemplo 7.11 16 3.7. Uso de getline() para leer cadenas de caracteres 17 3.7.1. Ejemplos 7.12, 7.13, 7.14 17 3.7.2. Un posible problema cuando se usa getline() 20 3.7.2.1. Ejemplos 7.15, 7.16, 7.17 20 3.8. Uso de gets() y fgets() para leer cadenas 23 3.8.1. Ejemplo 7.18 23 3.9. Examen breve 17 69 3.10. Las funciones miembro peek(), putback() e ignore() de istream 25 3.11. E/S a prueba de tipos 25 4. Procesamiento de archivos 25 4.1. La jerarquía de datos 25 4.2. Archivos y flujos 28 4.3. Clases: la base de los archivos C++ 29 4.3.1. Ejemplo 7.19 30 4.4. Lectura y escritura de un archivo en disco 31 4.4.1. Ejemplos 7.20, 7.21 31 4.5. El uso de ciclos para leer y procesar archivos 33 4.5.1. Ejemplo 7.22 34 4.6. Examen breve 18 69 4.7. Creación de un archivo de acceso secuencial 35 4.7.1. Ejemplo 7.23 35 4.8. Lectura de datos de un archivo de acceso secuencial 39 4.8.1. Ejemplos 7.24, 7.25 39 4.9. Actualización de archivos de acceso secuencial 43 4.10. Archivos de acceso aleatorio 44 4.11. Creación de un archivo de acceso aleatorio 45 4.11.1. Ejemplo 7.26 46 4.12. Escritura aleatoria de datos a un archivo de acceso aleatorio 47 4.12.1. Ejemplo 7.27 47 4.13. Lectura secuencial de datos desde un archivo de acceso aleatorio 48 4.13.1. Ejemplo 7.28 48 4.14. Ejemplo: un programa de procesamiento de transacciones 50 4.14.1. Ejemplo 7.29 50

Transcript of CONTENIDO DE LA LECCIÓN 8

Page 1: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-1

CONTENIDO DE LA LECCIÓN 8

INTRODUCCIÓN DE DATOS POR TECLADO 1. Introducción 3 2. Clase y objeto de entrada de flujo 3 3. Entrada de flujo 6

3.1. Operador de extracción de flujo 6 3.1.1. Ejemplos 7.1, 7.2, 7.3, 7.4, 7.5 6

3.2. Posibles errores 8 3.3. Lectura de tipos de datos mezclados 9

3.3.1. Ejemplo 7.6 9 3.4. Lectura de datos de un solo carácter 10

3.4.1. Ejemplo 7.7 10 3.5. Uso de la función miembro get() para leer datos de un solo carácter 12

3.5.1. Ejemplos 7.8, 7.9, 7.10 13 3.6. Lectura de cadena de caracteres 16

3.6.1. Ejemplo 7.11 16 3.7. Uso de getline() para leer cadenas de caracteres 17

3.7.1. Ejemplos 7.12, 7.13, 7.14 17 3.7.2. Un posible problema cuando se usa getline() 20

3.7.2.1. Ejemplos 7.15, 7.16, 7.17 20 3.8. Uso de gets() y fgets() para leer cadenas 23

3.8.1. Ejemplo 7.18 23 3.9. Examen breve 17 69 3.10. Las funciones miembro peek(), putback() e ignore() de istream 25 3.11. E/S a prueba de tipos 25

4. Procesamiento de archivos 25 4.1. La jerarquía de datos 25 4.2. Archivos y flujos 28 4.3. Clases: la base de los archivos C++ 29

4.3.1. Ejemplo 7.19 30 4.4. Lectura y escritura de un archivo en disco 31

4.4.1. Ejemplos 7.20, 7.21 31 4.5. El uso de ciclos para leer y procesar archivos 33

4.5.1. Ejemplo 7.22 34 4.6. Examen breve 18 69 4.7. Creación de un archivo de acceso secuencial 35

4.7.1. Ejemplo 7.23 35 4.8. Lectura de datos de un archivo de acceso secuencial 39

4.8.1. Ejemplos 7.24, 7.25 39 4.9. Actualización de archivos de acceso secuencial 43 4.10. Archivos de acceso aleatorio 44 4.11. Creación de un archivo de acceso aleatorio 45

4.11.1. Ejemplo 7.26 46 4.12. Escritura aleatoria de datos a un archivo de acceso aleatorio 47

4.12.1. Ejemplo 7.27 47 4.13. Lectura secuencial de datos desde un archivo de acceso aleatorio 48

4.13.1. Ejemplo 7.28 48 4.14. Ejemplo: un programa de procesamiento de transacciones 50

4.14.1. Ejemplo 7.29 50

Page 2: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-2

5. Solución de problemas en acción: Cálculo de voltaje 55

5.1. Problema 55 5.2. Definición del problema 568 5.3. Planeación de la solución 56 5.4. Codificación del programa 57

6. Lo que necesita saber 59 7. Preguntas y problemas 62

7.1. Preguntas 62 7.2. Problemas 65

Page 3: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-3

LECCIÓN 8 INTRODUCCIÓN DE DATOS POR TECLADO

INTRODUCCIÓN

En esta lección aprenderá que C++ proporciona un flujo de entrada llamado cin mediante el cual su programa puede leer información introducida desde el teclado. Cuando utilice cin para leer entrada desde el teclado, debe especificar una o más variables hacia la que desea que la entrada sea asignada.

Objetivos de esta lección: •• Utilizar cin para leer letras y números desde el teclado. •• Utilizar las funciones miembro get(), getline() para leer cadenas. •• Utilizar las funciones gets() y fgets() para leer cadenas. •• Utilizar las funciones miembro peek(), putback() e ignore() •• Aprender a crear, leer, escribir y actualizar archivos en disco. •• Utilizar ciclos para leer y procesar archivos. •• Familiarizarse con el procesamiento de archivo de acceso secuencial. •• Aprender a especificar operaciones de E/S no formateadas de alto desempeño. •• Comprender las diferencias entre el procesamiento de archivos de datos formateados y el

procesamiento de archivos de datos sin formato. •• Construir un programa de procesamiento de transacciones con procesamiento de archivos de

acceso aleatorio.

Cuando sus programas utilizan el flujo de salida cout, coloca datos en el flujo utilizando el operador de inserción (<<) De manera semejante, cuando sus programas utilizan cin para leer entrada desde el teclado, sus programas utilizarán el operador de extracción ( >>)

El almacenamiento de datos en variables y arreglos es temporal. Los archivos se utilizan para la retención permanente de grandes cantidades de datos. Las computadoras almacenan los archivos en dispositivos de almacenamiento secundario, tales como discos magnéticos, discos ópticos y cintas. En esta lección explicaremos la manera en que los archivos de datos se crean, actualizan y procesan mediante programas C++. Consideraremos a los archivos de acceso secuencial y a los de acceso aleatorio. Compararemos el procesamiento de archivos de datos formateados y el procesamiento de archivos de datos sin formato. CLASE Y OBJETO DE ENTRADA DE FLUJO La obtención de información dentro de un programa para procesamiento se llama lectura. En la mayoría de los sistemas actuales, la información se lee desde una de dos fuentes: el teclado o un archivo en disco. El primer enunciado C++ que usará para leer datos desde el teclado es cin (se pronuncia como si fueran dos palabras: c-in; “ci in”, en lenguaje fonético) Como cout, cin es un objeto de flujo, predefinido en C++, como parte del archivo de encabezado iostream.h. El objeto cin es un flujo de entrada asociado, de manera predeterminada, al teclado de su sistema.

Page 4: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-4

cin es un objeto de la clase istream, y se dice que está enlazado (o conectado) al dispositivo de entrada estándar que normalmente es el teclado. El operador de extracción de flujo, como se utiliza en la siguiente instrucción, causa que un valor para la variable entera calificación (suponiendo que calificación ha sido declarada como int) se reciba desde cin hacia la memoria:

cin >> calificación; Observe que la operación de extracción de flujo es lo suficientemente inteligente para saber el tipo de datos que es. Suponiendo que calificación se haya declarado adecuadamente, no se necesita especificar información adicional para utilizarla con dicho operador (como es el caso, por coincidencia, en la E/S estilo C)

Antes de que comprenda cómo trabaja este enunciado debe saber un poco más de cómo C++ ve una línea de datos. Suponga que escribe dos líneas de datos como sigue:

74 92 88↵↵ 23 45 16↵

Cuando escribe los datos anteriores desde el teclado, debe colocar cada número en forma consecutiva, separando los números con uno o más espacios. Conforme se ingresan los valores, éstos se almacenan en el flujo de memoria temporal (buffer) cin. Al final de la línea se deberá presionar ENTER (↵↵) ¿Cómo sabe el sistema que finaliza un elemento de datos y empieza otro? Correcto, el espacio en blanco (blancos) entre los elementos de datos separa un elemento de otro. Después, ¿Cómo reconoce el sistema el fin de la línea de datos? Correcto otra vez, al presionar la tecla ↵↵ que define el final de la línea e introduce un CRLF dentro de la memoria temporal de flujo. En la figura 7.1 se ilustra la memoria temporal de flujo y su contenido después de que esta operación ingresa los datos: Flujo de entrada cin 74 92 88↵↵ �� Carácter en blanco 23 45 16↵↵

Figura 7.1 . Memoria temporal (buffer) de flujo de entrada después de escribir dos líneas de datos.

El formato general para cin es el siguiente:

cin >> variable; Observe como después del objeto cin sigue un doble pico paréntesis a la derecha, el operador de extracción de flujo, >>, seguido por la variable a la que asignará los datos

74 �� 92 �� 88 �� CRLF 23 �� 45 16 CRLF ��

TECLADO

Page 5: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-5

introducidos. Decimos que el operador >> extrae los datos que se van a leer de los datos de entrada en la memoria temporal de flujo. Posteriormente, los datos de entrada se asignan a la variable escrita en el enunciado cin. Por supuesto, la variable deberá estar definida como un tipo de datos legal antes de utilizarla en el enunciado cin. Por ejemplo, suponga que se han definido tres variables de clase entero llamadas registro1, registro2 y registro3. Para introducir datos en las cada una de las variables, deberán insertarse tres enunciados cin dentro del programa, como los siguientes:

cin >> registro1; cin >> registro2; cin >> registro3;

Cuando C++ encuentre los enunciados anteriores en el programa, detendrá la ejecución hasta que el usuario escriba los datos solicitados. Ahora suponga que el usuario, desde el teclado, escribe lo siguiente;

74↵↵ 92↵↵ 88↵

¿Qué supone que pasará? Correcto, otra vez, el valor 74 se extraerá del flujo cin y se asignará a registro1, se extraerá el valor 92 y se asignará a registro2 y se extraerá el valor 88 y se asignará a registro3. De esta manera, se puede pensar en la operación cin como una operación de asignación. Cuando se use el operador >>, un valor escrito desde el teclado será extraído del flujo de entrada y será asignado a la variable listada en el enunciado cin. En la figura 7.2 se muestra el contenido de la memoria temporal para estas entradas. Flujo de entrada 74↵↵ 92↵↵ 88↵↵

Figura 7.2. Flujo de entrada de datos. El usuario también puede escribir estos mismos valores en una línea sencilla, como la siguiente:

74 CRLF 92 CRLF 88 CRLF

Teclado

Page 6: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-6

74 92 88↵ Flujo de entrada 74 92 88↵↵ �� Carácter en blanco Las mismas asignaciones de variables ocurrirán para ambas entradas del usuario, porque el operador de extracción de flujo >> ignora todos los espacios en blanco, incluyendo los CRLF.

Nota: Es posible usar el flujo cin con el operador >> para leer varias variables como parte de un enunciado sencillo. Por ejemplo, el enunciado:

cin >> registro1 >> registro2 >> registro3;

le permitirá leer tres entradas del usuario con un solo enunciado cin. Los valores escritos desde el

teclado se asignan uno a uno a las variables listadas en el enunciado cin. El orden de asignación es el orden respectivo de los datos de entrada y el listado de las variables.

ENTRADA DE FLUJO Ahora consideremos la entrada de flujo. Esta puede realizarse mediante el operador de extracción de flujo, es decir, el operador >> sobrecargado. Generalmente, dicho operador pasa por alto los caracteres de espacio en blanco (tales como los espacios en blanco, las tabulaciones y las nueva líneas) que estén en el flujo de entrada. Más adelante veremos la manera de cambiar este comportamiento. Cuando el operador de extracción de flujo encuentra el fin de archivo dentro de un flujo, devuelve cero (false); de lo contrario devuelve una referencia al objeto mediante el cual es llamado. Cada flujo contiene un conjunto de bits de estado que se utilizan para controlar el estado del flujo (es decir, estados de formato, de asignación de error, etc.) Si se introducen datos de tipo erróneo, la extracción de flujo causa que se establezca el failbit del flujo y si la operación falla que se establezca el badbit de flujo. OPERADOR DE EXTRACCIÓN DE FLUJO Para leer dos enteros se utiliza el objeto cin y el operador de extracción de flujo >> sobrecargado. Ejemplo 7.1

El siguiente programa: CIN1.CPP, calcula la suma de dos enteros introducidos desde el teclado mediante el operador de extracción de flujo.

74 �� 92 �� 88 CRLF

Teclado

Page 7: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-7

La relativamente alta precedencia de los operadores >> y << puede causar problemas.

Ejemplo 7.2

El siguiente programa: CIN2.CPP, no se compilará adecuadamente sin los paréntesis que están alrededor de la expresión condicional. El lector debe verificar esto.

Ejemplo 7.3

El siguiente programa: CIN3.CPP, usa cin para leer un número desde el teclado. El programa asigna el número tecleado a la variable llamada numero.

/* El siguiente programa: CIN3.CPP , asigna el número tecleado a la variable numero . */ #include <iostream.h> // Para cout void main(void) { int numero ; // El número leído desde el teclado cout << "Digite su número ENTERO favorito y oprima INTRO: "; cin >> numero; cout << "Su número entero favorito es: " << numero << endl; } //Fin de main()

/* El siguiente programa: CIN1.CPP , calcula la suma de dos enteros introducidos desde el teclado mediante cin y el operador de extracción de flujo. */ #include <iostream.h> //Para cin y cout void main(void) { int x, y;

cout << “Introduzca dos números enteros: ”; cin >> x >> y; cout << “La suma de ” << x << “ y ” << y << “ es: ” << (x + y) << endl;

}//Fin de main()

/* El siguiente programa: CIN2.CPP , prevé de un problema de precedencia entre el operador de inserción de flujo y el operador condicional. Se necesitan paréntesis alrededor de la expresión condicional. #include <iostream.h> //Para cout y cin void main(void) {

int x, y; cout << “Introduzca dos números enteros: ”; cin >> x >> y; cout << x << (x == y ? “ es” : “ no es” ) << “ igual a ” << y << endl;

}//Fin de main()

Page 8: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-8

Ejemplo 7.4 El siguiente programa: DOSNUMS.CPP, le solicita dos números. El programa asigna los números a las variables primero y segundo. El programa visualiza los números utilizando cout.

Ejemplo 7.5

El siguiente programa: CIN_LARG.CPP, lee un valor numérico de clase long int. Experimente con números negativos. POSIBLES ERRORES Cuando sus programas utilizan cin, debe tener cuidado de algunos errores que pueden ocurrir al introducir datos erróneos. Por ejemplo, corra el programa CIN3.CPP, cuando el programa le solicita su número entero favorito, digite el número 1000000 y oprima Enter (↵↵) El programa no visualizará el número 1000000, que fue el valor introducido. Si examina el programa, notará que cin asigna el valor a una variable de clase int. Como aprendió en la lección

/* El siguiente programa: DOSNUMS.CPP , solicita dos números. El programa asigna los números a las variables primero y segundo. Posteriormente visualiza los números utilizando cout. */ #include <iostream.h> // Para cout void main(void) { int primero, segundo; // Números tecleados cout << "Digite dos números ENTEROS separados por un espacio y oprima INTRO: "; cin >> primero >> segundo; cout << "Los números enteros tecleados son: " << primero << " y " << segundo << endl; } //Fin de main()

/* El siguiente programa: CIN_LARG.CPP, lee un valor numérico de clase long int. */ #include <iostream.h> //Para cout void main(void) { long valor; cout << "Digite un número ENTERO LARGO y oprima INTRO: "; cin >> valor; cout << "El número que tecleó fue: " << valor << endl; } //Fin de main()

Page 9: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-9

2, las variables de clase int contienen valores en el rango de –32768 a 32767. Razón por la cual ocurre un error de sobreflujo. Como ejercicio introduzca las letras ABC y observe lo que ocurre. LECTURA DE TIPOS DE DATOS MEZCLADOS Las dos reglas fundamentales que se aplican para leer cualquier dato son los siguientes:

1. Todos las variables listadas dentro del enunciado cin deben de ser previamente definidos antes de usarlas en el enunciado.

2. Los tipos de datos escritos para una variable determinada deberán coincidir con las clases de datos definidos para esta variable.

La primera regla parece obvia. En un programa C++, no podrá usar una variable a menos

que se haya definido previamente en el programa. La segunda regla necesita una mayor explicación. Ejemplo 7.6

El siguiente programa: USOCIN.CPP, ilustra el uso de los objetos cin y cout.

Suponga que el usuario escribe los siguientes datos cuando se encuentran los enunciados cin:

Escriba un número ENTERO: 98.5 ↵↵

Escriba un número DECIMAL:

¿Qué sucede? Observe que el programa define a registro1 como un entero y a registro2 como una variable de punto flotante. Sin embargo, el usuario ha escrito un valor decimal para registro1 (ni siquiera permite introducir el segundo número) De esta manera C++ intenta asignar un valor decimal (98.5) a una variable entera (registro1 ). A esto se le llama tipo incompatible y originará un error en la mayoría de los lenguajes robustos como Pascal. Sin embargo, recuerde que C++ no

// El siguiente programa: USOCIN.CPP, muestra el uso de la operación cin. #include <iostream.h> //Para cout void main(void)

{ // Se definen las variables int registro1 = 0; float registro2 = 0.0; // Pide y lee los datos del usuario cout << "Escriba un número ENTERO : "; cin >> registro1; cout << "Escriba un número DECIMAL : "; cin >> registro2; cout << endl; cout << "Los números escritos son :" << endl; cout << registro1 << endl; cout << registro2 << endl; } //Fin de main()

Page 10: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-10

requiere tanta escritura como algunos otros lenguajes estructurados. En realidad C++ asignará la parte entera del primer valor a registro1 y la parte decimal del primer valor a registro2. De esta manera, la variable registro1 toma el valor 98 y registro2 tomar el valor 0.5. Después, con el mismo programa, suponga que el usuario escribe la siguiente línea de datos:

98↵↵ 78↵

Ahora no hay problema. Pero ¿Cómo puede ser esto? ¿Por qué el compilador asigna un valor entero (78) a una variable de punto flotante (registro2)? Está bien, porque los enteros son una serie de números reales. El compilador simplemente convierte el valor entero en un formato de punto flotante. De esta manera el valor entero 78 se convierte en el valor de punto flotante 78.0 para almacenarse dentro de la memoria principal de trabajo.

LECTURA DE DATOS DE UN SOLO CARÁCTER La lectura de datos numéricos es directa, siempre y cuando se haga uso de las dos reglas para la lectura de datos. Sin embargo, hay algunas cosas que convienen recordar cuando se leen datos de carácter usando cin.

1. Sólo se lee un carácter a la vez. 2. Los espacios en blanco (espacios, tabuladores, líneas nuevas, retorno de carro, etc.) son

ignorados por cin cuando se usa el operador >>. Sin embargo, es posible leer el espacio en blanco usando diferentes funciones cin

3. Los valores numéricos se pueden leer como caracteres, pero cada dígito se lee como un

carácter por separado. Ejemplo 7.7

El siguiente programa LECCAR.CPP, ilustra el uso de los conceptos anteriores.

// El siguiente programa: LEECAR.CPP, ilustra la lectura de datos de la clase caracter. #include <iostream.h> //Para cout void main(void) { // Define las variables ingresadas y las inicializa con espacio en blanco. char calificacion1 = ' '; char calificacion2 = ' '; char calificacion3 = ' '; // Lectura/escritura de los datos del usuario cout << "Introduzca una calificación (R,D,C,B,A): "; cin >> calificacion1; cout << "La calificación introducida fue: " << calificacion1 << endl << endl; cout << "Introduzca una calificación (R,D,C,B,A): "; cin >> calificacion2; cout << "La calificación introducida fue: " << calificacion2 << endl << endl; cout << "Introduzca una calificación (R,D,C,B,A): "; cin >> calificacion3; cout << "La calificación introducida fué: " << calificacion3 << endl << endl; } //Fin de main()

Page 11: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-11

El programa anterior define tres variables (calificacion1, calificacion2, calificacion3) de la clase carácter, las cuales son inicializadas con espacios en blanco. Después el programa lee las tres variables de clase carácter desde el teclado y repite las variables en la pantalla del monitor. Ahora vamos a ver que hará el programa con algunos casos de entrada.

Caso 1: Introduzca una calificación (R,D,C,B,A): A↵↵ La calificación introducida fue: A Introduzca una calificación (R,D,C,B,A): B↵↵ La calificación introducida fue: B Introduzca una calificación (R,D,C,B,A): C↵↵ La calificación introducida fue: C

Se observa la salida que se esperaba. El compilador asigna un solo carácter de entrada a la variable carácter respectiva listada en el enunciado cin.

Caso 2:

El usuario escribe: ABC↵↵ El sistema muestra: A B

C

En este caso, el usuario ha escrito los caracteres ‘A’, ‘B’ y ‘C’ en una sola línea. De esta manera, los tres caracteres se colocan en la memoria temporal del flujo de entrada. El compilador todavía asigna el carácter A a calificacion1, el carácter B a calificacion2 y el carácter C a calificacion3. Veamos como funciona: el usuario ha colocado tres caracteres en el flujo de entrada. El primer enunciado cin extrae solamente el primer carácter (‘A’) y el subsecuente enunciado cout repite el carácter en la pantalla. Como resultado, el carácter ‘A’ se extrae del flujo, quedando los caracteres ‘B’ y ‘C’. El segundo enunciado cin extrae del flujo el segundo carácter (‘B’), dejando el carácter ‘C’ en el flujo. El siguiente enunciado cout repite ‘B’ en la pantalla. Por último, el tercer enunciado cin extrae el último carácter (‘C’) del flujo. Este carácter se repite entonces en la pantalla.

Caso 3: El usuario escribe: A B C↵↵ El sistema muestra: A B

C

Observe que hay espacios en blanco entre los caracteres ‘A’, ‘B’ y ‘C’. Este espacio en blanco es ignorado por el operador >>. De esta manera, el compilador todavía hace las asignaciones correctas a las variables respectivas.

Caso 4:

El usuario escribe: 75 92 88↵↵ El sistema muestra: 7 5 9

Page 12: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-12

En este caso, el usuario ha escrito, en una sola línea, tres calificaciones de examen con números y no con letra. Sin embargo, dado que las variables están definidas como objeto carácter, el sistema trata los dígitos como caracteres durante la operación de lectura; ve a cada dígito de un número como un carácter por separado. De esta manera, asigna el carácter ‘7’ a calificacion1, el carácter ‘5’ a calificacion2 y el carácter ‘9’ a calificacion3 . Los datos restantes (2 88) no son extraídos del flujo por los tres enunciados cin, sino que permanecen en la memoria temporal de flujo. Por supuesto cualquier enunciado cin posterior extraerá todos o parte de los datos restantes. Lo que es importante recordar es utilizar siempre variables numéricas (entero o punto flotante) para leer datos numéricos. Como se ha visto, los datos pueden corromperse con facilidad cuando se usan variables de carácter para leer datos numéricos.

Caso 5: El usuario escribe: 97.5 73 84↵↵ El sistema muestra: 9 7 .

Una vez más el usuario ha escrito tres calificaciones numéricas, las cuales son tratadas como datos de carácter por el programa. De esta manera, se asignan los primeros tres caracteres y la información restante se deja en la memoria temporal de flujo de entrada. Como puede ver en la repetición, el carácter ‘9’ se asigna a calificacion1 , el carácter ‘7’ a calificacion2 y el punto decimal se asigna a calificacion3.

USO DE LA FUNCIÓN MIEMBRO get() PARA LEER DATOS DE UN SOLO Carácter La función miembro get(), sin argumento introduce un carácter desde el flujo designado (aunque sea espacio en blanco) y devuelve dicho carácter como valor de la llamada de la función. Esta versión de get devuelve EOF cuando se llega al final del archivo de flujo. Habrá ocasiones en que quiera congelar la pantalla para que el usuario tome alguna acción, como presionar la tecla ENTER (↵↵) Se puede lograr insertando una llamada a la función get() dentro de su programa junto con una instrucción adecuada, en el punto donde quiera congelar la salida. El código C++ que se necesita para realizar esta tarea es el siguiente:

cout << “¡HOLA RAZA! Presione la tecla ENTER para continuar” << endl; cin.get(); cout << “¡BUENA SUERTE! << endl;

Con este código se muestra el mensaje ¡HOLA RAZA! Presione la tecla ENTER para continuar y la ejecución del programa se detiene con el enunciado cin.get() hasta que el usuario oprima la tecla ENTER. El enunciado cin.get() lee el CRLF que produce la tecla ENTER y la ejecución del programa continúa, mostrando el mensaje ¡BUENA SUERTE!

Note que no se da ningún argumento para get() Esto está bien, porque no necesitamos almacenar la entrada CRLF. La función miembro get() con un argumento de carácter introduce el siguiente carácter del flujo de entrada (aunque sea un carácter de espacio en blanco) y lo almacena en el argumento de carácter. Esta versión de get() devuelve 0 (false) cuando se encuentra el fin de archivo; en

Page 13: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-13

caso contrario devuelve una referencia al objeto istream para el cual se llamó la función miembro get() Ejemplo 7.8

El siguiente programa: CIN4.CPP, ilustra el operador de extracción de flujo devolviendo false (0) al encontrar el fin de archivo.

Ejemplo 7.9

El siguiente programa: EOFGET.CPP, muestra el uso de las funciones miembro eof() y get() sobre el flujo de entrada cin y la función miembro put sobre el flujo de salida cout.

/* El siguiente programa: CIN4.CPP , ilustra el uso del operador de extracción de flujo devolviendo false al encontrar el fin de archivo. */ #include <iostream.h> // Para cout y cin void main(void) {

int calificacion, maximaCalificacion = -1; cout << “Introduzca la calificacion (fin-de-archivo para terminar): ” ; while(cin >> calificacion)

{ if(calificacion > maximaCalificacion)

maximaCalificacion = calificacion; cout << “Introduzca la calificacion (fin-de-archivo para terminar): ” ;

}//Fin del while

cout << “\n\nLa calificación más alta es: ” << maximaCalificacion << endl; }//Fin de main()

/* El siguiente programa: EOFGET.CPP , muestra el uso de las funciones miembro get(), put() y eof() */ #include <iostream.h> //Para cout y cin void main(void)

{ char c; cout << “Antes de introducir datos cin.eof() es ” << cin.eof() << “\nIntroduzca una oración seguida del fin-de-archivo:\n”; while((c = cin.get()) != EOF)

cout.put(c);

cout << “\nEOF en el sistema es: ” << c; cout << “\nDespués de introducir datos cin.eof() es ” << cin.eof() << endl;

}//fin de main()

Page 14: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-14

El programa primero imprime el valor de cin.eof(), es decir, false (0 en la salida) para mostrar que no ha sucedido el fin de archivo en cin. Después el usuario introduce una línea de texto y oprime Enter (↵↵) seguido por un fin de archivo (<ctrl>-z en sistemas compatibles con IBM PC, <ctrl>-d en sistemas UNIX o Macintosh) El programa lee cada carácter y lo manda a cout utilizando la función miembro put. Cuando se encuentra el fin de archivo, termina el while y cin.eof(), que ahora es true, se vuelve a imprimir (1 en la salida) para mostrar que el fin de archivo se estableció en cin. Observe que este programa utiliza la versión de la función miembro get de istream que no toma argumentos y devuelve el carácter que se introduce.

Una tercera versión de la función miembro get() toma tres argumentos: un arreglo de

caracteres, un límite de tamaño y un delimitador (con un valor predeterminado de ‘\n’) Esta versión lee caracteres desde el flujo de entrada. Lee hasta 1 menos que el número máximo especificado de caracteres y termina, o termina tan pronto como se lee el delimitador. Se inserta un carácter nulo para terminar la cadena de entrada en el arreglo de caracteres que el programa utiliza como búfer. El delimitador no se coloca en el arreglo de caracteres, pero permanece en el flujo de entrada ya que será el siguiente carácter que se lea. Por lo tanto, el resultado de un segundo get() consecutivo es una línea vacía, a menos que el carácter delimitador se vacíe del flujo de entrada. Ejemplo 7.10

El siguiente programa: COMCINGET.CPP, compara la entrada utilizando cin con la extracción de flujo (la cual lee caracteres hasta que se encuentra un carácter de espacio en blanco) contra la entrada cin.get() Observe que la llamada a cin.get() no especifica un carácter delimitador y, por lo tanto, se utiliza el predeterminado ‘\n’. Resumiendo : Llegará el momento en que necesite leer y almacenar espacios en blanco (espacios, tabuladores, líneas nuevas, retorno de carro, etc.) así como caracteres que no lo sean. La clase iostream incluye

/* El siguiente programa: COMCINGET.CPP, compara la entrada de una cadena mediante cin y cin.get(). */ #include <iostream.h> //Para cout y cin void main(void)

{ const int TAMANO = 80; char bufer1[TAMANO], bufer2[TAMANO]; cout << “Introduzca una oración:\n”; cin >> bufer1; cout << “\nLa cadena leída con cin fue:\n” << bufer1 << “\n\n”; cin.get(bufer2, TAMANO); cout <<”\nLa cadena leída con cin.get() fue:\n” << bufer2 << endl;

}//Fin de main()

Page 15: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-15

una función llamada get() para este propósito. La función get() extraerá cualquier carácter individual, incluyendo espacios en blanco, desde el flujo de entrada. A esta función se le llama usando el objeto cin a través del siguiente formato.

cin.get(variable de tipo carácter) Para llamar a la función get(), preceda cin con un punto, ., seguido por la función get() usando una variable de clase carácter como argumento. Cuando se le llama, get() extrae un carácter individual desde el flujo de entrada y lo asigna a la variable carácter listada como su argumento. En uno de los casos considerados anteriormente para el uso del operador >>, el usuario escribe tres calificaciones separadas por espacios en blanco, como éstas:

A B C↵↵

¿Cuántos caracteres en total ocupan estos caracteres en la memoria temporal de flujo? Correcto, 6 caracteres. Hay tres caracteres distintos de espacios en blanco y tres caracteres de espacios en blanco. Para leer todos estos caracteres se necesitarán 6 llamadas get() como sigue:

cin.get(char1); cin.get(char2); cin.get(char3); cin.get(char4); cin.get(char5); cin.get(char6);

Por supuesto, esto supone que las variables, char1, ..., char6, han sido previamente definidos de clase carácter.

En contraparte con la función get() existe la función put() La función put() inserta un solo carácter dentro del flujo de salida y es llamada por el objeto cout como sigue:

cout.put(variable de tipo carácter)

Para mostrar los 6 caracteres que lee get() se llamará a put() seis veces, como sigue:

cout.put(char1); cout.put(char2); cout.put(char3); cout.put(char4); cout.put(char5); cout.put(char6);

Esto mostrará los seis caracteres conforme los escriba el usuario. De esta manera, la salida será:

A B C↵↵

Por supuesto, no se ve el carácter CRLF, pero éste deberá forzar el cursor a la siguiente líneas de la pantalla. Esto es prueba suficiente de que get() lee espacios en blanco así como caracteres que no son espacios en blanco. Como puede ver, la lectura de un solo carácter a la vez impone una limitación severa sobre la entrada de datos de tipo carácter. Se necesita una variable separada para cada carácter individual

Page 16: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-16

que se lee. Debido a que la mayoría de la información de caracteres del mundo real aparece en forma de cadenas, necesitaremos una forma para leer cadenas.

LECTURA DE CADENA DE CARACTERES Ejemplo 7.11

El siguiente programa CADENAS1.CPP, ilustra las dificultades que se tienen cuando trata de usar el objeto cin con el operador >> para leer cadenas de caracteres.

Este programa define nombre como un arreglo de caracteres cuyo tamaño se especifica por la constante TAMANO. La constante TAMANO tiene un valor de 31, lo cual permite al usuario escribir un nombre de hasta 30 caracteres dejando espacio para el carácter nulo ‘\0’. Después de la definición, se le pide que escriba su nombre y apellidos mediante un enunciado cout y el nombre y apellidos se leen por medio del enunciado cin. La entrada se repite en la pantalla por medio de otro enunciado cout. A continuación veremos como funciona el programa:

El usuario escribe: Miguel Ángel Toledo Martínez↵↵ El sistema muestra: Miguel

¿Qué le pasó al segundo nombre de Miguel y a los apellidos? Bueno, cuando se leen cadenas de caracteres, el operador >> termina la operación de lectura cuando encuentra cualquier espacio en blanco. De esta manera, el arreglo de caracteres nombre sólo incluyó la cadena Miguel. Si pudiéramos inspeccionar el arreglo de caracteres nombre en la memoria, veríamos lo siguiente:

[0] ‘M’

[1] ‘i’ [2] ‘g’ [3] ‘u’ [4] ‘e’ [5] ‘l’ [6] ‘\0’ [7] ‘\0’ . . (terminadores nulos)

/* El siguiente programa: CADENAS1.CPP, ilustra como cin lee las cadenas de caracteres usando el operador >>. */ #include <iostream.h> //Para cout const int TAMANO = 31; // Declara el tamaño del arreglo. void main(void) { char nombre[TAMANO] = "\0"; // Define el arreglo de caracteres cout << "Escriba su nombre y apellidos: "; cin >> nombre; cout << "Su nombre y apellidos son : " << nombre << endl; } //Fin de main()

Page 17: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-17

. [30]

Como puede ver, las posiciones del arreglo [0] a [5] contienen la cadena Miguel formada por los caracteres individuales M, i, g, u, e, l. La posición del arreglo [6] contiene el carácter terminador nulo ‘\0’. El operador >> inserta el terminador nulo en el arreglo y termina la operación de lectura cuando encuentra el carácter blanco (espacio en blanco) De esta manera, los demás caracteres que se ingresaron no son extraídos del flujo, como se muestra por las posiciones restantes del arreglo que contienen los terminadores nulos de la inicialización. De hecho, si a ésta siguiera otro enunciado cin, deberá leer el segundo nombre de Miguel (Ángel), dado que el usuario escribió el nombre completo, coloca Ángel en la memoria temporal de flujo.

Hay algunas formas para resolver el problema anterior. Una de ellas es definir por separado arreglos de caracteres para cada una de las palabras que serán leídas. En este caso, podrá crear los siguientes arreglos: primerNombre, segudoNombre, apellidoPaterno y apellidoMaterno, y usar cuatro enunciados cin >> para leer el primero y el segundo nombre, los apellidos paterno y materno respectivamente.

USO DE getline() PARA LEER CADENAS DE CARACTERES La función miembro getline() opera en forma similar a la tercera versión de la función miembro get() e inserta un carácter nulo después de la línea en el arreglo de caracteres. La función getline() elimina del flujo al delimitador (es decir, lee el carácter y lo descarta), pero no lo almacena en el arreglo de caracteres. Ejemplo 7.12

El siguiente programa GETLINE.CPP, ilustra el uso de entrada mediante la función miembro getline() La función getline() se usa junto con cin más que con el operador >>. Esta función

permitirá que cin lea la cadena completa, incluyendo cualquier espacio en blanco.

cin.getline(<variable cadena>, <tamaño del arreglo>, <‘carácter delimitador’>)

/* El siguiente programa: GETLINE.CPP, da entrada a caracteres mediante la función miembro getline() */ #include <iostream.h> //Para cout y cin void main(void)

{ const TAMANO = 80; char bufer[TAMANO]; cout << “Introduzca una oración:\n”; cin.getline(buffer, TAMANO), cout << “\nLa oración introducida es:\n ” << bufer << endl;

}//Fin de main()

Page 18: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-18

La función getline() usa tres argumentos. El primero es el identificador de la variable clase char. Este es el nombre del arreglo de caracteres que se define para almacenar la cadena. El segundo es el tamaño del arreglo dentro de la cual se leerá la cadena. Recuerde, el tamaño de la cadena más grande que se leerá dentro de este arreglo es en realidad uno menos que el tamaño del arreglo, con objeto de dejar espacio para el carácter del terminador nulo ‘\0’. La función getline() inserta automáticamente el terminador nulo como el último carácter de la cadena. Por último, el carácter delimitador le dice a la función getline() cuándo terminar la operación de lectura. El carácter delimitador termina la entrada de la cadena y no se almacenará como parte de la cadena. La función getline() lee los caracteres del arreglo, uno a uno, hasta que encuentre el delimitador específico. Una vez que encuentra el delimitador, la función lo extrae del flujo de entrada y lo descarta para que no se almacene como parte de la cadena. Si no se especifica ningún carácter delimitador, su valor predeterminado es el carácter de secuencia de escape ‘\n’ (CRLF) Ejemplo 7.13

El siguiente programa CADENAS2.CPP, es una mejora al programa CADENAS1.CPP, en la que se utiliza la función cin.getline()

El programa funciona como sigue:

El usuario escribe: Miguel Angel Toledo Martínez↵↵ El sistema muestra: Miguel Angel Toledo Martínez

La memoria se vería de la siguiente manera:

[0] ‘M’ [16] ‘e’ [1] ‘i’ [17] ‘d’ [2] ‘g’ [18] ‘o’ [3] ‘u’ [19] ‘ ’ [4] ‘e’ [20] ‘M’

/* El siguiente programa: CADENAS2.CPP, ilustra como cin lee las cadenas de caracteres usando la función miembro getline(). */ #include <iostream.h> //Para cout const int TAMANO = 31; // Declara el tamaño del arreglo. void main(void) { char nombre[TAMANO] = "\0"; // Define el arreglo de caracteres cout << "Escriba su nombre y apellidos: "; cin.getline(nombre, TAMANO); cout << "Su nombre completo es: " << nombre << endl; } //Fin de main()

Page 19: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-19

[5] ‘l’ [21] ‘a’ [6] ‘ ‘ [22] ‘r’ [7] ‘A’ [23] ‘t’ [8] ‘n’ [24] ‘í’ [9] ‘g’ [25] ‘n’ [10] ‘e’ [26] ‘e’ [11] ‘l’ [27] ‘z’ [12] ‘ ’ [28] ‘\0’ [13] ‘T’ [29] ‘\0’ [14] ‘o’ [30] ‘\0’ [15] ‘l’ [31] ‘\0’

Observe que el arreglo ahora conserva la cadena completa, incluyendo el espacio en blanco entre los nombres y apellidos. También note que no se especifica ningún carácter delimitador para la función getline() Como resultado, la tecla ENTER (CRLF) termina la operación. Sin embargo, el carácter CRLF (‘\n’) no se almacena en el arreglo como parte de la cadena. Este carácter fue extraído del flujo y descartado por la función getline()Veamos un ejemplo más de uso del getline()

Ejemplo 7.14

El siguiente programa: DIREC.CPP, lee y escribe el nombre y la dirección del usuario.

/* El siguiente programa: DIREC.CPP, lee y escribe los datos del usuario: Nombre, dirección y número telefónico. */ #include <iostream.h> // Para cout const TAMANO = 31 // Tamaño del arreglo void main(void) { // Definición de las cadenas de caracteres char nombre[TAMANO] = "\0"; char calle[TAMANO] = "\0"; char ciudad[21] = "\0"; char estado[12] = "\0"; char codPostal[11] = "\0"; char telefono[14] = "\0"; // Pide al usuario y muestra las cadenas leídas cout << "Escriba su nombre y apellidos : "; cin.getline(nombre,TAMANO); cout << "Escriba el nombre de su calle : "; cin.getline(calle,TAMANO); cout << "Escriba el nombre de su ciudad: "; cin.getline(ciudad,21); cout << "Escriba el nombre de su estado: "; cin.getline(estado,12);

Page 20: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-20

Primero observe que cada una de las cadenas se han definido como un arreglo de caracteres. Se ha definido cada longitud de los arreglos para que sea un carácter más grande que lo necesario. Esto es para dejar espacio para el carácter del terminador nulo ‘\0’.

UN POSIBLE PROBLEMA CUANDO SE USA getline() Aunque getline() trabaja cuando se leen cadenas de caracteres en forma consecutiva, tendrá problemas al tratar de usarla para leer una cadena de caracteres después de haber usado cin para leer una variable de tipo carácter o una variable numérica. Ejemplo 7.15

El siguiente programa: CINGET2.CPP, ilustra lo que ocurre cuando se lee una variable de cadena de caracteres llamada nombre, después de haber leído la variable entera numero.

cout << "Escriba su código postal : "; cin.getline(codPostal, 11); cout << "Escriba su número telefónico : "; cin.getline(telefono, 14); // Muestra las cadenas de nombre y dirección cout << endl << endl; cout << "SU NOMBRE COMPLETO Y DIRECCION ES LA SIGUIENTE" << endl; cout << nombre << endl; cout << calle << endl; cout << ciudad << ", " << estado << " C.P. " << codPostal << endl; cout << "Número Telefónico: " << telefono << endl; } //Fin de main()

/* El siguiente programa: CINGET2.CPP, ilustra el problema de usar cin.getline() para leer una cadena de caracteres después de que ha leído una variable numérica */ #include <iostream.h> // Para cout const int TAMANO = 31; // Define el tamaño del arreglo void main(void) { // Definición de variables de entrada int numero = 0; char nombre[TAMANO] = "\0"; // Pregunta al usuario y lee/muestra los datos ingresados cout << "Escriba un entero : "; cin >> numero; cout << "El número que introdujo fué: " << numero << endl << endl; cout << "Escriba un nombre : "; cin.getline(nombre,TAMANO); cout << "El nombre que introdujo fué: " << nombre << endl; } //Fin de main()

Page 21: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-21

Cuándo se ejecuta este programa, parece como si C++ saltara sobre el enunciado cin.getline() ¿Cómo puede ser esto? Bien, si vemos el contenido de numero y nombre esto es lo que veríamos (suponiendo que se introdujo el entero 123):

numero

int 123 nombre

[0] ‘\0’ [1] ‘\0’

Observe que la variable de cadena nombre tiene el terminador nulo en la posición [0] del arreglo porque cuando se escribe el número 123, se debe oprimir la tecla ENTER (↵↵) Esto coloca un carácter CRLF en la memoria temporal de flujo. Sin embargo, el enunciado cin>>numero no extrae el carácter CRLF (porque es un espacio en blanco) y permanece en la memoria temporal. Cuando se ejecuta el enunciado cin.getline(nombre,TAMANO), lee la memoria temporal y ve el carácter CRLF. Como el valor predeterminado del carácter delimitador es justamente CRLF, la función termina de ejecutarse y agrega el carácter terminador nulo en el arreglo. De este modo, el usuario nunca tiene la oportunidad de ingresar un nombre.

Existen tres formas básicas de resolver el problema anterior. Una de ellas es especificar un carácter delimitador diferente en la función getline() Sin embargo, el usuario deberá escribir este carácter para terminar la operación, con las consecuentes molestias.

Una segunda forma es limpiar la memoria temporal leyendo el carácter CRLF dentro de una variable temporal después de cualquier dato numérico o carácter y antes de leer cualquier cadena de datos. Para hacer esto, deberá definirse una variable temporal como arreglo de dos caracteres, por ejemplo, temporal[2]

Después de usar cin para leer cualquier dato numérico o de un solo carácter emplearemos el enunciado cin.getline(temporal,2) para leer el caracteres CRLF restante en la memoria temporal del teclado, de esta manera se limpia la memoria temporal.

Ejemplo 7.16

El siguiente programa: CINGET3.CPP, modifica el programa CINGET2.CPP, empleando una variable temporal.

/* El siguiente programa: CINGET3.CPP, ilustra como usar una variable temporal para limpiar la memoria temporal (buffer) del teclado, después de haber leído una variable numérica. */ #include <iostream.h> // Para cout const int TAMANO = 31; // Define el tamaño del arreglo void main(void) {

Page 22: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-22

Un tercer método es emplear el manipulador de E/S ws (espacio en blanco – whitespace) para leer cualquier espacio en blanco antes de getline() El manejador ws extrae cualquier espacio en blanco que haya quedado en la memoria temporal de entrada. Para usarlo, simplemente coloque en su programa un enunciado cin >> ws antes del enunciado cin.getline.

Ejemplo 7.17

El siguiente programa: CINGET4.CPP, ilustra el uso de ws .

El enunciado cin >> ws extrae cualquier espacio en blanco CRLF dejado en la memoria temporal del flujo de entrada para que la función getline() no termine su ejecución antes de leer la cadena. Este manejador ws se incluye como parte del archivo de encabezado iostream.h.

// Definición de variables de entrada int numero = 0; char temporal[2] = "\0"; char nombre[TAMANO] = "\0"; // Pregunta al usuario y lee/muestra los datos ingresados cout << "Escriba un entero : "; cin >> numero; cout << "El número que introdujo fué: " << numero << endl << endl; cin.getline(temporal,2); cout << "Escriba un nombre : "; cin.getline(nombre,TAMANO); cout << "El nombre que introdujo fué: " << nombre << endl; } // Fin de main()

/* El siguiente programa: CINGET4.CPP, ilustra el uso de ws para limpiar los espacios en blanco de la memoria temporal del teclado antes de usar getline() para leer los datos tipo cadena. */ #include <iostream.h> // Para cout const int TAMANO = 31; // Define el tamaño del arreglo void main(void) { // Definición de variables de entrada int numero = 0; char nombre[TAMANO] = "\0"; // Pregunta al usuario y lee/muestra los datos ingresados cout << "Escriba un entero : "; cin >> numero; cout << "El número que introdujo fue: " << numero << endl << endl; cout << "Escriba un nombre : "; cin >> ws; cin.getline(nombre,TAMANO); cout << "El nombre que introdujo fue: " << nombre << endl; } //Fin de main()

Page 23: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-23

USO DE gets() Y fgets() PARA LEER CADENAS Hay una serie de funciones que manejan la E/S de cadenas en C y C++. Dos de estas funciones se listan en la tabla.7.1. Estas funciones se proporcionan en el archivo de encabezado stdio.h. De hecho, hay muchas funciones de E/S en este archivo, pero en este curso solo necesitaremos emplear las funciones de E/S para cadenas. Las funciones gets() y fgets() solucionarán el problema que tenemos con getline()

Tabla 7.1. Funciones de E/S de cadenas en C y C++

Función Descripción gets() Lee la cadena de entrada. Convierte el CRLF en un terminador nulo. fgets() Lee la cadena de entrada. Lee el CRLF y le adiciona un terminador nulo

Para usar gets() y fgets(), deberá incluir el archivo de cabecera stdio.h y pasar uno o mas argumentos a la función respectiva. Este es el formato para ambas funciones:

gets(<variable cadena>); fgets(<variable cadena>, <tamaño del arreglo>, stdin);

La función gets() sólo necesita que el identificador de la variable cadena pase a la función. La función fgets() necesita el identificador de la cadena, el tamaño del arreglo de caracteres y la palabra stdin. En la tabla 7.1 se puede ver que gets() convertirá el carácter CRLF (producido por la tecla ENTER) en un terminador nulo. De esta manera, ningún CRLF se almacenará en la cadena con gets() Sin embargo, fgets() lee el carácter CRLF almacenándolo en el arreglo de caracteres y adiciona el terminador nulo. Como resultado, los caracteres CRLF y el terminador nulo siempre ocuparán al menos 2 bytes de la cadena. Ejemplo 7.18

El siguiente programa: GETSFGETS.CPP, ilustra el uso de las funciones gets() y fgets()

Page 24: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-24

Suponga que se escriben las siguientes entradas cuando se ejecuta el programa:

Escriba su nombre: Miguel Angel Escriba su dirección: Ciudad C++

Los contenidos de las variables serían:

nombre [0] ‘M’ [6] ‘ ’ [12] ‘\0’ [1] ‘i’ [7] ‘A’ [2] ‘g’ [8] ‘n’ [3] ‘u’ [9] ‘g’ [4] ‘e’ [10] ‘e’ [5] ‘l’ [11] ‘l’ dirección

[0] ‘C’ [6] ‘ ’ [1] ‘i’ [7] ‘C’ [2] ‘u’ [8] ‘+’ [3] ‘d’ [9] ‘+’ [4] ‘a’ [10] ‘\n’ [5] ‘d’ [11] ‘\0’

Observe que nombre no incluye el carácter CRLF, el cual sí está en direccion. Siempre podrá usar cout para escribir datos de cadenas. El objeto cout escribirá todos los caracteres almacenados en la cadena hasta que encuentre el terminador nulo. De esta manera, no se generará ningún CRLF si se lee una cadena usando gets() y posteriormente se escribe usando cout. Sin embargo, la misma cadena que se lee usando fgets() generará un CRLF cuando después se escriba con cout.

EXAMEN BREVE 17

/* El siguiente programa: GETSFGETS.CPP, ilustra el uso de gets() y fgets() */ #include <iostream.h> // Para cout #include <stdio.h> // Para gets() y fgets() const int TAMANO = 31; // Define el tamaño del arreglo void main(void) { // Define arreglos de cadenas char nombre[TAMANO] = "\0"; char direccion[TAMANO] = "\0"; // Instrucción y lectura de datos del usuario cout << "Escriba un nombre : "; gets(nombre); cout << "El nombre que introdujo fué : " << nombre << endl << endl; cout << "Escriba su dirección : "; fgets(direccion, TAMANO, stdin); cout << "La dirección que introdujo fue: " << direccion << endl; } //Fin de main()

Page 25: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-25

LAS FUNCIONES MIEMBRO peek(), putback() e ignore() DE istream

La función miembro ignore() se salta un número designado de caracteres (el número predeterminado es un carácter) o termina al encontrar un delimitador designado (el delimitador predeterminado es EOF el cual causa que ignore() salte al final del archivo cuando está leyendo de un archivo)

La función miembro putback() coloca el carácter obtenido previamente por un get() del

flujo de entrada de vuelta en dicho flujo. Esta función es útil para aplicaciones que revisan un flujo de entrada buscando un campo que comience con un carácter específico. Cuando se recibe ese carácter, la aplicación lo devuelve al flujo para que pueda incluirse en los datos que serán introducidos.

La función miembro peek devuelve el siguiente carácter de un flujo de entrada, pero no

elimina el carácter del flujo.

E/S A PRUEBA DE TIPOS C++ proporciona E/S a prueba de tipos. Los operadores << y >> están sobrecargados para aceptar elementos de datos de tipos específicos. Si se procesa un dato inesperado, se establecen diversos indicadores de error con los cuales el usuario puede probar para determinar si una operación de E/S terminó satisfactoriamente o si falló. De esta forma el programa conserva el control . PROCESAMIENTO DE ARCHIVOS LA JERARQUÍA DE DATOS

Al final de cuentas, todos los elementos de datos procesados por computadoras digitales se reducen a combinaciones de ceros y unos. Esto ocurre debido a que resulta simple y económico construir dispositivos electrónicos que pueden asumir dos estados estables –uno representa a 0 y el otro representa a 1. Es de admirar que las funciones impresionantes que realizan las computadoras involucren solamente las manipulaciones fundamentales de ceros y unos. El elemento de datos más pequeño en una computadora puede asumir el valor 0 o el valor 1. A tal elemento de datos se le llama bit (abreviatura de dígito binario, un dígito que sólo puede asumir uno de dos valores) El sistema de circuitos de la computadora realiza diversas manipulaciones de bits simples, tales como examinar el valor de un bit, establecer el valor de un bit e invertir un bit ( de 1 a 0 o de 0 a 1) Para los programadores es problemático trabajar con los datos en la forma de bajo nivel de bits. En vez de ello, los programadores prefieren trabajar con los datos en formas tales como dígitos decimales (esto es, 0, 1, 2, 3, 4, 5, 6, 7, 8 y 9), letras (es decir, A a la Z y de la a a la z) y símbolos especiales (esto es $, @, %, &,, *, -, +, “, :, ?, / y muchos otros). A los dígitos, letras y símbolos especiales se les conoce como caracteres. Al conjunto de todos los caracteres que se utilizan para escribir programas y representar elementos de datos en una computadora particular

Page 26: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-26

se le conoce como el conjunto de caracteres de la computadora. Debido a que las computadoras sólo pueden procesar unos y ceros, cada carácter del conjunto de caracteres de la computadora está representado como una secuencia de unos y ceros (llamada byte) Los bytes se componen comúnmente de 8 bits. Los programadores crean programas y elementos de datos con caracteres, y las computadoras manipulan y procesan estos caracteres como patrones de bits. Así como los caracteres están compuestos de bits, los campos están compuestos de caracteres (o bytes) Un campo es un grupo de caracteres que tienen significado. Por ejemplo, un campo que consista solamente de letras mayúsculas y minúsculas pueden utilizarse para representar el nombre de una persona. Los elementos de datos que son procesados por las computadoras forman una jerarquía de datos, en la cual los elementos de datos llegan a ser más grandes y más complejos en estructura conforme avanzamos de bits a caracteres (o bytes), a campos, y así sucesivamente. Un registro (es decir, una struct o una class en C++) está compuesto de varios campos (llamados miembros en C++) Por ejemplo, en un sistema de nómina un registro para un empleado particular puede consistir de los siguientes campos:

1. Número de identificación del empleado. 2. Nombre. 3. Dirección. 4. Salario por hora. 5. Número de exenciones. 6. Ingresos acumulados. 7. Cantidad de impuestos federales retenidos, etcétera.

Por lo tanto, un registro es un grupo de campos relacionados. En el ejemplo anterior, cada

uno de los campos pertenece al mismo empleado. Por supuesto, una compañía particular puede tener muchos empleados, y tendrá un registro de nómina por cada empleado. Un archivo es un grupo de registros relacionados. Un archivo de nómina de una compañía contiene normalmente un registro para cada empleado. Por lo tanto, un archivo de nómina para una compañía pequeña podría contener sólo 22 registros y, en cambio, un archivo de nómina para una compañía grande podría contener 100,000 registros. No es poco usual que una compañía tenga muchos archivos, y que cada uno contenga millones de caracteres de información. La figura 7.3 ilustra la jerarquía de datos.

Page 27: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-27

Miguel Negro

José Azul

Judith Verde

Iris Naranja

Raúl Rojo

Judith Verde Judith Campo 01001010 Byte (carácter ASCII J) 1 Bit

Figura 7.3. La jerarquía de datos.

Para facilitar la recuperación de registros específicos de un archivo, al menos un campo

de cada registro se escoge como clave del registro. Una clave de registro especifica que un registro pertenece a una persona o entidad particular que es única con respecto a todos los demás registros del archivo. En el registro de nómina descrito anteriormente, normalmente el número de identificación del empleado es el que se elige como clave del registro.

Hay muchas formas para organizar los registros en un archivo. El tipo más común de

organización se llama archivo secuencial y en él los registros se almacenan típicamente en orden de acuerdo al campo de clave del registro. En un archivo de nómina los registros se colocan, por lo general, en orden por número de identificación de empleado. El primer registro de empleado del archivo contiene el número más pequeño de identificación del empleado, y los registros subsecuentes contienen números de identificación de empleado cada vez más grandes.

La mayoría de los negocios utilizan muchos archivos diferentes para almacenar datos. Por ejemplo, las compañías pueden tener archivos de nómina, archivos de cuentas por cobrar (que listan dinero que deben los clientes), archivos de cuentas por pagar (que listan el dinero que se debe a los proveedores), archivos de inventario (que tienen hechos acerca de los artículos que maneja el negocio) y muchos otros tipos de archivos. A un grupo de archivos relacionados a

Archivo

Archivo Registro

Page 28: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-28

veces de le llama base de datos. A un conjunto de programas que está diseñado para crear y manejar bases de datos se le llama DBMS (Sistema de administración de bases de datos) ARCHIVOS Y FLUJOS C++ ve a cada archivo simplemente como una secuencia de bytes (figura 7.4) Todo archivo termina ya sea con un marcador de fin de archivos o con un número de byte específico que está registrado en una estructura de datos administrativa mantenida por el sistema. Cuando se abre el archivo, se crea un objeto y se asocia un flujo con ese objeto. En otra lección hemos visto que existen cuatro objetos que se crean automáticamente para nosotros: cin, cout, cerr y clog. Los flujos asociados con ellos proporcionan canales de comunicación entre un programa y un archivo o dispositivo particular. Por ejemplo, el objeto cin (el objeto de flujo de entrada estándar) permite que un programa reciba datos desde el teclado; el objeto cout (el objeto de flujo de salida estándar) permite que un programa envíe datos a la pantalla, y los objetos cerr y clog (objetos de flujo de error estándar) permiten que un programa envíe mensajes de error a la pantalla.

0 1 2 3 4 5 6 7 8 9 ... n - 1 Marcador

de fin de archivo

Figura 7.4. La vista de C++ de un archivo de n bytes.

Para realizar el procesamiento de archivos en C++ se deben incluir los archivos de encabezado <iostream.h> y <fstream.h>. <fstream.h> incluye las definiciones para las clases de flujo ifstream (para entrada desde un archivo), ofstream (para salida hacia un archivo) y fstream (para entrada y salida de un archivo) Los archivos se abren mediante la creación de objetos de estas clases de flujo, las cuales se derivan de (es decir, heredan la funcionalidad de) las clases istream, ostream e iostream, respectivamente. Por lo tanto, todas las funciones miembro, operadores y manipuladores que se han descrito en la lección 5 (COMO ENVIAR MENSAJES A LA PANTALLA) y la primera parte de esta lección, también pueden aplicarse a los flujos de archivo. En la figura 7.5 se resumen las relaciones de herencia de las clases de E/S tratadas hasta este momento.

ios istream ostream ifstream iostream ofstream fstream

Figura 7.5. Parte de la jerarquía de las clases de E/S de flujo.

Page 29: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-29

CLASES: LA BASE DE LOS ARCHIVOS C++ Los objetos cin y cout que ha usado en sus programas para entrada desde el teclado y salida a la pantalla son objetos de la clase iostream. Cómo estará de acuerdo, los objetos cin y cout invocan flujos de archivos predefinidos. De esta manera, decimos que la entrada (input) estándar se lee desde el flujo cin y la salida (output) estándar se escribe al flujo cout. Cuando se incluye el archivo de encabezado iostream.h en el programa, los flujos de archivo cin y cout se definen en forma automática. Por supuesto, los únicos archivos a los que puede tener acceso en forma conveniente con cin y cout son los archivos de teclado y pantalla que están unidos a estos flujos de archivos. Cuando elabore su propio flujo de archivos para leer/escribir archivos a disco, lo primero que debe hacer es definir un objeto para una de las clases de archivo. Los objetos del flujo de archivos que se usan exclusivamente para entrada se definen como objetos de la clase ifstream. De esta manera, el enunciado:

ifstream entrada;

define a entrada como un objeto del flujo de archivo de entrada. Use la clase ofstream para definir objetos del flujo de archivo que se usen exclusivamente para la salida. De esta manera, el enunciado:

ofstream salida;

define salida como un objeto del flujo de archivo de salida. Por último, deberá usar la clase fstream cuando defina objetos que se utilizarán para la entrada y salida de archivos.

El enunciado:

fstream entradaSalida;

define entradaSalida como un objeto del flujo de archivos para entrada y salida. Después, deberá unir o conectar el objeto del flujo de archivos a un archivo físico del disco. Cuando un objeto de flujo de archivos está unido a un archivo físico del disco, el archivo en disco se abre para ser accesado. Para ello se necesita la función open(), que es heredada por todas las clases del flujo de archivo. Aquí esta el formato necesario para llamar esta función:

<objeto de flujo de archivo>.open (<nombre del archivo en disco>, <modo de apertura de archivo>); Lo primero que debe especificar es el objeto del flujo de archivos. El nombre del objeto es seguido por un punto y después por la función open() y sus argumentos necesarios. Este enunciado simplemente llama la función open() definida en la clase del flujo de archivos respectivo. La función open() tiene dos argumentos: un nombre del archivo en disco y un designador del modo de apertura. El nombre del archivo en disco se debe ajustar a los requerimientos del

Page 30: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-30

sistema operativo. Para el sistema DOS, el nombre del archivo no puede exceder de ocho caracteres. Una extensión de tres caracteres, separados por un punto del nombre del archivo, es opcional. De esta manera, nombres de archivos de DOS como ejemplo, ejemplo.dat y ejemplo1.dat son todos nombres legales para archivos. El nombre del archivo en el disco físico se puede especificar directamente dentro de dobles comillas (por ejemplo “ejemplo.dat”) o en forma indirecta como una variable tipo cadena. El argumento indicador del modo de apertura define que tipo de acceso se realizará dentro del archivo. Aunque hay más, por ahora sólo nos interesan los indicadores ios::in e ios::out. Suponga que queremos abrir un flujo de archivos llamado entradaSalida. El flujo de archivos se define por la clase fstream y será asignado a un archivo en disco llamado prueba.dat. Además, el programa leerá y escribirá en el archivo. El enunciado de apertura adecuado será:

entradaSalida.open(“prueba.dat”, ios :: in | ios :: out);

El (los) modo(s) de lectura/escritura siempre debe(n) especificarse cuando un objeto de flujo de archivos se define con la clase fstream, pues por definición, esta clase se usa para ambos accesos a archivos de entrada y salida (lectura/escritura) Los modos lectura/escritura no necesitan especificarse cuando se abren archivos definidos por las clases ifstream u ofstream, porque los archivos son predeterminados como entrada y salida respectivamente. Por ejemplo, si se define salida como un objeto de flujo de archivo de la clase ofstream, el enunciado de apertura simplemente será:

salida.open(“prueba.dat”);

por otra parte, si entrada se define como un objeto de flujo de archivo de la clase ifstream, el enunciado de apertura será:

entrada.open(“prueba.dat”); Ejemplo 7.19 Escriba los enunciados para crear los siguientes archivos en disco:

a. Un flujo de archivos llamada lectura que lea de un archivo de disco llamado ejemplo.doc. b. Un flujo de archivo llamado escritura que escriba en un archivo de disco llamado ejemplo.doc. c. Un flujo de archivo llamado lecturaEscritura que lea y escriba un archivo de disco llamado

ejemplo.doc.

Solución: a. ifstream lectura;

lectura.open(“ejemplo.doc”); b. ofstream escritura;

escritura.open(“ejemplo.doc”); c. fstream lecturaEscritura;

lecturaEscritura.open(“ejemplo.doc”, ios::in | ios::out);

Page 31: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-31

Una vez que se abre un archivo, estará listo para el procesamiento. Después de terminar el procesamiento del archivo, deberá cerrar el archivo. Esto se realiza con la función close() Para cerrar un archivo, lo que necesita hacer es llamar la función close() con su objeto de flujo de archivo usando el operador punto. Como resultado, los enunciados salidaEntrada.close(), salida.close() y entrada.close() deberán cerrar los archivos que se abrieron en la explicación anterior. LECTURA Y ESCRITURA DE UN ARCHIVO EN DISCO Se lee desde un archivo en disco con el objeto de flujo de archivo de entrada y el operador de extracción >>, justo como se usa el objeto de flujo de archivo estándar cin y el operador >>. Por ejemplo, suponga que hemos abierto un archivo de entrada, como éste:

ifstream entrada; entrada.open(“prueba.dat”);

El objeto de flujo de archivo que creamos se llama entrada. Para obtener una cadena de datos desde este archivo, todo lo que necesitamos hacer es aplicar el operador >> para nuestro objeto entrada, como éste:

entrada >> cadena; Por supuesto, este enunciado supone que cadena se definió como un arreglo de caracteres. Escriba un archivo de disco usando su objeto de flujo de archivo de salida y el operador de inserción <<, justo como usó el objeto de flujo de archivo estándar cout y el operador <<. De esta manera, si abrimos un objeto de archivo de salida, como éste:

ofstream salida; salida.open(“prueba.dat”);

podemos escribir una cadena de datos a este archivo usando el enunciado:

salida << cadena << endl; De nuevo, se supone que la variable cadena se ha definido como una variable cadena o arreglo de caracteres. Ejemplo 7.20

El siguiente programa: DISCO1.CPP, copia tres cadenas de datos de un archivo en disco llamado entrada.dat a un archivo de salida llamado salida.dat.

Solución:

Una operación de copia de archivo requiere que se lea un archivo y que repita esa información de entrada a un segundo archivo. Leeremos el archivo de entrada entrada.dat y lo escribiremos al archivo de salida salida.dat. Primero, necesitamos abrir dos objetos archivo. Los nombraremos entrada y salida. Después, leeremos utilizando el objeto de archivo entrada y escribiremos

Page 32: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-32

usando el objeto de archivo salida. Por último, debemos cerrar ambos archivos. El código que se requiere es el siguiente:

Lo primero que debe notar en este programa es que el archivo de encabezado fstream.h se incluye con la declarativa del preprocesador #include. Este archivo de encabezado se debe incluir para usar las clases ifstream y ofstream necesarias para la E/S del archivo. Después se definen dos objetos de archivos para sus clases respectivas y los archivos se abren con la función open() El enunciado entrada >> cadena lee una cadena de datos desde el archivo de disco entrada.dat y la coloca en el arreglo de caracteres llamado cadena. Recuerde que el operador >> termina cuando encuentra un espacio en blanco. Como resultado, no se leerá ningún espacio en blanco que forme parte de una cadena determinada. ¿Cómo se leen los espacios en blanco como parte de una cadena? Al utilizar la función getline() en lugar del operador >> con su objeto de archivo de entrada. El enunciado será entrada.getline (cadena,TAMANO). Lo mismo se aplica cuando se leen cadenas desde un archivo en disco usando su propio objeto como cuando se leen cadenas desde el teclado usando el objeto cin. Una vez que se lee una cadena, el enunciado salida << cadena << endl copia la cadena al archivo en disco salida.dat. Observe que cada declaración de salida incluye un manejador endl para colocar un CRLF al final de cada línea en el archivo salida. Por último, ambos archivos se cierran al llamar la función close() con cada objeto archivo.

// El siguiente programa: DISCO1.CPP, copia tres cadenas de un archivo a otro #include <fstream.h> // Para archivos de E/S const int TAMANO = 25; // Tamaño de la cadena de caracteres void main(void) { // Define la variable de cadena char cadena[TAMANO] = "\0"; // Define los objetos del archivo y los archivos de entrada ifstream entrada; // Define el objeto de entrada ofstream salida; // Define el objeto de salida entrada.open("entrada.dat"); // Abre el archivo de entrada salida.open("salida.dat"); // Abre el archivo de salida // Copia los datos del archivo desde un archivo de entrada a un // archivo de salida entrada >> cadena; // Lee la primera cadena salida << cadena << endl; // Escribe la primera cadena entrada >> cadena; // Lee la segunda cadena salida << cadena << endl; // Escribe la segunda cadena entrada >> cadena; // Lee la tercera cadena salida << cadena << endl; // Escribe la tercera cadena entrada.close(); // Cierra el archivo de entrada salida.close(); // Cierra el archivo de salida } //Fin de main()

Page 33: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-33

Ejemplo 7.21

El siguiente programa: DISCO2.CPP, lee dos enteros desde un archivo denominado enteros y escriba la suma en un archivo de nombre suma.

En este caso, hemos definido tres variables enteras: dos para guardar los dos valores enteros leídos y una para dejar el resultado de la suma. Primero se definen los objetos archivos y se abren los dos archivos de disco. El objeto entrada lee los dos enteros desde el archivo enteros usando el operador >>. El objeto salida y el operador << calculan la suma y luego la escriben al archivo suma. Por últ imo, los dos archivos se cierran. ¡Esto es todo lo que tiene que hacer!

EL USO DE CICLOS PARA LEER Y PROCESAR ARCHIVOS Aunque todavía no estudia los ciclos, suele ser ventajoso usar un ciclo para leer un archivo de entrada para que no repita el enunciado de entrada para cada elemento de datos en el archivo.

/* El siguiente programa: DISCO2.CPP, lee dos enteros de un archivo y escribe la suma en otro archivo. */ #include <fstream.h> // Para archivos de E/S const int TAMANO = 25; // Tamaño de la cadena de caracteres void main(void) { // Define las variables int entero1 = 0; // Primera variable entera int entero2 = 0; // Segunda variable entera int suma = 0; // Variable suma // Define los objetos del archivo y los archivos de entrada ifstream entrada; // Define el objeto de entrada ofstream salida; // Define el objeto de salida entrada.open("enteros"); // Abre el archivo de entrada salida.open("suma "); // Abre el archivo de salida // Copia los datos del archivo desde un archivo de entrada a un // archivo de salida entrada >> entero1; // Lee el primer entero entrada >> entero2; // Lee la segundo entero suma = entero1 + entero2; // Calcula la suma salida << suma << endl; // Escribe la suma entrada.close(); // Cierra el archivo de entrada salida.close(); // Cierra el archivo de salida } //Fin de main()

Page 34: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-34

El formato general para un ciclo manejando archivos es:

<lee el primer elemento de los datos del archivo de entrada> while(!entrada.eof()) { // comienza ciclo < proceso de elementos de datos > <lectura de elementos de datos del archivo de entrada> }

Un enunciado debe preceder al ciclo while para leer el primer elemento de datos en el archivo entrada. Entonces el ciclo se codifica para leer los datos restantes en el archivo. El ciclo dice mientras no (!) encuentre la marca de fin de archivo (eof()), procese el actual elemento de datos y lea otro elemento de datos. Así cuando el ciclo se ejecuta, procesará y leerá los elementos de datos del archivo en forma repetida, uno por uno, hasta encontrar el marcador de fin de archivo. Todos los archivos en disco contienen una marca de fin de archivo, llamado EOF. Cuando se lee un archivo en disco, instruimos al compilador para buscar una marca de EOF y terminar las operaciones de lectura cuando lo encuentre. Esto se logra utilizando el ciclo while y la prueba para el marcador EOF usando la función estándar eof() Observe que los enunciados de procesamiento y lectura de archivos se colocan dentro del cuerpo del ciclo, que es enmarcado con las llaves ({ }) Ejemplo 7.22 El siguiente programa DISCO3.CPP, es otra versión del programa DISCO1.CPP, en donde se emplea un ciclo para copiar el archivo entrada.dat al archivo salida.dat.

// El siguiente programa: DISCO3.CPP, copia un archivo a otro. #include <fstream.h> // Para archivos de E/S const int TAMANO = 25; // Tamaño de la cadena de caracteres void main(void) { // Define la variable de cadena char cadena[TAMANO] = "\0"; // Define los objetos del archivo y los archivos de entrada ifstream entrada; // Define el objeto de entrada ofstream salida; // Define el objeto de salida entrada.open("entrada.dat"); // Abre el archivo de entrada salida.open("salida.dat"); // Abre el archivo de salida // Lee la primera cadena entrada >> cadena;

Page 35: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-35

Note que la operación de lectura y escritura del archivo están colocados dentro del ciclo. Esto es porque simplemente leemos un elemento de datos desde el archivo de entrada y lo repetimos al archivo de salida como nuestra operación de procesamiento. El ciclo leerá una cadena desde el archivo de entrada repetidamente, después escribirá la cadena al archivo de salida hasta que encuentre el marcador de fin de archivo dentro del archivo de entrada.

Otra ventaja de usar un ciclo para leer un archivo es que a menudo no se sabe cuántos elementos de datos contiene un archivo. Al utilizar un ciclo y verificar el marcador de EOF, no necesitará saber cuantos elementos de datos están en el archivo, porque el ciclo continuará leyendo el archivo hasta encontrar el final de éste.

CREACIÓN DE UN ARCHIVO DE ACCESO SECUENCIAL C++ no impone estructura sobre un archivo. Por lo tanto, en los archivos C++ no existen los conceptos como registro. Debido a esto, el programador debe estructurar los archivos para satisfacer los requerimientos de las aplicaciones. En el siguiente ejemplo vemos cómo el programador puede imponer una estructura de registro simple en un archivo. Primero presentamos el programa y luego lo analizamos a detalle. Ejemplo 7.23

El siguiente programa: SECUENCIAL.CPP, crea un archivo de acceso secuencial simple que puede utilizarse en un sistema de cuentas por cobrar para ayudar en la administración del dinero que deben los clientes a una compañía. Para cada cliente, el programa obtiene el número de cuenta, el nombre y el saldo del cliente (es decir, la cantidad que el cliente todavía debe a la compañía por bienes y servicios recibidos en el pasado) Los datos obtenidos para cada cliente constituyen un registro para ese cliente. En esta aplicación el número de cuenta se utiliza como clave del registro. Esto es, el archivo será creado y mantenido con relación al número de cuenta. Este programa asume que el usuario introduce registros en orden de número de cuenta. En un sistema de cuentas por cobrar completo se proporcionaría una capacidad de ordenamiento para que el usuario pueda dar los registros en cualquier orden, y éstos se ordenarían y escribirían en el archivo.

EXAMEN BREVE 18

// Copia los datos del archivo desde un archivo de entrada a un // archivo de salida usando un ciclo while (!entrada.eof()) { // Empieza el ciclo salida << cadena << endl; // Escribe una cadena para el archivo salida entrada >> cadena; // Lee una cadena desde el archivo entrada } // Cierra los archivos entrada.close(); // Cierra el archivo de entrada salida.close(); // Cierra el archivo de salida } //Fin de main()

Page 36: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-36

Ahora examinemos el programa. Como se dijo anteriormente, los archivos se abren al crear objetos de las clases de flujo ifstream, ofstrem, o fstream. En el programa SECUENCIAL.CPP se abrirá para salida, por lo que se crea un objeto ofstream. Se pasan dos argumentos al constructor del objeto el nombre archivo y el modo de apertura del archivo. Para un objeto ofstream el modo de apertura del archivo puede ser ios::out, para enviar datos a un archivo, o ios::app, para agregar datos al final de un archivo (sin modificar los datos que ya se encuentran en el archivo) Los archivos existentes que se abren con el modo ios::out se truncan, es decir, se descartan todos los datos que están en el archivo. Si el archivo especificado todavía no existe, se crea un archivo con ese nombre. La declaración:

ofstream archivoSalida(“clientes.dat”, ios::out);

crea un objeto ofstream llamado archivoSalida que está asociado con el archivo clientes.dat y que se abre para salida. Los argumentos “clientes.dat” e ios::out se pasan al constructor de ofsteam, el cual abre el archivo. Esto establece una línea de comunicación con el archivo. Los argumentos se pasan a la función constructora de ofstream, la cual abre el archivo. Los objetos ofstream se abren en forma predeterminada para salida, por lo que se habría podido utilizar la instrucción:

/* El siguiente programa: SECUENCIAL.CPP, crea un archivo secuencial .*/ #include <iostream.h> //Para cout y cin #include <fstream.h> //Para crear un objeto de E/S #include <stdlib.h> //Para exit() int main(void) { //El constructor ofstream abre un archivo ofstream archivoSalida("clientes.dat", ios::out); if(!archivoSalida) //Operador ! sobrecargado { cerr << "El archivo no pudo abrirse" << endl; exit(1); //Prototipo en stdlib.h }//Fin de if cout << "Introduzca el número de cuenta, nombre y balance.\n" << "introduzca fin-de-archivo para terminar.\n? "; int cuenta; char nombre[ 30 ]; float balance; while( cin >> cuenta >> nombre >> balance) { archivoSalida << cuenta << ' ' << nombre << ' ' << balance << '\n'; cout << "? "; }//Fin de while() return 0; //El destructor de ofstream cierra el archivo }//Fin de main()

Page 37: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-37

ofstream archivoSalida(“cliente.dat”);

para abrir a cliente.dat para salida. La figura 7.6 lista los modos de apertura de archivo.

Modo Descripción ios::app Escribe toda la salida al final del archivo. ios::ate Abre un archivo para salida y se mueve al final del archivo (normalmente se utiliza

para agregarle datos a un archivo) Los datos pueden escribirse en cualquier lugar del archivo.

ios::in Abre un archivo para entrada. ios::out Abre un archivo para salida. ios::trunc Descarta el contenido del archivo en caso de que exista (esto también es una acción

predeterminada para ios::out). ios::nocreate Si el archivo no existe, la operación de apertura falla. ios::noreplace Si el archivo existe, la operación de apertura falla.

Figura 7.6. Modos de apertura de archivos.

Es posible crear un objeto ofstream sin abrir un archivo específico, ya que posteriormente se puede asociar un archivo hacia el objeto. Por ejemplo, la declaración:

Ofstream.archivoSalida;

crea un objeto ofstream llamado archivoSalida. La función miembro open de ofstream abre un archivo y lo asocia con un objeto ofstream existente de la siguiente manera:

archivoSalida.open(“clientes.dat”, ios::out);

Después de crear un objeto ofstream y de intentar abrirlo, el programa prueba para ver si la operación de apertura fue satisfactoria. La condición de la estructura if:

if( !archivoSalida) { cerr << “El archivo no pudo abrirse” << endl; exit(1); }//Fin de if

utiliza la función miembro operator! Sobrecargada del operador ios para determinar si la operación de apertura fue satisfactoria. La condición devuelve un valor diferente de cero (true), si failbit o badbit están establecidos para el flujo en la operación open. Algunos errores posibles son el intento de abrir un archivo no existente para lectura, el intento de abrir un archivo para lectura sin tener permiso y la apertura de un archivo para escritura cuando no hay espacio de disco disponible. Cuando la condición indica que el intento de apertura no fue satisfactorio, se envía a la salida el mensaje de error El archivo no pudo abrirse y se llama a la función exit() para terminar el programa. El argumento de exit() se regresa al entorno desde el cual se llamó al programa. El argumento 0 indica que el programa terminó normalmente, y cualquier otro indica que el programa terminó a causa de un error. El valor que exit() devuelve lo utiliza el entorno invocador (por lo general, el sistema operativo) para responder adecuadamente ante el error. Otra función miembro sobrecargada del operador ios, operator void *, convierte el flujo a un apuntador para que pueda probarse como 0 (el apuntador nulo) o diferente de 0 (cualquier otro

Page 38: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-38

valor del apuntador) Si failbit o badbit están establecidos para el flujo, se devuelve 0 (false) La condición en el siguiente encabezado de while llama automáticamente a la función miembro operator void *.

while( cin >> cuenta >> nombre >> balance) La condición seguirá siendo true mientras failbit o badbit no se hayan establecido para cin. Al dar el marcador de fin de archivo se establece failbit para cin. La función operator void * puede utilizarse para probar si hay fin de archivo en un objeto de entrada, en vez de llamar explícitamente a la función miembro eof() del objeto de entrada. Si el archivo se abre satisfactoriamente, el programa comienza a procesar datos. La siguiente instrucción le pide al usuario que dé los diversos campos para cada registro o que introduzca el fin de archivo cuando haya terminado la entrada de datos:

cout << “Introduzca el número de cuenta, nombre y balance.\n” << “Introduzca fin-de-archivo para terminar.\n? ”;

La figura 7.7 lista las combinaciones de teclado para introducir el fin de archivo en diversos sistemas computacionales. Sistema computacional Combinación de teclado Sistema UNIX <ctrl>d (en una línea aparte) IBM PC y compatibles <ctrl>z Macintosh <ctrl>d VAX(VMS) <ctrl>

Figura 7.7. Combinaciones de fin de archivo para diversos sistemas.

La línea while( cin >> cuenta >> nombre >> balance ) introduce cada conjunto de datos y determina si se ha dado el fin de archivo. Cuando éste se introduce, o si se introducen datos erróneos, la operación >> de extracción de flujo de cin devuelve 0 (normalmente esta extracción de flujo devuelve cin) y la estructura while termina. El usuario introduce el fin de archivo para informarle al programa que ya no hay más datos a procesar. El marcador de fin de archivo se establece cuando el usuario da la combinación de teclas de fin de archivo. La estructura while continúa el ciclo mientras no se haya dado dicho marcador. La línea archivoSalida << cuenta << ‘ ‘ << nombre << ‘ ‘ << balance << ‘\n’; escribe un conjunto de datos en el archivo cliente.dat mediante el operador << de inserción de flujo y el objeto archivoSalida que se asoció con el archivo al inicio del programa. Los datos se pueden recuperar mediante un programa que esté diseñado para leer el archivo. Observe que el archivo creado en el programa SECUENCIAL.CPP es un archivo de texto. Cualquier editor de texto puede leerlo. Una vez que se ha introducido el marcador de fin de archivo, main() termina. Esto causa que el objeto archivoSalida se destruya llamando a su función destructor, la cual cierra el archivo cliente.dat. El programador puede cerrar explícitamente un objeto ofstream mediante la función miembro close() de la siguiente manera:

ArchivoSalida.close();

Para verificar que éste se haya creado satisfactoriamente, en la siguiente sección crearemos un programa para leer el archivo e imprimir su contenido.

Page 39: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-39

LECTURA DE DATOS DE UN ARCHIVO DE ACCESO SECUENCIAL Los datos se almacenan en archivos para que puedan ser recuperados y procesarlos cuando se necesite. La sección anterior mostró la manera de crear un archivo para acceso secuencial. En esta sección trataremos la manera de leer datos secuencialmente desde un archivo. Ejemplo 7.24

El siguiente programa LECTURA.CPP, lee registros del archivo clientes.dat creado por el programa SECUENCIAL.CPP, e imprime el contenido de los registros.

/* El siguiente programa: LECTURA.CPP, realiza lectura e impresión de un archivo secuencial. */ #include <iostream.h> //Para cout y cin #include <fstream.h> //Para crear el objeto archivoEntrada. #include <iomanip.h> //Para setw() #include <stdlib.h> //Para exit() void lineaSalida(int, const char *, double); int main(void) { //El constructor de ifstream abre el archivo ifstream archivoEntrada("clientes.dat", ios::in); if(!archivoEntrada) { cerr << "El archivo no pudo abrirse\n"; exit(1); }//Fin de if int cuenta; char nombre[30]; double balance; cout << setiosflags(ios::left) << setw(10) << "Cuenta" << setw(13) << "Nombre" << "Balance\n"; while( archivoEntrada >> cuenta >> nombre >> balance ) lineaSalida( cuenta, nombre, balance ); return 0; / /El destructor de ifstream cierra el archivo }//Fin de main() void lineaSalida( int cuenta, const char *nombre, double balance ) { cout << setiosflags(ios::left) << setw(10) << cuenta << setw(13) << nombre << setw(7) << setprecision(2) << resetiosflags(ios::left) << setiosflags(ios::fixed | ios::showpoint) << balance << '\n'; }//Fin de lineaSalida()

Page 40: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-40

Los archivos se abren para entrada mediante la creación de un objeto de la clase ifstream. Se pasan dos argumentos al objeto –el nombre del archivo y el modo de apertura del archivo. La declaración:

ifstream archivoEntrada(“clientes.dat”, ios::in);

crea un objeto ifstream llamado archivoEntrada y lo asocia con el archivo clientes.dat que se abrirá para entrada. Los argumentos que están entre paréntesis se pasan a la función constructora de ifstream, la cual abre el archivo y establece una línea de comunicación con el archivo. De manera predeterminada los objetos de la clase ifstream se abren para entrada, por lo que se podría haber utilizado la instrucción:

ifstream archivoEntrada(“clientes.dat”);

para abrir clientes.dat para entrada. Al igual que como sucede con un objeto ofstream, se puede crear un objeto ifstream sin abrir un archivo específico y posteriormente se le puede asociar un archivo. El programa utiliza la condición !archivoEntrada para determinar si el archivo se abrió satisfactoriamente antes de intentar la recuperación de datos del archivo. La declaración while( archivoEntrada >> cuenta >> nombre >> balance ) lee un conjunto de datos (es decir, un registro) desde el archivo. Cada vez que se ejecuta la línea, se lee otro registro desde el archivo hacia las variables contador, nombre y balance. Los registros se despliegan utilizando la función lineaSalida(), la cual usa manipuladores de flujo con parámetros para formatear los datos para su despliegue. Cuando se llega al fin de archivo, la secuencia de entrada de la estructura while devuelve 0 (normalmente se devuelve el flujo archivoEntrada), el archivo se cierra por medio de la función destructor ifstream y el programa termina. Para recuperar datos secuencialmente desde un archivo, los programas comienzan, por lo general, leyendo desde el inicio del archivo y leen todos los datos en forma consecutiva hasta que se encuentra el dato deseado. Tal vez sea necesario procesar el archivo secuencialmente varias veces (desde su inicio) durante la ejecución del programa. Las clases istream y ostream proporcionan funciones miembro para reubicar el apuntador de posición de archivo (el número de byte del siguiente byte del archivo a leer o escribir). Estas funciones miembro son seekg (buscar y obtener), para la clase istream y seekp (buscar y colocar), para la clase ostream. Cada objeto istream tiene un apuntador obtener que indica el número de bytes del archivo en donde sucederá la siguiente entrada, y cada objeto ostream tiene un apuntador colocar que indica el número de byte del archivo en el cual se colocará la siguiente salida. La instrucción:

archivoEntrada.seekg(0);

ubica el apuntador de posición de archivo en el inicio del archivo (localidad 0) que está asociado a archivoEntrada. El argumento seekg es normalmente un entero long. Es posible especificar un segundo argumento para indicar la dirección de búsqueda, la cual puede ser ios::beg (lo predeterminado) para ubicación con relación al inicio de un flujo, ios::cur para ubicación relación a la posición actual en un flujo, e ios::end para ubicación con relación al final del flujo. El apuntador de posición de archivo es un valor entero que especifica la localidad en el archivo como un número de bytes a partir de la localidad inicial del archivo (a esto también se le conoce

Page 41: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-41

como desplazamiento a partir del inicio del archivo). Algunos ejemplos de la ubicación del apuntador de posición de archivo obtener son:

//Ubica en el enésimo byte de objetoArchivo //asume ios::beg objetoArchivo.seekg(n); //Ubica n bytes hacia delante en objetoArchivo objetoArchivo.seekg(n, ios::cur); //Ubica y bytes hacia atrás con respecto al final de objetoArchivo objetoArchivo.seekg(y, ios::end); //Ubica al final de objetoArchivo objetoArchivo.seekg(0, ios::end);

Es posible realizar las mismas operaciones con la función miembro seekp de ostream. Se proporcionan las funciones miembro tellg y tellp para devolver las localidades actuales de los apuntadores obtener y colocar, respectivamente. La siguiente instrucción asigna el valor del apuntador de posición de archivo obtener a la variable localidad de tipo long.

localidad = objetoArchivo.tellg(); Ejemplo 7.25

El siguiente programa: CONSULTA.CPP, permite que un gerente de crédito despliegue la información de las cuentas para los clientes que tienen su saldo en cero (es decir, los clientes que no le deben dinero a la compañía), los saldos a favor (es decir, los clientes a los cuales la compañía les debe dinero) y los saldos en contra (es decir, los clientes que deben dinero a la compañía por bienes y servicios recibidos en el pasado) El programa despliega un menú y permite que el gerente de crédito dé alguna de tres opciones para obtener información de crédito. La opción 1 produce un listado de cuentas con saldo en cero. La opción 2 produce una lista de cuentas con saldos a favor. La opción 3 produce una lista de cuentas con saldos en contra. La opción 4 termina la ejecución del programa. Al dar una opción inválida simplemente se despliega la petición para que se dé otra selección.

/* El siguiente programa: CONSULTA.CPP , ilustra el uso de consultas hechas a un archivo de crédito. */ #include <iostream.h> //Para cout y ci #include <fstream.h> //Para crear el objeto archivoCliente #include <iomanip.h> //Para setw() #include <stdlib.h> //Para exit() enum tipoRespuesta{balanceCero = 1, balanceCredito, balanceDebito, fin}; int obtenerRespuesta(); bool debeDesplegar(int, double); void lineaSalida(int, const char *, double); int main(void) { //El constructor de ifstream abre el archivo ifstream archivoCliente("clientes.dat", ios::in);

Page 42: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-42

if(!archivoCliente) { cerr << "El archivo no pudo abrirse" << endl; exit(1); }//Fin de if int respuesta; int cuenta; char nombre[30]; double balance; cout << "Introduzca opcion\n" << " 1 - Listar cuentas con balance en cero\n" << " 2 - Listar cuentas con balance acreedor\n" << " 3 - Listar cuentas con balance deudor\n" << " 4 - Fin de corrida"; respuesta = obtenerRespuesta();

while(respuesta != fin) { switch(respuesta) { case balanceCero: cout << "\nCuentas con balance cero:\n"; break; case balanceCredito: cout << "\nCuentas con balance acreedor:\n"; break; case balanceDebito: cout << "\nCuentas con balance deudor:\n"; break; }//Fin de switch() archivoCliente >> cuenta >> nombre >> balance; while(!archivoCliente.eof()) { if(debeDesplegar(respuesta, balance)) lineaSalida(cuenta, nombre, balance); archivoCliente >> cuenta >> nombre >> balance; }//Fin de while interno archivoCliente.clear(); //Restablece el eof() para la siguiente entrada. archivoCliente.seekg(0); //Se mueve al inicio del archivo respuesta = obtenerRespuesta(); }//Fin de while externo cout << "Fin de corrida." << endl; return 0; //El destructor de ifstream cierra el archivo }//Fin de main() int obtenerRespuesta()

Page 43: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-43

ACTUALIZACIÓN DE ARCHIVOS DE ACCESO SECUENCIAL

Los datos que se formatean y escriben en un archivo de acceso secuencial, como se muestra en la sección CREACIÓN DE UN ARCHIVO SECUENCIAL, no pueden modificarse sin correr el riesgo de destruir otros datos del archivo. Por ejemplo, si se tuviera que cambiar el nombre Juan a Juanacatlán, el nombre anterior no se podría sobrescribir simplemente. El registro para Juan se escribió en el archivo como:

300 Juan 0.00 Si este registro se sobrescribiera comenzando en la misma localidad del archivo y utilizando el nombre más largo, el registro sería:

300 Juanacatlán 0.00 El nuevo registro contiene siete caracteres más que el registro original. Por lo tanto, los caracteres que están más allá de la segunda a de Juanacatlán sobrescribirían el inicio del

{ int respuesta; do { cout << "\n? "; cin >> respuesta; }

while(respuesta < balanceCero && respuesta > fin); return respuesta; }//Fin de obtenerRespuesta() bool debeDesplegar(int tipo, double balance) { if(tipo == balanceCredito && balance < 0) return true; if(tipo == balanceDebito && balance > 0) return true; if(tipo == balanceCero && balance == 0) return true; return false; }//Fin de deboDesplegar() void lineaSalida(int cuenta, const char *nombre, double balance) { cout << setiosflags(ios::left) << setw(10) << cuenta << setw(13) << nombre << setw(7) << setprecision(2) << resetiosflags(ios::left) << setiosflags(ios::fixed | ios::showpoint) << balance << '\n'; }//Fin de lineaSalida()

Page 44: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-44

siguiente registro secuencial en el archivo. El problema que hay aquí es que en el modelo de entrada/salida formateada en que se utiliza el operador de inserción << y el operador de extracción >>, los campos –y por lo tanto los registros- pueden variar en tamaño. Por ejemplo: 7, 14, -117, 2074 y 27383 son int y cada uno se almacena de manera interna en el mismo número de bytes de datos sin formato, pero cuando estos enteros se envían como texto formateado a la pantalla o a un archivo en disco, se convierten en campos de diferentes tamaño. Por lo tanto, el modelo de entrada/salida formateada no se utiliza generalmente para actualizar registros en su lugar. Tal actualización se puede hacer, pero es problemática. Por ejemplo, para hacer el cambio de nombre anterior se podrían copiar los registros que están antes de 300 Juan 0.00 en un archivo de acceso secuencial hacia un nuevo archivo, luego se escribiría el registro actualizado en el nuevo archivo y se copiarían los registros que están después de 300 Juan 0.00 hacia el nuevo archivo. Esto requiere el procesamiento de todos los registros del archivo para actualizar un registro. Esta técnica puede ser aceptable si se están actualizando muchos registros en un solo paso por el archivo. ARCHIVOS DE ACCESO ALEATORIO Hasta ahora hemos visto la manera de crear archivos de acceso secuencial y buscar en ellos para localizar información particular. Los archivos de acceso secuencial son inadecuados para las llamadas aplicaciones de acceso instantáneo en donde se debe localizar inmediatamente un registro de información particular. Algunas aplicaciones populares de acceso instantáneo son los sistemas de reservación de línea aéreas, los sistemas de bancos, los sistemas de punto de venta, las máquinas de cajero automático y otros tipos de sistemas de procesamiento de transacciones que requieren un acceso rápido de datos específicos. El banco en donde uno tiene su cuenta puede tener cientos, miles o hasta millones de otros clientes, pero cuando se ut iliza una máquina de cajero automático la cuenta se revisa en segundos para ver si hay fondos suficientes. Este tipo de acceso instantáneo es posible con los archivos de acceso aleatorio. Los registros individuales de un archivo de acceso aleatorio se pueden acceder directamente (y en forma rápida) sin buscar por todos los demás registros. Como hemos dicho, C++ no impone ninguna estructura a un archivo. Por lo tanto, la aplicación que necesite utilizar archivos de acceso aleatorio literalmente debe crearlos. Se pueden utilizar una diversidad de técnicas para crear archivos de acceso aleatorio. Tal vez la más simple es la que requiere que todos los registros de un archivo sean de la misma longitud. El uso de registros de longitud fija hace que para un programa sea fácil calcular (en función del tamaño y la clave del registro) la localidad exacta de cualquier registro con relación al inicio del archivo. Pronto veremos la manera en que esto facilita el acceso inmediato a registros específicos, incluso en archivos grandes. La figura 7.8 ilustra la visión de C++ de un archivo de acceso aleatorio que está compuesto de registros de longitud fija (cada registro es de 100 bytes de largo) Un archivo de acceso aleatorio es como un tren con muchos carros, algunos vacíos y otros llenos.

Page 45: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-45

Desplazamiento de bytes 0 100 200 300 400 500 600 100 bytes

100 bytes

100 bytes

100 bytes

100 bytes

100 bytes

100 bytes

Figura 7.8. La visión de C++ de un archivo de acceso aleatorio

Es posible insertar los datos en un archivo de acceso aleatorio sin destruir otros datos que

estén en el archivo. Los datos que han sido almacenados previamente también pueden actualizarse o borrarse sin tener que volver a escribir todo el archivo. En las siguientes secciones explicaremos la manera de crear un archivo de acceso aleatorio, introducir datos, leer los datos en forma secuencial y aleatoria, actualizar los datos y borrar datos que ya no se necesiten. CREACIÓN DE UN ARCHIVO DE ACCESO ALEATORIO La función miembro write de ostream da salida a un número fijo de bytes, que se inicia en una localidad de memoria específica, hacia un flujo especificado. Cuando el flujo está asociado con un archivo, los datos se escriben comenzando en la localidad del archivo que está especificada por el apuntador de posición de archivo colocar. La función miembro read de istream introduce un número fijo de bytes desde un flujo especificado hacia un área en memoria que comienza en una dirección especificada. Si el flujo está asociado con un archivo, los bytes se introducen comenzando en la localidad de archivo que está especificada por el apuntador de posición de archivo obtener. Ahora, para escribir una variable entera numero a un archivo, en vez de utilizar:

archivoSalida << numero; que podría imprimir tan pocos como 1 o tantos como 11 dígitos (10 dígitos más un signo, que cada uno requiere 1 byte de almacenamiento) para un entero de 4 bytes, podríamos utilizar:

archivoSalida.write(reinterpret_cast<const char *>(&numero), sizeof(numero)); que siempre escribe 4 bytes (en las máquinas que tienen enteros de 4 bytes). La función write espera un primer argumento de tipo const char * y, por lo tanto, utilizamos el operador de conversión mediante cast reinterpret_cast<const char *> para convertir la dirección de numero a un apuntador const char *. El segundo argumento de write es un entero del tipo size_t que especifica el número de bytes a escribir. Como veremos, la función read de istream después puede utilizarse para leer los 4 bytes de vuelta hacia la variable entera numero. Los programas de procesamiento de archivos de acceso aleatorio rara vez escriben un solo campo en un archivo. Por lo general, escriben una struct o un objeto de class a la vez, como veremos en los siguientes ejemplos.

Page 46: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-46

Ejemplo 7.26

Considere el siguiente planteamiento de un problema: Cree un programa de procesamiento de crédito que sea capaz de almacenar hasta 100 registros de longitud fija para una compañía que puede tener hasta 100 clientes. Cada registro deberá consistir de un número de cuenta que se utilizará como clave del registro, un apellido, un nombre y un saldo. El programa deberá ser capaz de actualizar una cuenta, agregar una nueva cuenta, borrar una cuenta y listar todos los registros de cuenta en un archivo de texto formateado para impresión. Las siguientes secciones presentan las técnicas necesarias para crear este programa de procesamiento de crédito. Llamemos al programa ALEATORIO.CPP.

/* Este es un archivo de cabecera llamado clntdata.h utilizados por los programas. Se define la estructura datoCliente que se utiliza en los programas ALEATORIO.CPP, ESCRIALEA.CPP, LECSEC.CPP . */ #ifndef CLNDATA_H #define CLNDATA_H struct datoCliente { int numeroCuenta; char apellido[15]; char nombre[10]; float balance; }; #endif

/* El siguiente programa: ALEATORIO.CPP , crea secuencialmente un archivo accesado aleatoriamente. El archivo de cabecer clntdata.h se encuentra definido en otro archivo. */ #include <iostream.h> //Para cout y cin #include <fstream.h> //Para crear el objeto creditoSalida #include <stdlib.h> //Para exit() #include "c:\apuntes1 c++\programas fuentes\leccion 07\clntdata.h" int main(void) { ofstream creditoSalida("credito.dat", ios::out); if(!creditoSalida) { cerr << "El archivo no pudo abrirse." << endl; exit(1); }//Fin de if datoCliente clienteBlanco = {0, " ", " ", 0.0};

Page 47: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-47

El programa ALEATORIO.CPP, ilustra la apertura de un archivo de acceso aleatorio, la definición del formato del registro utilizando una struct (definida en el archivo de encabezado clntdata.h) y la escritura de datos en el disco. Este programa inicializa los 100 registros del archivo credito.dat con struct vacías mediante la función write . Cada struct vacía contiene 0 en el número de cuenta, la cadena nula (representada por comillas vacías) para el apellido, la cadena nula para el nombre y 0.0 para el saldo. El archivo se inicializa con la cantidad adecuada de espacio vacío en donde se almacenarán los datos de las cuentas y para determinar en programas subsecuentes si cada registro está vacío o contiene datos. En dicho programa, las instrucciones:

creditoSalida.write(reinterpret_cast<const char *>(&clienteBlanco), sizeof(datoCliente));

causan que la estructura clienteBlanco de tamaño sizeof(datoCliente) se escriba en el archivo credito.dat que está asociado con el objeto creditoSalida de ofstream. Recuerde que el operador sizeof devuelve el tamaño en byte del objeto que está contenido entre paréntesis. Observe que el primer argumento de la función write debe ser de tipo const char *. Sin embargo, el tipo de dato de &clienteBlanco es datoCliente *. Para convertir &clienteBlanco al tipo de apuntador adecuado, la expresión:

reinterpret_cast<const char *>(&clienteBlanco) utiliza el operador de conversión mediante cast reinterpret_cast para convertir la dirección de clienteBlanco a const char *, de modo que la llamada write compile sin emitir un error de sintaxis.

ESCRITURA ALEATORIA DE DATOS A UN ARCHIVO DE ACCESO ALEATORIO

Ejemplo 7.27 El siguiente programa: ESCRIALEA.CPP, escribe datos hacia el archivo credito.dat. Utiliza la combinación de las funciones seekp y write de ostream para almacenar datos en localidades exactas del archivo. La función seekp establece el apuntador de posición de archivo colocar a una localidad específica del archivo y luego write envía los datos a la salida. Observe que el programa incluye el archivo de encabezado clntdata.h.

for(int i = 0; i < 100; i++) creditoSalida.write(reinterpret_cast<const char *>(&clienteBlanco), sizeof(datoCliente)); return 0; }//Fin de main()

/* El siguiente programa: ESCRIALEA.CPP , ilustra la escritura a un archivo de acceso aleatorio.*/ #include <iostream.h> //Para cout y cin #include <fstream.h> //Para crear el objeto creditoSalida #include <stdlib.h> //Para exit() #include "c:\apuntes1 c++\programas fuentes\leccion 07\clntdata.h"

Page 48: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-48

La línea creditoSalida.seekp( (cliente .numeroCuenta - 1 ) * sizeof(datoCliente ) ); ubica el apuntador de posición colocar para el objeto creditoSalida en la localidad de bytes calculada por (cliente.numeroCuenta - 1 ) * sizeof(datoCliente). Debido a que el número de cuenta está entre 1 y 100, se resta 1 al número de cuenta cuando se calcula la localidad, en bytes, del registro. Por lo tanto, para el registro 1 el apuntador de posición de archivo se establece al byte 0 del archivo. Observe que el objeto creditoSalida de ofstream se abre mediante el modo de apertura de archivo ios::ate . El apuntador de posición de archivo colocar se establece inicialmente al final del archivo, pero los datos se pueden escribir en cualquier lugar de él.

LECTURA SECUENCIAL DE DATOS DESDE UN ARCHIVO DE ACCESO ALEATORIO Ejemplo 7.28

En las secciones anteriores creamos un archivo de acceso aleatorio y escribimos datos en ese archivo. En esta sección desarrollaremos un programa al que llamaremos: LECSEC.CPP, que lee el archivo de manera secuencial e imprime solamente los registros que contienen datos. Estos programas producen un beneficio adicional: los registros se encuentran ordenados.

int main(void) { ofstream creditoSalida("credito.dat", ios::ate); if(!creditoSalida) { cerr << "El archivo no pudo abrirse." << endl; exit(1); }//Fin de if cout << "Introduzca número de cuenta " << "(1 a 100, 0 para terminar)\n? "; datoCliente cliente; cin >> cliente.numeroCuenta; while(cliente.numeroCuenta > 0 && cliente.numeroCuenta <= 100) { cout << "Introduzca apellido, nombre, balance\n? "; cin >> cliente.apellido >> cliente.nombre >> cliente.balance; creditoSalida.seekp( (cliente.numeroCuenta - 1 ) * sizeof(datoCliente) ); creditoSalida.write(reinterpret_cast<const char *>(&cliente), sizeof(datoCliente)); cout << "Introduzca número de cuenta\n? "; cin >> cliente.numeroCuenta; }//Fin de while return 0; }//fin de main()

Page 49: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-49

/* El siguiente programa: LECSEC.CPP , ilustra la lectura secuencial de un archivo de acceso aleatorio. */ #include <iostream.h> //Para cout y cin #include <iomanip.h> //Para setw() #include <fstream.h> //Para crear el objeto creditoEntrada #include <stdlib.h> //Para exit() #include "c:\apuntes1 c++\programas fuentes\leccion 07\clntdata.h" void lineaSalida(ostream&, const datoCliente &); int main(void) { ifstream creditoEntrada("credito.dat", ios::in); if(!creditoEntrada) { cerr << "El archivo no pudo abrirse." << endl; exit(1); }//Fin de if cout << setiosflags(ios::left) << setw(10) << "Cuenta" << setw(16) << "Apellido" << setw(11) << "Nombre" << resetiosflags(ios::left) << setw(10) << "Balance" << endl; datoCliente cliente; creditoEntrada.read(reinterpret_cast<char *>(&cliente), sizeof(datoCliente)); while(creditoEntrada && !creditoEntrada.eof()) { if(cliente.numeroCuenta != 0) lineaSalida(cout, cliente); creditoEntrada.read(reinterpret_cast<char *>(&cliente), sizeof(datoCliente)); }//Fin de while return 0; }//Fin de main() void lineaSalida(ostream &output, const datoCliente &c) { output << setiosflags(ios::left) << setw(10) << c.numeroCuenta << setw(16) << c.apellido << setw(11) << c.nombre << setw(10) << setprecision(2) << resetiosflags(ios::left) << setiosflags(ios::fixed | ios:: showpoint) << c.balance << '\n'; }//Fin de lineaSalida()

Page 50: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-50

La función read de istream introduce un número específico de bytes desde la posición actual en el flujo especificado hacia un objeto. Así:

creditoEntrada.read(reinterpret_cast<char *>(&cliente), sizeof(datoCliente));

lee el número de bytes especificados por sizeof(datoCliente ) desde el archivo asociado con el objeto creditoEntrada de ifstream y almacena los datos en la estructura cliente. Observe que la función read requiere un primer argumento de tipo char *. Debido a que &cliente es de tipo datoCliente *, debe convertirse a char * por medio del operador de conversión mediante cast reinterpre_cast. Observe que el programa incluye el archivo de encabezado clntdata.h. El programa lee secuencialmente cada uno de los registros del archivo credito.dat, revisa cada registro para ver si contiene datos y despliega salidas formateadas para los registros que contienen datos. La condición:

while(creditoEntrada && !creditoEntrada.eof()) utiliza la función miembro eof() de ios para determinar si se ha llegado al final del archivo y causa que termine la ejecución de la estructura while. Asimismo, si hay algún error en la lectura del archivo, el ciclo terminará debido a que creditoEntrada se evaluará como false. lineaSalida (que toma dos argumentos, un objeto ostream y una estructura datoCliente que se enviará a la salida) envía a la salida la entrada de datos del archivo. El tipo del parámetro de ostream es interesante, debido a que cualquier objeto ostream (tal como cout) o cualquier objeto de una clase derivada de ostream (tal como un objeto de tipo ofstream) se puede proporcionar como argumento. Esto significa que la misma función puede utilizarse, por ejemplo, para realizar la salida hacia el flujo de salida estándar y hacia un flujo de archivo sin escribir funciones separadas. ¿Qué hay acerca del beneficio adicional? Si examina la ventana de salida, ¡observará que los registros están listados en orden (por número de cuenta)! Esta es una consecuencia simple de la forma en que almacenamos estos registros en el archivo mediante técnicas de acceso directo. En comparación al ordenamiento de burbuja que hemos visto, el ordenamiento con técnicas de acceso directo es extremadamente rápido. La velocidad se logra haciendo que el archivo sea lo suficientemente grande como para almacenar todo registro posible que pueda crearse. Esto significa, por supuesto, que el archivo podría estar ocupado en forma dispersa la mayor parte del tiempo, lo que es un desperdicio de almacenamiento. Por lo tanto , éste es otro ejemplo del compromiso del espacio contra el tiempo: mediante el uso de grandes cantidades de espacio somos capaces de desarrollar un algoritmo de ordenamiento mucho más rápido.

EJEMPLO: UN PROGRAMA DE PROCESAMIENTO DE TRANSACCIONES Ejemplo 7.29

Ahora presentaremos un programa sustancial: TRANSAC.CPP, de procesamiento de transacciones, que utiliza un archivo de acceso aleatorio para lograr un procesamiento de acceso instantáneo. El programa mantiene información de cuentas bancarias, actualiza cuentas existentes, agrega nuevas cuentas, borra cuentas y almacena un listado formateado de todas las cuentas actuales en un archivo de texto para impresión. Asumimos que ya se ejecutaron los programa ALEATORIO.CPP para crear el archivo credito.dat y el programa ESCRIALEA.CPP para insertar los datos iniciales.

Page 51: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-51

El programa tiene cinco opciones (la opción 5 es para terminar el programa). La opción 1 llama a la función archivoTexto() para almacenar una lista formateada de toda la información de cuentas en un archivo de texto, llamado imprimir.txt, que puede imprimirse posteriormente. La función archivoTexto() toma un objeto fstream como argumento para utilizarlo a fin de introducir datos desde el archivo credito.dat. La función archivoTexto() utiliza la función miembro read de istream y las técnicas de acceso de archivo secuencial para introducir los datos de credito.dat. La función lineaSalida(), ya en programas anteriores estudiada se utiliza para enviar los datos al archivo imprimir.txt. Observe que archivoTexto() utiliza la función miembro seekg de istream para asegurarse de que el apuntador de posición de archivo esté al inicio del archivo. La opción 2 llama a la función actualizarRegistro() para actualizar una cuenta. La función sólo actualizará un registro existente, por lo que la función primero determina si el registro especificado está vacío. El registro se lee en la estructura cliente mediante la función miembro read de istream, después cliente.numeroCuenta se compara contra cero para determinar si el registro contiene información. Si cliente.numeroCuenta es cero, se imprime un mensaje indicando que el registro está vacío y se despliegan las selecciones del menú. Si el registro contiene información, la función actualizarRegistro() despliega el registro en pantalla mediante la función lineaSalida(), introduce la cantidad de transacciones, calcula el nuevo saldo y vuelve a escribir el registro en el archivo. La opción 3 llama a la función nuevoRegistro() para agregarle una nueva cuenta al archivo. Si el usuario introduce el número de una cuenta existente, nuevoRegistro() despliega un mensaje indicando que la cuenta existe y despliega las alternativas del menú. La opción 4 llama a la función borrarRegistro() para borrar un registro del archivo. Al usuario se le pide que introduzca un número de cuenta. Sólo puede borrarse un registro existente, por lo que si la cuenta especificada está vacía, se emite un mensaje de error. Si la cuenta existe, se reinicializa copiando un registro vacío (clienteBlanco) al archivo. Se despliega un mensaje para informar al usuario que se ha borrado el registro. El archivo credito.dat se abre mediante la creación de un objeto fstream para lectura y escritura usando los modos ios::in e ios::out a los que se les aplicó un OR.

/* El siguiente programa: TRANSAC.CPP, lee de manera secuencial un archivo de acceso aleatorio, actualiza los datos que ya están escritos en el archivo, crea nuevos datos a colocarse en el archivo y borra datos que ya están en el archivo. */ #include <iostream.h> //Para cout y cin #include <fstream.h> //Para los objetos entraSaleCredito, archivoImprimir #include <iomanip.h> //Para setw() #include <stdlib.h> //Para exit() #include "c:\apuntes1 c++\programas fuentes\leccion 07\clntdata.h" int pedirSeleccion(); void archivoTexto(fstream &); void actualizarRegistro(fstream &); void nuevoRegistro(fstream &); void borrarRegistro(fstream &); void lineaSalida(ostream &, const datoCliente &); int obtenerCuenta(const char *); enum seleccion{ARCHIVOTEXTO = 1, ACTUALIZAR, NUEVO, BORRAR, FIN};

Page 52: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-52

int main(void) { fstream entraSaleCredito("credito.dat", ios::in | ios::out); if(!entraSaleCredito) { cerr << "El archivo no pudo abrirse." << endl; exit(1); }//Fin de if int opcion; while( ( opcion = pedirSeleccion()) != FIN ) { switch(opcion) { case ARCHIVOTEXTO: archivoTexto(entraSaleCredito); break; case ACTUALIZAR: actualizarRegistro(entraSaleCredito); break; case NUEVO: nuevoRegistro(entraSaleCredito); break;

case BORRAR: borrarRegistro(entraSaleCredito); break; default: cerr << "Opción incorrecta\n"; break; }//Fin de switch() entraSaleCredito.clear(); //Restablece el marcador de fin de archivo }//Fin de while return 0; }//Fin de main() //Peticion para la selección de un menú pedirSeleccion() { cout << endl; cout << " Introduzca su elección" << endl << " 1 - Guardar un archivo de cuentas como texto formateado\n" << " llamado \"imprimir.text\"para impresión\n"

Page 53: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-53

<< " 2 - Actualizar una cuenta\n" << " 3 - Agregar una nueva cuenta\n" << " 4 - Borrar una cuenta\n" << " 5 - Terminar el programa\n? ";

int seleccionMenu; cin >> seleccionMenu; return seleccionMenu; }//Fin de pedirSeleccion() //Crea archivo de texto formateado para impresión void archivoTexto(fstream &leeDelArchivo) { ofstream archivoImprimir( "imprimir.txt", ios::out); if(!archivoImprimir) { cerr << "El archivo no pudo abrirse." << endl; exit(1); }//Fin de if archivoImprimir << setiosflags(ios::left) << setw(10) << "Cuenta" << setw(16) << "Apellido" << setw(11) << "Nombre" << resetiosflags(ios::left) << setw(10) << "Balance" << endl; leeDelArchivo.seekg(0); datoCliente cliente; leeDelArchivo.read(reinterpret_cast<char *>(&cliente), sizeof(datoCliente)); while(!leeDelArchivo.eof()) { if(cliente.numeroCuenta != 0) lineaSalida(archivoImprimir, cliente); leeDelArchivo.read(reinterpret_cast<char *>(&cliente), sizeof(datoCliente)); }//Fin de while }//Fin de archivoTexto() //Actualiza el saldo de una cuenta void actualizarRegistro(fstream &actualizarArchivo) { int cuenta = obtenerCuenta("Introduzca la cuenta a actualizar"); actualizarArchivo.seekg(( cuenta - 1 ) * sizeof(datoCliente)); datoCliente cliente; actualizarArchivo.read(reinterpret_cast<char *>(&cliente), sizeof(datoCliente)); if(cliente.numeroCuenta != 0)

Page 54: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-54

{ lineaSalida(cout, cliente); cout << "\nIntroduzca cargo (+) o pago (-): "; float transaccion; //Cargo o pago cin >> transaccion; //Debe validar cliente.balance += transaccion; lineaSalida(cout, cliente); actualizarArchivo.seekp(( cuenta - 1 ) * sizeof(datoCliente)); actualizarArchivo.write(reinterpret_cast<const char *>(&cliente), sizeof(datoCliente)); }//Fin de if else cerr << "La cuenta #" << cuenta << " no tiene información." << endl; }//Fin de actualizarRegistro() //Crea e inserta un nuevo registro void nuevoRegistro(fstream &insertarEnArchivo) { int cuenta = obtenerCuenta("Introduzca el nuevo número de cuenta"); insertarEnArchivo.seekg((cuenta - 1) * sizeof(datoCliente)); datoCliente cliente; insertarEnArchivo.read(reinterpret_cast<char *>(&cliente), sizeof(datoCliente)); if(cliente.numeroCuenta == 0) { cout << "Introduzca apellido, nombre, balance\n? "; cin >> cliente.apellido >> cliente.nombre >> cliente.balance; cliente.numeroCuenta = cuenta; insertarEnArchivo.seekp((cuenta - 1) * sizeof(datoCliente)); insertarEnArchivo.write(reinterpret_cast<char *>(&cliente), sizeof(datoCliente)); }//Fin de if else cerr << "La cuenta #" << cuenta << " ya tiene información." << endl; }//Fin de nuevoRegistro() if(cliente.numeroCuenta != 0) { datoCliente clienteBlanco = {0, " ", " ", 0.0}; borrarDeArchivo.seekp((cuenta - 1) * sizeof(datoCliente)); borrarDeArchivo.write(reinterpret_cast<char *>(&clienteBlanco),

sizeof(datoCliente)); cout << "La cuenta #" << cuenta << " eliminada." << endl; }//Fin de if else cerr << "La cuenta #" << cuenta << " está vacía." << endl; }//Fin de borrarRegistro()

Page 55: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-55

SOLUCIÓN DE PROBLEMAS EN ACCIÓN: CÁLCULO DE VOLTAJE PROBLEMA

Como resumen general para el material presentado en este capítulo vamos a escribir un programa amigable que calcule el voltaje dados, por el usuario, los valores de la corriente y resistencia. La salida final del programa será una tabla que muestre los valores de corriente, resistencia y voltaje con sus respectivos títulos subrayados. Además, mostraremos el nombre del usuario y la fecha en que el programa fue ejecutado.

Primero definiremos el problema en términos de salida, entrada y proceso como sigue:

DEFINICIÓN DEL PROBLEMA

Salida: La salida del programa deberá ser una tabla que muestre el valor del voltaje

calculado junto con los valores de corriente y resistencia usados en el cálculo. El nombre del usuario y la fecha en que el programa fue ejecutado se muestra arriba de la tabla.

Nombre del usuario: xxxxxxxxxxxxxxxxxxxxx Fecha: dd/mm/aa RESISTENCIA CORRIENTE VOLTAJE ddd.dddd ddd.dddd ddd.dddd Entrada: La entrada deberá ser el nombre del usuario, la fecha en que el programa fue

ejecutado, un valor para corriente y uno para resistencia. Procesamiento: Ley de Ohm: Voltaje = Corriente ×× Resistencia.

//Envía a la salida una línea con la información del cliente void lineaSalida(ostream &output, const datoCliente &c) { output << setiosflags(ios::left) << setw(10) << c.numeroCuenta << setw(16) << c.apellido << setw(11) << c.nombre << setw(10) << setprecision(2) << resetiosflags(ios::left) << setiosflags(ios::fixed | ios::showpoint) << c.balance << '\n'; }//Fin de lineaSalida() //Obtiene un número de cuenta desde el teclado int obtenerCuenta(const char *peticion) { int cuenta; do { cout << peticion<< " (1 - 100): "; cin >> cuenta; }while(cuenta < 1 || cuenta > 100); return cuenta; }//fin de obtenerCuenta()

Page 56: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-56

PLANEACIÓN DE LA SOLUCIÓN

El siguiente paso es construir una serie de algoritmos a partir de la definición del problema. Usando el diseño estructurado de programa, dividiremos el problema en subproblemas individuales para solucionar el problema general. Identifique las diferentes tareas que se deben realizar para la solución del problema. Primero, el programa debe obtener del usuario los datos requeridos. Una vez que los datos se ingresan, el programa debe mostrar los resultados. De esta manera, podemos identificar tres tareas o funciones del programa como sigue:

•• Obtención del nombre del usuario, fecha de ejecución del programa, valores de corriente y

resistencia. •• Multiplicación de corriente y resistencia para obtener el voltaje. •• Despliegue del nombre del usuario, de la fecha de ejecución del programa y de la tabla de

resultados.

El diagrama de estructura siguiente muestra la estructura en bloques necesarios para resolver el problema.

Debido a que estamos usando la técnica estructurada de bloque para diseñar el programa, debemos emplear el refinamiento paso a paso para desarrollar los algoritmos. El algoritmo de nivel inicial main(), simplemente manifestará la definición del problema y llamará a las funciones o subprogramas individuales como sigue:

ALGORITMO INICIAL voltaje() INICIO Llamada a la función para obtener del usuario los datos de entrada. Llamada a la función para calcular el voltaje de circuito usando la ley

de Ohm. Llamada a la función para mostrar los resultados.

FIN.

El primer nivel de refinamiento necesita que mostremos un algoritmo detallado para cada módulo de subprograma o función. Estos son como sigue:

voltaje() Determine el voltaje de un circuito usando

la ley de Ohm

calcularVoltaje() Calcule el voltaje

obtenerDato() Obtenga los datos de entrada del usuario

visualizarResultado()

Muestre resultados

Page 57: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-57

PRIMER NIVEL DE REFINAMIENTO obtenerDato() Inicio Escribir(“Favor de introducir su nombre: ”) Leer(nombre). Escribir(“Favor de introducir la fecha: ”) Leer(fecha). Escribir/”Favor de introducir el valor de la corriente: ”. Leer(corriente) Escribir(“Favor de pedir el valor de la resistencia: ”) Leer(resistencia) Fin.

calcularVoltaje() Inicio voltaje = corriente x resistencia. Fin.

visualizarResultado() Inicio Escribir(“Nombre del usuario: ”, nombre).

Escribir(“Fecha: ”, fecha). Escribir(“RESISTENCIA CORRIENTE VOLTAJE ”)

voltaje. Escribir(resistencia, corriente, voltaje)

Fin. CODIFICACIÓN DEL PROGRAMA

A continuación se muestra como los algoritmos anteriores se traducen a un código C++.

//* Nombre del problema: SOLPROB.CPP Salida: El problema de salida deber una tabla mostrando los valores de voltaje junto con los valores de resistencia y corriente usados en el cálculo. El nombre del usuario y la fecha de ejecución del programa aparecerán en la parte su- perior de la pantalla. Entrada: La entrada es el nombre del usuario y fecha, un valor para corriente y uno para resistencia Procesamiento: Ley de Ohm: voltaje = corriente * resistencia */ #include <iostream.h> // Para cin y cout #include <iomanip.h> // Para setw() const int TAMANO_NOMBRE = 31; // Tamano del arreglo para el nombre del usuario const int TAMANO_FECHA = 9; // Tamano del arreglo de la fecha void main(void) {

Page 58: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-58

// Definición de variables char nombre[TAMANO_NOMBRE] = "\0"; char fecha[TAMANO_FECHA] = "\0"; float voltaje = 0.0; float corriente = 0.0; float resistencia = 0.0; // Mensaje de descripción del problema cout << "Este programa calculará el voltaje, a partir de los valores" " de corriente y resistencia que se escriban" << endl << endl;

// Obtención de los datos de entrada del usuario cout << "Escriba su nombre: "; cin.getline(nombre,TAMANO_NOMBRE); cout << "Escriba la fecha en el formato dd/mm/aa: "; cin.getline(fecha,TAMANO_FECHA); cout << endl << "Escriba un valor de corriente en amperes: "; cin >> corriente; cout << "Escriba un valor de resistencia en ohms: "; cin >> resistencia; // Calcula el voltaje usando la ley de Ohm voltaje = corriente * resistencia; // Muestra el nombre, la fecha y la tabla del resultado cout.setf(ios::fixed); cout.precision(3); cout << "\n\n" << nombre << endl; cout << fecha; cout << "\n\n\n\n" << setw(20) << "RESISTENCIA" << setw(20) << "CORRIENTE" << setw(20) << "VOLTAJE\n" << setw(20) << "-----------" << setw(20) << "---------" << setw(20) << "-------" << endl; cout << setw(20) << resistencia << setw(20) << corriente << setw(20) << voltaje << endl; } //Fin de main()

Page 59: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-59

LO QUE NECESITA SABER Antes de que continúe con la siguiente lección, asegúrese de haber comprendido los siguientes conceptos:

qq C++ proporciona el flujo de entrada cin para introducir datos por el teclado.

qq El objeto de flujo de entrada cin (conectado normalmente al teclado) se utiliza para introducir datos . Es posible introducir varios elementos de datos concatenando operadores de extracción de flujo (>>)

qq Cuando su programa utiliza cin para leer datos , su programa debe especificar una o más variables en la que cin colocará los datos.

qq Para asignar entrada a una variable, su programa debe usar cin con el operador de extracción (>>)

qq Cuando su programa usa cin para leer múltiples valores , cin utiliza espacios en blanco para determinar donde un valor termina y comienza un segundo valor.

qq Si el usuario no introduce datos correctos, pueden ocurrir errores , y el valor que cin asigna a las variables de su programa puede ser incorrecto.

qq Para leer los datos en C++ emplee el objeto de flujo de entrada cin. Como cout, cin es un objeto de flujo de archivo predefinido en C++.

qq El enunciado cin debe incluir una(s) variable (s) para lectura. Cualquier variable listada dentro de un enunciado cin se debe definir antes de usarse en el programa.

qq Además, la clase de datos ingresada para una variable determinada deberá coincidir con la clase de datos definida para esa variable.

qq El operador de extracción de flujo >> se usa dentro del enunciado cin para extraer datos desde el flujo de entrada y asignar estos datos a la(s) variable(s) listadas en el enunciado cin.

qq Cuando lea los datos de un solo carácter, el operador >> ignora los espacios en blanco. Sin embargo, puede usar la función get() con cin para leer datos de espacio en blanco de un solo carácter.

qq La función miembro get() sin argumentos introduce un carácter y lo devuelve; devuelve EOF si encuentra el fin de archivo del flujo.

qq La función miembro get() con un argumento de clase char introduce un carácter. Se devuelve EOF cuando encuentra el final del archivo, en caso contrario se devuelve el objeto istream para el que se llamó la función miembro get()

qq La función miembro get() con tres argumentos –un arreglo de caracteres, un límite de tamaño y un delimitador (que tiene un valor predeterminado de nueva línea) –lee caracteres desde el flujo de entrada hasta un máximo del límite de caracteres – 1 y termina, o termina cuando se lee el delimitador. La cadena de entrada se termina con un carácter nulo. El delimitador no se coloca en el arreglo de caracteres, sino que permanece en el flujo de entrada.

qq La función miembro getline() opera en forma similar a la función get() con tres argumentos. La función getline() elimina al delimitador de flujo de entrada pero no lo almacena en la cadena.

qq Cuando lea datos de cadena de caracteres, el operador >> terminará en un espacio en blanco. Como resultado, debe usar la función getline() junto con cin cuando lea un arreglo de caracteres o cadenas.

qq Cuando use getline(), deberá descartar los caracteres restantes “\n” en la memoria temporal del teclado a partir de una operación de lectura anterior. Para lograr esto, use un carácter delimitador diferente para getline(), una variable temporal o el manejador ws.

qq La función miembro ignore() se salta un número especificado de caracteres (el número predeterminado es un carácter) en el flujo de entrada, y termina si encuentra al delimitador especificado (el delimitador predeterminado es EOF)

qq La función miembro putback() devuelve al flujo el carácter anterior obtenido mediante un get() qq La función miembro peek() devuelve el siguiente carácter de un flujo de entrada, pero no elimina el

carácter del flujo.

Page 60: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-60

qq Las funciones gets() y fgets() están contenidas en el archivo de encabezado stdio.h, también se usan para leer cadenas. La función gets() convierte un CRLF en un terminador nulo cuando se almacena la cadena y fgets() lee y almacena el CRLF mientras se adiciona el terminador nulo al final de la cadena.

qq Todas las E/S del programa están soportadas por archivos que operan sobre clases predefinidas en C++. Para tener acceso a los archivos en disco, use una de las siguientes tres clases: ifstream, ofstream o fstream. La clase ifstream se usa para realizas operaciones de entrada o lectura, desde archivos en disco; La clase ofstream se usa para realizar operaciones de salida o escritura, en archivos en disco. La clase fstream se puede usar para realizar ambas operaciones de lectura y escritura de archivos en disco. Todas estas tres clases se declaran en el archivo de encabezado fstream.h.

qq Hay tres tareas que siempre se ejecutarán para manejar archivos en disco en C++:

1. Defina un objeto de flujo de archivo para una de las clases de archivo fstream.h. 2. Para abrir el archivo, una el objeto de flujo de archivo a un archivo en disco en particular. 3. Cierre el archivo.

qq Todos los archivos en disco contienen una marca de fin de archivo, llamado EOF, para señalar el final de un archivo determinado en el disco. Al leer un archivo en disco, instruimos al compilador a buscar un marcador de EOF y cuando se detecta termina la operación de lectura. Esto se logra con el uso de un ciclo y la verificación del marcador de EOF usando una función estándar llamada eof()

qq Los programas amigables requieren interacción entre el programa y el usuario. Como mínimo un programa amigable debe hacer lo siguiente: 1. Escribir un mensaje de descripción del programa para el usuario. 2. Instruir al usuario antes de cualquier operación de lectura. 3. Generar un buen formato de salida cuyo significado sea fácilmente entendible para el usuario.

qq Todos los elementos de datos que son procesados por una computadora están reducidos a combinaciones de ceros y unos.

qq El elemento de datos más pequeño en una computadora puede asumir el valor 0 o el valor 1. A tal elemento de dato se le llama bit.

qq A los dígitos, las letras y los símbolos especiales se les conoce como caracteres. Al conjunto de todos los caracteres que pueden utilizarse para escribir programas y representar elementos de datos en una computadora particular se le llama el conjunto de caracteres de la computadora. Cada carácter dentro del conjunto de caracteres de la computadora está representado como un patrón de ocho uno y ceros (lla mado un byte)

qq Un campo es un grupo de caracteres (0 bytes) que tiene significado.

qq Un registro es un grupo de campos relacionados.

qq Al menos un campo de un registro se elige como clave de registro para identificarlo como perteneciente a una persona o entidad particular que es única entre todos los registros del archivo.

qq El acceso secuencial es el método más popular para acceder datos en un archivo.

qq A un conjunto de programas que está diseñado para crear y administrar bases de datos se le llama DBMS (sistema de administración de bases de datos)

qq C++ ve a cada archivo como un flujo secuencial de bytes.

qq Cada archivo termina en alguna forma de marcador de fin de archivo dependiente de la máquina.

qq Los flujos proporcionan canales de comunicación entre archivos y programas. qq Los archivos de encabezado <iostream.h> y <fstream.h> se deben incluir en un programa para

realizar la E/S de archivos de C++. El archivo de encabezado <fstream.h> incluye las defin iciones para las clases de flujo ifstream, ofstream y fstream.

qq Los archivos se abren simplemente instanciando objetos de las clases de flujo ifstream, ofstream y fstream.

qq C++ no impone ninguna estructura a un archivo. Por lo tanto, los conceptos como registro no existen en C++. El programador debe estructurar un archivo para satisfacer los requerimientos de una aplicación en particular.

qq Los archivos se abren para salida creando un objeto de la clase ofstream. Se pasan dos argumentos al objeto, el nombre del archivo y el modo de apertura del archivo. Para un objeto ofstream el modo de

Page 61: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-61

apertura de archivo puede ser ios::out para enviar los datos hacia el archivo o ios::app para agregar datos al final del archivo. Los archivos existentes que se abren con el modo ios::out se truncan. Los archivos que no existen se crean.

qq La función miembro de operador operator! de ios devuelve un valor diferente de cero (true) si se ha establecido el indicador failbit o badbit para un flujo en la operación open.

qq La función miembro de operador operator void * de ios convierte el flujo hacia un apuntador para compararlo con 0 (el apuntador nulo) Si no se han establecido ni failbit ni badbit para el flujo, se devuelve 0 (false)

qq Los programas pueden procesar ningún archivo, un archivo o varios archivos. Cada archivo tiene un nombre único y está asociado con un objeto de flujo de archivo adecuado. Todas las funciones de procesamiento de archivo deben referirse a un archivo mediante el objeto adecuado.

qq Un apuntador obtener indica la posición del archivo a partir de la cual va a suceder la siguiente entrada, y un apuntador colocar indica la posición del archivo en la cual se colocará la siguiente salida. Las clases istream y ostream proporcionan funciones miembro para reubicar el apuntador de posición de archivo. Las funciones son seeg() (buscar obtener) para la clase istream, y sep() (buscar colocar) para la clase ostream.

qq Las funciones miembro tellp() y tellg() devuelven las localidades actuales de los apuntadores colocar y obtener, respectivamente.

qq Una forma conveniente para implementar los archivos de acceso aleatorio es utilizar solamente registros de longitud fija. Mediante el uso de esta técnica un programa puede calcular rápidamente la localidad exacta de un registro con relación al inicio del archivo.

qq Los datos se pueden insertar en un archivo de acceso aleatorio sin destruir otros datos del archivo. Los datos se pueden actualizar o borrar sin volver a escribir el archivo completo.

qq La función miembro write() de ostream da salida, hacia un flujo especificado, a un número de bytes que comienzan en una localidad de memoria indicada. Cuando el flujo está asociado con un archivo, los datos se escriben en la localidad especificada por el apuntador de posición de archivo colocar.

qq La función miembro read() de istream introduce un número de bytes desde el flujo especificado hacia un área en memoria que comienza en una dirección indicada. Los bytes se introducen comenzando en la localidad especificada por el apuntador de posición de archivo obtener.

qq La función write() espera un primer argumento de tipo const char *, por lo que este argumento se debe convertir mediante cast a un const char *, en caso de que sea de algún otro tipo de apuntador. El segundo argumento es un entero que especifica el número de bytes a escribir.

qq El operador unario sizeof() de tiempo de compilación devuelve el tamaño en bytes del objeto que está contenido entre paréntesis, sizeof() devuelve un entero sin signo.

qq La función miembro read()e istream introduce un número especificado de bytes desde el flujo indicado hacia un objeto, read() requiere un primer argumento de tipo char *.

qq La función miembro eof() de ios determina si se ha establecido el marcador de fin de archivo para el flujo indicado. El fin de archivo se establece después de que falla un intento de lectura.

Page 62: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-62

PREGUNTAS Y PROBLEMAS PREGUNTAS

1. Considere el siguiente segmento de programa:

char A = ‘ ’; char B = ‘ ’; cin >> A; cin >> B; cout << A << B << endl;

¿Qué se mostrará por cada una de las siguientes entradas de usuario?

a. A B

b. AB c. 3.14 d. A B (Nota: hay un espacio entre la ‘A’ y la ‘B’)

2. ¿Qué función deberá utilizar con cin para obtener datos de cadena?

3. Defina una variable apropiado y escriba los enunciados para leer y después mostrar el nombre de su escuela.

4. Cierto o falso: la función getline() almacena el carácter delimitador como parte de un arreglo de cadena.

5. El carácter delimitador predeterminado para la función getline() es el carácter _________________.

6. ¿Qué argumentos deben proporcionarse cuando se usa getline() para leer datos de cadena?

7. ¿Cuál es la relación entre iostream, cin y getline()?

8. ¿Por qué debe definir una cadena con un byte mas largo que la máxima longitud de cadena si está usando cin.getline() para leer la cadena?

9. Explique la diferencia entre una secuencia de escape y un manejador de E/S. 10. ¿Cuándo deberá usar gets() o fgets() en lugar de cin.getline()?

11. ¿Qué archivo de encabezado debe incluirse para usar gets() o fgets()?

12. Explique cómo la operación de gets() difiere de la fgets()

13. ¿Por qué getline() es insegura para lectura de datos de carácter después de usarse para lectura de datos numéricos o de un solo carácter?

14. Describa tres métodos para solucionar el problema getline() referido en la pregunta 13.

15. Escriba el código necesario para obtener una salida decimal con tres lugares decimales a la derecha del punto decimal.

16. ¿Qué es un flujo de archivo?

17. Escriba los enunciados para crear los siguientes flujos de archivo en disco:

a. Un flujo de archivo llamado archEnt que leerá un archivo en disco llamado misDatos.txt. b. Un flujo de archivo llamado archSal que escribirá a un archivo cuyo nombre se almacena en un arreglo de caracteres

llamado nombre.

18. ¿Por qué es ventajoso usar ciclos cuando se lee y procesa un archivo en disco?

19. ¿Qué se deberá hacer para que un programa sea más amigable para el usuario?

20. Escriba instrucciones (o comentarios) que logren lo siguiente:

a. Indique que el programa calculará el producto de tres enteros. b. Declare x, y, z y resultado como variables de clase int. c. Pídale al usuario que introduzca tres enteros. d. Lea del teclado tres enteros y guárdelos en las variables x , y e z. e. Calcule el producto de los tres enteros contenidos en las variables x, y e z y asigne el resultado a la variable resultado. f. Imprima en pantalla “El producto es: ” seguido del valor de la variable resultado. g. Devuelva un valor desde main() que indique que el programa ha terminado correctamente.

21. Escriba una instrucción o línea de C++ que haga lo siguiente:

a. Imprima el mensaje “Teclee dos números”.

Page 63: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-63

b. Asigne el producto de las variables b y c a la variable a. c. Indique que el programa efectúa un cálculo de nómina ejemplo (es decir, ponga un texto que ayuda a documentar el

programa) d. Introduzca tres valores enteros desde el teclado y cárguelos en las variables a, b y c.

22. ¿Qué es lo que se imprime (si es que se imprime algo) cuando se ejecutan las siguientes instrucciones de C++? Si no se imprime nada, entonces responda “nada”. Suponga que x = 2 e y = 3.

a. cout << x; b. cout << x + x; c. cout << “x = ”; d. cout << “x = ” << x; e. cout << x + y << “ = ” << y + x; f. z = x + y; g. cin >> x >> y; h. //cout << “x + y = ” << x + y; i. cout << “\n”;

23. ¿Cuáles de las siguientes instrucciones de C++ contienen variables cuyos valores serán destruidos?

a. cin >> b >> c >> d >> e >> f; b. p = i + j + k + 7; c. cout << “variables cuyos valores son destruidos”; d. cout << “a = 5”;

24. Para cada una de las siguientes indicaciones, escriba una sola instrucción que realice la tarea indicada. a. Obtenga el siguiente carácter del flujo de entrada sin extraerlo del flujo. Respuesta: cin.peek();

b. Introduzca un solo carácter en la variable c de tipo char utilizando la función miembro get de istream en dos formas diferentes. Respuesta: c = cin.get(); cin.get(c);

c. Introduzca y descarte los siguientes seis caracteres de un flujo de entrada. Respuesta: cin.ignore(6); d. Lea 10 caracteres en el arreglo de caracteres nombre. Suspenda la lectura de caracteres si encuentra el delimitador

‘.’. No elimine dicho delimitador del flujo de entrada. Escriba otra instrucción que realice esta tarea y elimine el delimitador de la entrada. Respuesta: cin.get(nombre, 10, ‘.’); cin.getline(nombre, 10, ‘.’);

e. Introduzca un valor entero en la variable int meses y un valor de punto flotante en la variable float tasaCredito.

f. Lea caracteres en el arreglo de caracteres línea hasta que encuentre el carácter ‘z’ y con un límite de 20 caracteres (que incluyan el carácter nulo de terminación). No extraiga el carácter delimitador del flujo. Respuesta: cin.get(linea, 20, ‘z’);

g. Lea una cadena hacia la variable del arreglo de caracteres estado.

h. Lea caracteres en el arreglo s hasta que se encuentre el carácter ‘p’ con un límite de 10 caracteres (incluyendo el carácter nulo de terminación) Extraiga el delimitador del flujo de entrada y descártelo.

i. Lea una cadena de la forma “caracteres” desde la entrada estándar. Guarde la cadena en el arreglo de caracteres s. Elimine las comillas del flujo de entrada. Lea un máximo de 50 caracteres (incluyendo el carácter nulo de terminación)

25. Llene los espacios en blanco en cada una de las siguientes oraciones.

a. A final de cuentas, todos los elementos de datos procesados por una computadora están reducidos a combinaciones de _______________ y _________________.

b. El elemento de datos más pequeño que una computadora puede procesar se llama _________________.

c. Un ______________ es un grupo de registros relacionados.

d. Los dígitos, letras y símbolos especiales se mencionan como ____________________.

e. A un grupo de archivos relacionados se le llama _______________________.

f. La función miembro _______________ de las clases de flujo de archivo fstream, ifstream y ofstream cierra un archivo.

g. La función miembro _______________ de istream lee un carácter desde el flujo especificado.

h. Las funciones miembro _____________ y _______________ de istream leen una línea desde el flujo especificado.

i. La función miembro _____________ de las clases de flujo de archivo fstream, ifstream y ofstream abre un archivo.

j. La función miembro _____________ de istream se utiliza normalmente cuando se leen datos desde un archivo en aplicaciones de acceso aleatorio.

Page 64: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-64

k. Las funciones miembro ______________ y ____________ de las clases istream y ostream colocan el apuntador de posición adecuado a una ubicación especifica en un flujo de entrada o salida, respectivamente.

26. Indique cuáles de las siguientes afirmaciones son verdaderas y cuáles falsas (si la respuesta es falso, explique por qué):

a. La función miembro read no puede utilizarse para leer datos desde el objeto de entrada cin.

b. El programador debe crear explícitamente los objetos cin, cout, cerr y colg.

c. Un programa debe llamar explícitamente a la función close para cerrar un archivo que esté asociado con un objeto ifstream, ofstream o fstream.

d. Si el apuntador de posición de archivo apunta a una posición de un archivo secuencial diferente al inicio del archivo, el archivo debe cerrarse y volverse a abrir para leer desde el inicio del archivo.

e. La función miembro write de ostream puede escribir hacia el flujo de salida estándar cout.

f. Los datos que están en archivos de acceso secuencial se actualizan siempre sin sobrescribir los datos contiguos.

g. No es necesario buscar por todos los registros en un archivo de acceso aleatorio para encontrar un registro específico.

h. Los registros en los archivos de acceso aleatorio deben ser de longitud fija.

i. Las funciones miembro seekp y seekg deben buscar con relación al inicio del archivo.

27. Suponga que cada uno de los siguientes enunciados se aplica al mismo programa.

a. Escriba una instrucción que abra el archivo oldmast.dat para entrada. Utilice el objeto inOldMaster de ifstream.

b. Escriba una instrucción que abra el archivo trans.dat para entrada. Utilice el objeto inTransaction de ifstream.

c. Escriba una instrucción que abra el archivo newmast.dat para salida (y creación). Utilice el objeto outNewMaster de ofstream.

d. Escriba una instrucción que lea un registro desde el archivo oldmast.dat. El registro consiste del entero accountNum, la cadena name y el punto flotante currenBalance. Utilice el objeto inOldMaster de ifstream.

e. Escriba una instrucción que lea un registro desde el archivo trans.dat. El registro consiste del entero accountNum y un punto flotante dollarAmount. Utilice el objeto inTransaction de ifstream.

f. Escriba una instrucción que escriba un registro en el archivo newmast.dat. El registro consiste de un entero accountNum, una cadena name y un punto flotante currentBalance. Utilice el objeto outNewMaster de ofstream.

28. Encuentre el error y muestre cómo corregirlo en cada uno de los siguientes ejemplos.

a. El archivo pagables.dat al que hace referencia el objeto salidaPagable de ofstream no se ha abierto.

salidaPagable << cuenta << compania << importe << endl;

b. La siguiente instrucción debe leer un registro del archivo pagables.dat. El objeto entradaPagable de ifstream hace referencia a este archivo y el objeto entradaRecibidos de istream hace referencia al archivo recibidos.dat.

entradaRecibidos >> cuenta >> compania >> importe; c. El archivo herramientas.dat debe estar abierto para agregar datos al archivo sin descartar los datos actuales.

Ofstream salidaHerramientas(“herramientas.dat”, ios::out);

29. Llene los espacios en blanco en cada una de las siguientes frases. a. Las computadoras almacenan grandes cantidades de datos en dispositivos de almacenamiento secundario como

__________.

b. Un ____________ está compuesto de varios campos.

c. A un campo que sólo puede contener dígitos, letras y espacios en blanco se le llama campo ________________.

d. Para facilitar la recuperación de registros específicos de un archivo, se elige un campo en cada registro como una ______________.

e. La gran mayoría de información que está almacenada en sistemas de computadora está guardada en archivos ___________.

f. A un grupo de caracteres relacionados que tienen significado se le llama un ________________.

g. Los objetos de flujo estándar que están declarados en el archivo de encabezado <iostream.h> son ____________. _________ y ____________.

h. La función miembro ______________ de ostream envía a la salida un carácter en el flujo especificado.

i. La función miembro _____________ de ostream se utiliza generalmente para escribir datos a un archivo accedido en forma aleatoria.

j. La función miembro _____________ de istream reposiciona e l apuntador de posición de archivo en un archivo.

Page 65: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-65

30. Indique cuáles de las siguientes afirmaciones son verdaderas y cuáles falsas (si la respuesta es falso, explique por qué): a. Las funciones impresionantes que realizan las computadoras involucran esencialmente la manipulación de ceros y

unos.

b. La gente prefiere manejar bits en vez de caracteres y campos, debido a que los bits son más compactos.

c. La gente especifica programas y elementos de datos como caracteres, y luego las computadoras manipulan y procesan estos caracteres como grupos de ceros y unos.

d. El código postal de cinco dígitos de una persona es un ejemplo de un campo numérico.

e. La dirección de la calle de una persona se considera, por lo general, un campo alfabético en las aplicaciones de computadora.

f. Los elementos de datos que están representados en las computadoras forman una jerarquía de datos en la cual los elementos de datos se hacen cada vez más grandes y más complejos conforme se avanza de campos a caracteres, a bits, etcétera.

g. Una clave de registro identifica a un registro como perteneciente a un campo particular.

h. La mayoría de las organizaciones almacenan toda su información en un solo archivo para facilitar el procesamiento en computadora.

i. Cada instrucción que procesa un archivo en un programa C++ hace referencia explícitamente a ese archivo por nombre.

j. Cuando un programa crea un archivo, la computadora almacena automáticamente dicho archivo para referencia futura.

PROBLEMAS 1. Escriba un programa que le pida al usuario que introduzca dos números, que obtenga dichos números y que

imprima la suma, el producto, la diferencia y el cociente de ambos. 2. Escriba un programa para calcular el interés simple sobre un préstamo de 2000 pesos a dos años, a una tasa de

12.5%. Dé formato a su salida en forma apropiada, mostrando el importe del préstamo, el periodo de tiempo, la tasa de interés y la importe del interés (Nota: establezca la bandera ios::showpoint para asegurar en forma apropiada el formato de pesos y centavos para una salida monetaria).

3. Escriba un programa que pida al usuario escribir cualquier palabra de cuatro letras. Después muestre la palabra al revés.

4. La fuerza eléctrica en un circuito de corriente directa se define como el producto del voltaje y la corriente. En símbolos, fuerza = voltaje x corriente. Escriba un programa para calcular la fuerza eléctrica de un valor de voltaje de 12 volts y un valor de corriente de 0.00125 amperes. Genere una tabla de valores de entrada y salida en forma decimal.

5. Escriba un programa que emplee un ciclo para leer un archivo de carácter de nombre letminus que consista en todos los caracteres minúsculos. Convierta los caracteres minúsculos a mayúsculas restado 32 a cada carácter y escriba los caracteres mayúsculos a un archivo de nombre letmayus. (Nota: deberá crear el archivo letminus usando su editor de texto ASCII)

6. Escriba un programa que calcule los valores de fuerza y voltaje escritos por el usuario. Genere una tabla de valores de entrada y salida en formato decimal.

7. Escriba un programa que calcule el pago semanal bruto para un empleado, determinando el salario mínimo y el número de horas trabajadas. Suponga que el empleado es de medio tiempo y además trabaja menos de 40 horas a la semana. Genere una pantalla mostrando el nombre del empleado, el pago por hora, horas trabajadas y pago bruto. Proporcione los títulos apropiados en pantalla. (Nota: establezca la bandera ios::showpoint para asegurar la salida adecuada en formato monetario de pesos y centavos.)

8. Escriba un programa para calcular la circunferencia y área de un círculo a partir de una entrada del radio escrita por el usuario. Genere una pantalla tabular mo strando el radio del círculo, la circunferencia y el área. (Nota: La circunferencia de un círculo es igual a 2 ππ r. El área de un círculo = ππ r2.

9. Escriba un programa que permita a un estudiante calcular el promedio de cuatro registros de exámenes. Genere una pantalla tabular con el nombre del estudiante, nombre del curso, registros individuales de los exámenes y promedio de exámenes.

Page 66: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-66

10. El equipo de boliche “cuatro escuadras” tiene cuatro jugadores. En una noche de juego, cada miembro hace tres juegos. Escriba un programa que lea la fecha, nombre del jugador y registro individual de juego para cada participante. Usando la información de entrada, muestre una pantalla del informe de boliche. El informe debe mostrar la fecha en la parte superior de la pantalla y después una tabla mostrando los registros, total y promedio entero de cada uno de los jugadores.

11. Escriba un programa que calcule la resistencia equivalente de un circuito de cinco resistencias en serie escritas por el usuario. Genere una tabla de valores de entrada y salida en formato decimal. (Nota: la resistencia equivalente de un circuito en serie se obtiene sumando las resistencias individuales.)

12. Escriba un programa que calcule la resistencia equivalente de dos resistencias paralelas escritas por el usuario. Genere una tabla de valores de entrada y salida en formato decimal. Use la siguiente fórmula para calcular el valor de resistencia equivalente:

Requiv = (R1 x R2) / (R1 + R2)

Observe el uso de paréntesis para agrupar las cantidades en esta ecuación ¿Por qué supone que sea necesario?.

13. Escriba un programa que pruebe la entrada de valores enteros en formato decimal, octal y hexadecimal. Envíe a la salida a cada entero leído por el programa en los tres formatos. Pruebe el programa con los siguientes datos de entrada: 10, 010, 0x10.

14. Escriba un programa que introduzca una cadena desde el teclado y determine su longitud. Imprima dicha cadena utilizando una longitud del doble de la anchura del campo.

15. En algunos lenguajes de programación se introducen cadenas rodeadas de comillas sencillas o dobles. Escriba un programa que lea las tres cadenas Susana, “Susana” y ‘Susana’. ¿Las comillas sencillas o dobles se ignoran o se leen como parte de la cadena?

16. Escriba un programa para mostrar que las funciones miembro getline() y el get() de tres argumentos de istream hacen que la cadena de entrada termine con un carácter nulo de terminación de cadena. También muestre que get() deja el carácter delimitador en el flujo de entrada y, en cambio getline() extrae el carácter delimitador y lo descarta. ¿Qué pasa con los caracteres que no son leídos en el flujo?

17. Escriba un programa que cree el manipulador saltaBlanco definido por el usuario para saltarse los caracteres de espacio en blanco iniciales en el flujo de entrada. El manipulador deberá utilizar la función isspace() de la biblioteca ctype.h para revisar si el carácter es un espacio en blanco. A cada carácter se le deberá introducir mediante la función miembro get() de istream. Cuando encuentre un carácter que no sea espacio en blanco, el manipulador saltaBlanco terminará su trabajo colocando el carácter de devolución en el flujo de entrada y devolviendo una referencia a istream.

18. La pregunta 27 le pide al lector que escriba una serie de instrucciones. De hecho, estas instrucciones forman la parte medular de un tipo importante de programa de procesamiento de archivos, es decir, un programa de concordancia de archivos. En el procesamiento de datos comerciales es común tener varios archivos en cada sistema de aplicación. Por ejemplo, en un sistema de cuentas por cobrar hay generalmente un sistema de archivo maestro que contiene información detallada acerca de cada cliente, tal como el nombre, dirección, número telefónico, saldo pendiente, límite de crédito, condiciones de descuento, acuerdos contractuales y, posiblemente, una historia condensada de las compras recientes y sus pagos realizados.

Conforme suceden transacciones (es decir, se hacen ventas y llegan por correo los pagos en efectivo), éstas se introducen en un archivo. Al final de cada periodo del negocios (es decir, un mes para algunas compañías, una semana para otras y un día en algunos casos) se aplica el archivo de transacciones (llamado trans.dat en la pregunta 27) al archivo maestro (llamado oldmast.dat en la pregunta 27) y, por lo tanto, se actualiza el registro de compras y pagos de cada cuenta. Durante una ejecución de actualización se vuelve a escribir el archivo maestro como un nuevo archivo (newmast.dat), el cual luego se utiliza al final del siguiente periodo del negocio para iniciar nuevamente el proceso de actualización.

Los programas de concordancia de archivo deben manejar determinados problemas que no existen en los programas de un solo archivo. Por ejemplo, no siempre sucede una concordancia. Puede ser que un cliente que esté en el archivo maestro no haya hecho ninguna compra o pago en efectivo en el periodo actual del negocio y, por lo tanto, no aparecerá ningún registro para este cliente en un archivo de transacciones. En forma similar, un cliente que hizo alguna compra o pago en efectivo puede haberse cambiado a esta comunidad, y puede ser que la compañía todavía no haya tenido oportunidad de crear un registro maestro para este cliente.

Page 67: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-67

Utilice las instrucciones que se escribieron en la pregunta 27 como base para escribir un programa completo de concordancia de archivo de cuentas por cobrar. Utilice el número de cuenta de cada archivo como clave de registro para efectos de concordancia. Suponga que cada archivo es un archivo secuencial con los registros almacenados en orden ascendente por número de cuenta.

Cuando suceda una concordancia (es decir, cuando aparezcan registros con el mismo número de cuenta en el archivo maestro y en el archivo de transacciones) súmele el importe en dólares que está en el archivo de transacciones al saldo actual, que está en el archivo maestro, y escriba el registro del newmast.dat. (Suponga que las compras están indicadas por cantidades positivas en el archivo de transacciones y que los pagos están indicados por cantidades negativas). Cuando hay un registro maestro para una cuenta particular, pero no hay registro de transacciones correspondiente, escriba simplemente el registro maestro a newmast.dat. Cuando haya un registro de transacción, pero no hay registro maestro correspondiente, imprima el mensaje registro de transacción no concuerda con uno maestro... (llene el número de cuenta a partir del registro de transacciones)

19. Después de escribir el programa del problema 18 escriba un programa simple para crear algunos datos de prueba para la revisión del programa. Utilice los siguientes datos de cuentas por ejemplo:

Número de cuenta En archivo maestro

Nombre

Saldo

100 Juan Pérez 348.17

300 María Candelaria 27.19

500 Samuel Hernández 0.00

700 Susana Dosamantes -14.22

Número de cuenta En archivo de transacciones

Cantidad de la transacción

100 27.14

300 62.11

400 100.56

900 82.17

20. Ejecute el programa del problema 18 utilizando los archivos de datos de prueba que se crearon en el problema 19. Imprima el nuevo archivo maestro. Revise que las cuentas se hayan actualizado correctamente.

21. Es posible (y de hecho es común) tener varios registros de transacciones que tengan la misma clave de registro. Esto sucede debido a que un cliente particular puede hacer varias compras y pagos en efectivo durante un periodo del negocio. Vuelva a escribir el programa de concordancia de archivos de cuentas por cobrar del problema 18 para tomar en cuenta la posibilidad de manejar varios registros de transacciones que tengan la misma clave de registro. Modifique los datos de prueba del problema 19 para incluir los registros de transacciones adicionales siguientes:

Número de cuenta En archivo de transacciones

Cantidad de la transacción

300 83.79

700 80.78

700 1.53

Page 68: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-68

22. Escriba una serie de instrucciones que logren cada una de las siguientes cosas. Suponga que se ha definido la estructura: struct persona {

char apellido[15]; char nombre[15]; char edad[2];

};

y que el archivo de acceso aleatorio se ha abierto adecuadamente.

a. Inicialice el archivo nombreEdad.dat con 100 registros que contengan apellido = “no asignado”, nombre = “ “ y edad = “0”.

b. Introduzca 10 apellidos, nombres y edades y escríbalos en el archivo.

c. Actualice un registro que tenga información, y si no hay ninguna, dígale al usuario “Registro sin información!.

d. Borre el registro que tenga información volviendo a inicializar ese registro particular.

23. Usted es el propietario de una ferretería y necesita llevar un inventario que pueda decirle qué tantas herramientas diferentes tiene, qué tantas hay en existencia y el costo de cada una. Escriba un programa que inicialice el archivo de acceso aleatorio hardware.dat con 100 registros vacíos, permita que se introduzcan los datos relacionados con cada herramienta, que se listen todas las herramientas, que se borre un registro para una herramienta que ya no se tenga y que se actualice cualquier información que haya en el archivo. El número de identificación de cada herramienta deberá ser el número de registro. Utilice la siguiente información para iniciar el archivo.

# de registro Nombre de la herramienta Cantidad Costo 3 Lijadora eléctrica 7 57.98

17 Martillo 76 11.99

24 Sierra 21 11.00

39 Cortadora de césped 3 79.50

56 Pinzas 18 99.99

68 Desarmador 106 6.99

77 Almádena 11 21.50

83 Llave de tuerca 34 7.50

24. Escriba un programa que utilice el operador sizeof() para determinar los tamaños en bytes de los diversos tipos de datos que hay en el sistema de cómputo que usted tiene. Escriba los resultados en el archivo tamdato.dat para que pueda imprimir posteriormente el resultado. El formato de los resultados en un archivo debe ser:

Tipo de dato Tamaño char 1

unsigned char 1

short int 2

unsigned short int 2 int 4

unsigned int 4

long int 4

unsigned long int 4

float 4

Page 69: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-69

double 8 long double 16

Nota: Los tamaños de los tipos de datos integrados en su computadora pueden ser diferentes de los que se listan anteriormente.

EXAMEN BREVE 17

1. El operador que deberá emplear para extraer datos del flujo de entrada cin es el operador ___________________________.

2. Escriba los enunciados para pedir al usuario que ingrese el valor para una variable entera llamada numero. Use cin para leer la entrada del usuario.

3. Dé algunos ejemplos de espacios en blanco. 4. Escriba un enunciado para leer un carácter de espacio en blanco y almacénelo en una variable

llamada espacioBlanco.

5. Escriba un enunciado para mostrar el carácter de espacio en blanco leído en la pregunta 4. 6. Cierto o falso: cuando se leen datos de un solo carácter, cin >> sólo leerá un carácter a la vez.

7. ¿Cuándo termina el enunciado cin, si se usa el operador >> para leer datos de cadenas de carácter? 8. ¿Qué función puede emplearse con cin para leer datos de cadena que incluyan espacios en blanco? 9. ¿Cuándo debe usarse gets() o fgets() en lugar de cin para leer datos de cadenas de caracteres?

10. Utilice la función gets() para leer una cadena de hasta 25 caracteres y almacenarla en una variable denominada nombre.

11. ¿Cómo trata gets() el carácter CRLF? 12. ¿Cómo trata fgets() el carácter CRLF?

EXAMEN BREVE 18 1. ¿Qué archivo de encabezado deberá incluirse para leer/escribir archivos en disco? 2. ¿Qué clase se usa para definir objetos de archivo de entrada? 3. La clase que se usa para definir objetos de archivo de salida es: ________________.

4. ¿Cuáles son las tres tareas que deben ejecutarse siempre para leer o escribir cualquier archivo en disco?

5. Defina un objeto de nombre miEntrada como un objeto de archivo de entrada y uno de nombre miSalida como un objeto de archivo de salida.

6. Escriba los enunciados para abrir un archivo de disco llamado datos y procesarlo por medio del objeto de archivo de entrada de la pregunta 5 y a un archivo en disco llamado resultado para procesarlo durante el uso del objeto de archivo de salida de la pregunta 5.

7. Suponga que el archivo llamado datos en la pregunta 6 contiene un número desconocido de enteros. Escriba el código que se necesita para leer cada entero en el archivo, multiplicarlo por 10 y escriba el producto en el archivo resultado en la pregunta 6.

8. Combine todas sus respuestas para las preguntas anteriores dentro de un programa sencillo en C++ que realizará las tareas de procesamiento de archivos indicadas.

Page 70: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-70

RESPUESTAS EXAMEN BREVE 17 1. El operador que deberá emplearse para extraer datos del flujo de entrada cin es el operador >>. 2. Los siguientes enunciados pedirán al usuario que introduzca un valor para una variable entera

llamada numero usando cin para leer la entrada del usuario. cout << “Por favor escriba un número entero: ” << ; cin >> numero;

3. Los espacios , los tabuladores, las líneas nuevas (CRLF) y los retornos de carro se consideran espacios en blanco.

4. cin.get(espacioBlanco); 5. cout.put(espacioBlanco) 6. Cierto. Cuando se lee un dato de tipo carácter, cin leerá solamente un carácter a la vez.

7. Cuándo encuentre el espacio en blanco 8. La función getline() se puede usar como cin.getline() para incluir espacio en blanco cuando se lea

una cadena de datos.

9. La función gets o fgets() se debe usar en lugar de cin cuando se lea una cadena de datos después de leer datos numéricos o de carácter.

10. char nombre[26]; gets(nombre);

11. Convierte el carácter CRLF en un terminador nulo.

12. Lee y almacena el carácter CRLF y después agrega un terminador nulo.

RESPUESTAS EXAMEN BREVE 18 1. El archivo de cabecera fstream.h debe incluirse para leer o escribir archivos en disco. 2. La clase que se utiliza para definir los objetos de archivo de entrada es la ifstream.

3. La clase que se utiliza para definir los objetos de archivo de salida es la ofstream. 4. Las tareas que siempre debe ejecutar para procesar cualquier archivo en disco son: (1) definir el

objeto de archivo, (2) abrir un archivo y unirlo al objeto y (3) cerrar el archivo.

5. ifstream miEntrada; ofstream miSalida;

6. miEntrada.open(“datos”); miSalida.open(“resultado”);

7. // Lee el primer entero miEntrada >> entero; // Lee y procesa los datos del archivo usando un ciclo while(!miEntrada.eof()) { // Inicio del ciclo vecesDiez = 10 * entero; // multiplica el entero por 10 miEntrada >> entero; // Lee un entero del archivo de entrada. miSalida << vecesDiez << endl; // Escribe el producto a un archivo de salida.

Page 71: CONTENIDO DE LA LECCIÓN 8

MIGUEL Á. TOLEDO MARTÍNEZ

FUNDAMENTOS – LECCIÓN 7 7-71

}

8. #include <fstream.h> // Para archivo de E/S void main(void) { // Define variables int entero; // Variable entera para datos de entrada int vecesDiez; // Variable entera para el producto 10 veces // Define los archivos objeto y archivos abiertos ifstream miEntrada; // Define objeto entrada ofstream miSalida; // Define objeto salida miEntrada.open(“datos”); // Abre archivo de entrada miSalida.open(“resultado”); // Abre archivo de salida // Lee el primer entero miEntrada >> entero; // Lee y procesa los datos del archivo utilizando un ciclo while(!miEntrada.eof()) { // Inicio del ciclo vecesDiez = 10 * entero; // multiplica el entero por 10 miEntrada >> entero; // Lee un entero del archivo de entrada. miSalida << vecesDiez << endl; // Escribe el producto a un archivo de

// salida. }

miEntrada.close(); miSalida.close(); }