06.05.2013 Views

CONTENIDO DE LA LECCIÓN 8

CONTENIDO DE LA LECCIÓN 8

CONTENIDO DE LA LECCIÓN 8

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

MIGUEL Á. TOLEDO MARTÍNEZ<br />

<strong>CONTENIDO</strong> <strong>DE</strong> <strong>LA</strong> <strong>LECCIÓN</strong> 8<br />

INTRODUCCIÓN <strong>DE</strong> DATOS POR TEC<strong>LA</strong>DO<br />

1. Introducción 3<br />

2. Clase y objeto de entrada de flujo 3<br />

3. Entrada de flujo 6<br />

3.1. Operador de extracción de flujo 6<br />

3.1.1. Ejemplos 7.1, 7.2, 7.3, 7.4, 7.5 6<br />

3.2. Posibles errores 8<br />

3.3. Lectura de tipos de datos mezclados 9<br />

3.3.1. Ejemplo 7.6 9<br />

3.4. Lectura de datos de un solo carácter 10<br />

3.4.1. Ejemplo 7.7 10<br />

3.5. Uso de la función miembro get() para leer datos de un solo carácter 12<br />

3.5.1. Ejemplos 7.8, 7.9, 7.10 13<br />

3.6. Lectura de cadena de caracteres 16<br />

3.6.1. Ejemplo 7.11 16<br />

3.7. Uso de getline() para leer cadenas de caracteres 17<br />

3.7.1. Ejemplos 7.12, 7.13, 7.14 17<br />

3.7.2. Un posible problema cuando se usa getline() 20<br />

3.7.2.1. Ejemplos 7.15, 7.16, 7.17 20<br />

3.8. Uso de gets() y fgets() para leer cadenas 23<br />

3.8.1. Ejemplo 7.18 23<br />

3.9. Examen breve 17 69<br />

3.10. Las funciones miembro peek(), putback() e ignore() de istream 25<br />

3.11. E/S a prueba de tipos 25<br />

4. Procesamiento de archivos 25<br />

4.1. La jerarquía de datos 25<br />

4.2. Archivos y flujos 28<br />

4.3. Clases: la base de los archivos C++ 29<br />

4.3.1. Ejemplo 7.19 30<br />

4.4. Lectura y escritura de un archivo en disco 31<br />

4.4.1. Ejemplos 7.20, 7.21 31<br />

4.5. El uso de ciclos para leer y procesar archivos 33<br />

4.5.1. Ejemplo 7.22 34<br />

4.6. Examen breve 18 69<br />

4.7. Creación de un archivo de acceso secuencial 35<br />

4.7.1. Ejemplo 7.23 35<br />

4.8. Lectura de datos de un archivo de acceso secuencial 39<br />

4.8.1. Ejemplos 7.24, 7.25 39<br />

4.9. Actualización de archivos de acceso secuencial 43<br />

4.10. Archivos de acceso aleatorio 44<br />

4.11. Creación de un archivo de acceso aleatorio 45<br />

4.11.1. Ejemplo 7.26 46<br />

4.12. Escritura aleatoria de datos a un archivo de acceso aleatorio 47<br />

4.12.1. Ejemplo 7.27 47<br />

4.13. Lectura secuencial de datos desde un archivo de acceso aleatorio 48<br />

4.13.1. Ejemplo 7.28 48<br />

4.14. Ejemplo: un programa de procesamiento de transacciones 50<br />

4.14.1. Ejemplo 7.29 50<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-1


MIGUEL Á. TOLEDO MARTÍNEZ<br />

5. Solución de problemas en acción: Cálculo de voltaje 55<br />

5.1. Problema 55<br />

5.2. Definición del problema 568<br />

5.3. Planeación de la solución 56<br />

5.4. Codificación del programa 57<br />

6. Lo que necesita saber 59<br />

7. Preguntas y problemas 62<br />

7.1. Preguntas 62<br />

7.2. Problemas 65<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-2


MIGUEL Á. TOLEDO MARTÍNEZ<br />

<strong>LECCIÓN</strong> 8<br />

INTRODUCCIÓN <strong>DE</strong> DATOS POR TEC<strong>LA</strong>DO<br />

INTRODUCCIÓN<br />

En esta lección aprenderá que C++ proporciona un flujo de entrada llamado cin<br />

mediante el cual su programa puede leer información introducida desde el teclado. Cuando<br />

utilice cin para leer entrada desde el teclado, debe especificar una o más variables hacia la que<br />

desea que la entrada sea asignada.<br />

Objetivos de esta lección:<br />

• Utilizar cin para leer letras y números desde el teclado.<br />

• Utilizar las funciones miembro get(), getline() para leer cadenas.<br />

• Utilizar las funciones gets() y fgets() para leer cadenas.<br />

• Utilizar las funciones miembro peek(), putback() e ignore()<br />

• Aprender a crear, leer, escribir y actualizar archivos en disco.<br />

• Utilizar ciclos para leer y procesar archivos.<br />

• Familiarizarse con el procesamiento de archivo de acceso secuencial.<br />

• Aprender a especificar operaciones de E/S no formateadas de alto desempeño.<br />

• Comprender las diferencias entre el procesamiento de archivos de datos formateados y el<br />

procesamiento de archivos de datos sin formato.<br />

• Construir un programa de procesamiento de transacciones con procesamiento de archivos de<br />

acceso aleatorio.<br />

Cuando sus programas utilizan el flujo de salida cout, coloca datos en el flujo utilizando<br />

el operador de inserción ()<br />

El almacenamiento de datos en variables y arreglos es temporal. Los archivos se utilizan<br />

para la retención permanente de grandes cantidades de datos. Las computadoras almacenan los<br />

archivos en dispositivos de almacenamiento secundario, tales como discos magnéticos, discos<br />

ópticos y cintas. En esta lección explicaremos la manera en que los archivos de datos se crean,<br />

actualizan y procesan mediante programas C++. Consideraremos a los archivos de acceso<br />

secuencial y a los de acceso aleatorio. Compararemos el procesamiento de archivos de datos<br />

formateados y el procesamiento de archivos de datos sin formato.<br />

C<strong>LA</strong>SE Y OBJETO <strong>DE</strong> ENTRADA <strong>DE</strong> FLUJO<br />

La obtención de información dentro de un programa para procesamiento se llama lectura.<br />

En la mayoría de los sistemas actuales, la información se lee desde una de dos fuentes: el teclado<br />

o un archivo en disco.<br />

El primer enunciado C++ que usará para leer datos desde el teclado es cin (se pronuncia<br />

como si fueran dos palabras: c-in; “ci in”, en lenguaje fonético) Como cout, cin es un objeto de<br />

flujo, predefinido en C++, como parte del archivo de encabezado iostream.h. El objeto cin es un<br />

flujo de entrada asociado, de manera predeterminada, al teclado de su sistema.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-3


MIGUEL Á. TOLEDO MARTÍNEZ<br />

cin es un objeto de la clase istream, y se dice que está enlazado (o conectado) al<br />

dispositivo de entrada estándar que normalmente es el teclado. El operador de extracción de<br />

flujo, como se utiliza en la siguiente instrucción, causa que un valor para la variable entera<br />

calificación (suponiendo que calificación ha sido declarada como int) se reciba desde cin hacia<br />

la memoria:<br />

cin >> calificación;<br />

Observe que la operación de extracción de flujo es lo suficientemente inteligente para<br />

saber el tipo de datos que es. Suponiendo que calificación se haya declarado adecuadamente, no<br />

se necesita especificar información adicional para utilizarla con dicho operador (como es el caso,<br />

por coincidencia, en la E/S estilo C)<br />

Antes de que comprenda cómo trabaja este enunciado debe saber un poco más de cómo C++<br />

ve una línea de datos. Suponga que escribe dos líneas de datos como sigue:<br />

74 92 88¿<br />

23 45 16↵<br />

Cuando escribe los datos anteriores desde el teclado, debe colocar cada número en forma<br />

consecutiva, separando los números con uno o más espacios. Conforme se ingresan los valores,<br />

éstos se almacenan en el flujo de memoria temporal (buffer) cin. Al final de la línea se deberá<br />

presionar ENTER (¿) ¿Cómo sabe el sistema que finaliza un elemento de datos y empieza otro?<br />

Correcto, el espacio en blanco (blancos) entre los elementos de datos separa un elemento de<br />

otro. Después, ¿Cómo reconoce el sistema el fin de la línea de datos? Correcto otra vez, al<br />

presionar la tecla ¿ que define el final de la línea e introduce un CRLF dentro de la memoria<br />

temporal de flujo. En la figura 7.1 se ilustra la memoria temporal de flujo y su contenido<br />

después de que esta operación ingresa los datos:<br />

74 92 88 CRLF 23 45 16 CRLF<br />

Flujo de entrada cin<br />

Carácter en blanco<br />

74 92 88¿<br />

23 45 16¿<br />

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

El formato general para cin es el siguiente:<br />

cin >> variable;<br />

TEC<strong>LA</strong>DO<br />

Observe como después del objeto cin sigue un doble pico paréntesis a la derecha, el<br />

operador de extracción de flujo, >>, seguido por la variable a la que asignará los datos<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-4


MIGUEL Á. TOLEDO MARTÍNEZ<br />

introducidos. Decimos que el operador >> extrae los datos que se van a leer de los datos de<br />

entrada en la memoria temporal de flujo. Posteriormente, los datos de entrada se asignan a la<br />

variable escrita en el enunciado cin. Por supuesto, la variable deberá estar definida como un tipo<br />

de datos legal antes de utilizarla en el enunciado cin.<br />

Por ejemplo, suponga que se han definido tres variables de clase entero llamadas<br />

registro1, registro2 y registro3. Para introducir datos en las cada una de las variables, deberán<br />

insertarse tres enunciados cin dentro del programa, como los siguientes:<br />

cin >> registro1;<br />

cin >> registro2;<br />

cin >> registro3;<br />

Cuando C++ encuentre los enunciados anteriores en el programa, detendrá la ejecución<br />

hasta que el usuario escriba los datos solicitados. Ahora suponga que el usuario, desde el teclado,<br />

escribe lo siguiente;<br />

74¿<br />

92¿<br />

88↵<br />

¿Qué supone que pasará? Correcto, otra vez, el valor 74 se extraerá del flujo cin y se<br />

asignará a registro1, se extraerá el valor 92 y se asignará a registro2 y se extraerá el valor 88 y se<br />

asignará a registro3. De esta manera, se puede pensar en la operación cin como una operación de<br />

asignación. Cuando se use el operador >>, un valor escrito desde el teclado será extraído del<br />

flujo de entrada y será asignado a la variable listada en el enunciado cin. En la figura 7.2 se<br />

muestra el contenido de la memoria temporal para estas entradas.<br />

74 CRLF 92 CRLF 88 CRLF<br />

Flujo de entrada 74¿<br />

92¿<br />

88¿<br />

Teclado<br />

Figura 7.2. Flujo de entrada de datos.<br />

El usuario también puede escribir estos mismos valores en una línea sencilla, como la<br />

siguiente:<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-5


MIGUEL Á. TOLEDO MARTÍNEZ<br />

74 92 88 CRLF<br />

74 92 88↵<br />

Flujo de entrada 74 92 88¿<br />

Carácter en blanco<br />

Las mismas asignaciones de variables ocurrirán para ambas entradas del usuario, porque<br />

el operador de extracción de flujo >> ignora todos los espacios en blanco, incluyendo los CRLF.<br />

Nota: Es posible usar el flujo cin con el operador >> para leer varias variables como parte de un<br />

enunciado sencillo. Por ejemplo, el enunciado:<br />

ENTRADA <strong>DE</strong> FLUJO<br />

cin >> registro1 >> registro2 >> registro3;<br />

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

teclado se asignan uno a uno a las variables listadas en el enunciado cin. El orden de asignación es<br />

el orden respectivo de los datos de entrada y el listado de las variables.<br />

Ahora consideremos la entrada de flujo. Esta puede realizarse mediante el operador de<br />

extracción de flujo, es decir, el operador >> sobrecargado. Generalmente, dicho operador pasa<br />

por alto los caracteres de espacio en blanco (tales como los espacios en blanco, las tabulaciones<br />

y las nueva líneas) que estén en el flujo de entrada. Más adelante veremos la manera de cambiar<br />

este comportamiento. Cuando el operador de extracción de flujo encuentra el fin de archivo<br />

dentro de un flujo, devuelve cero (false); de lo contrario devuelve una referencia al objeto<br />

mediante el cual es llamado. Cada flujo contiene un conjunto de bits de estado que se utilizan<br />

para controlar el estado del flujo (es decir, estados de formato, de asignación de error, etc.) Si se<br />

introducen datos de tipo erróneo, la extracción de flujo causa que se establezca el failbit del flujo<br />

y si la operación falla que se establezca el badbit de flujo.<br />

OPERADOR <strong>DE</strong> EXTRACCIÓN <strong>DE</strong> FLUJO<br />

Para leer dos enteros se utiliza el objeto cin y el operador de extracción de flujo >><br />

sobrecargado.<br />

Ejemplo 7.1<br />

Teclado<br />

El siguiente programa: CIN1.CPP, calcula la suma de dos enteros introducidos desde el teclado<br />

mediante el operador de extracción de flujo.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-6


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: CIN1.CPP, calcula la suma de dos enteros introducidos desde el teclado<br />

mediante cin y el operador de extracción de flujo.<br />

*/<br />

#include //Para cin y cout<br />

void main(void)<br />

{<br />

Ejemplo 7.2<br />

Ejemplo 7.3<br />

La relativamente alta precedencia de los operadores >> y x >> y;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo 7.4<br />

El siguiente programa: DOSNUMS.CPP, le solicita dos números. El programa asigna los números<br />

a las variables primero y segundo. El programa visualiza los números utilizando cout.<br />

/* El siguiente programa: DOSNUMS.CPP, solicita dos números. El programa asigna<br />

los números a las variables primero y segundo. Posteriormente visualiza los<br />

números utilizando cout.<br />

*/<br />

#include // Para cout<br />

void main(void)<br />

{<br />

Ejemplo 7.5<br />

El siguiente programa: CIN_<strong>LA</strong>RG.CPP, lee un valor numérico de clase long int.<br />

Experimente con números negativos.<br />

POSIBLES ERRORES<br />

int primero, segundo; // Números tecleados<br />

cout > primero >> segundo;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

2, las variables de clase int contienen valores en el rango de –32768 a 32767. Razón por la cual<br />

ocurre un error de sobreflujo. Como ejercicio introduzca las letras ABC y observe lo que ocurre.<br />

LECTURA <strong>DE</strong> TIPOS <strong>DE</strong> DATOS MEZC<strong>LA</strong>DOS<br />

Las dos reglas fundamentales que se aplican para leer cualquier dato son los siguientes:<br />

1. Todos las variables listadas dentro del enunciado cin deben de ser previamente definidos<br />

antes de usarlas en el enunciado.<br />

2. Los tipos de datos escritos para una variable determinada deberán coincidir con las clases de<br />

datos definidos para esta variable.<br />

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

que se haya definido previamente en el programa. La segunda regla necesita una mayor<br />

explicación.<br />

Ejemplo 7.6<br />

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

// El siguiente programa: USOCIN.CPP, muestra el uso de la operación cin.<br />

#include //Para cout<br />

void main(void)<br />

{<br />

// Se definen las variables<br />

int registro1 = 0;<br />

float registro2 = 0.0;<br />

// Pide y lee los datos del usuario<br />

cout > registro1;<br />

cout > registro2;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

requiere tanta escritura como algunos otros lenguajes estructurados. En realidad C++ asignará la<br />

parte entera del primer valor a registro1 y la parte decimal del primer valor a registro2. De esta<br />

manera, la variable registro1 toma el valor 98 y registro2 tomar el valor 0.5.<br />

Después, con el mismo programa, suponga que el usuario escribe la siguiente línea de datos:<br />

98¿<br />

78↵<br />

Ahora no hay problema. Pero ¿Cómo puede ser esto? ¿Por qué el compilador asigna un valor<br />

entero (78) a una variable de punto flotante (registro2)? Está bien, porque los enteros son una<br />

serie de números reales. El compilador simplemente convierte el valor entero en un formato de<br />

punto flotante. De esta manera el valor entero 78 se convierte en el valor de punto flotante 78.0<br />

para almacenarse dentro de la memoria principal de trabajo.<br />

LECTURA <strong>DE</strong> DATOS <strong>DE</strong> UN SOLO CARÁCTER<br />

La lectura de datos numéricos es directa, siempre y cuando se haga uso de las dos reglas<br />

para la lectura de datos. Sin embargo, hay algunas cosas que convienen recordar cuando se leen<br />

datos de carácter usando cin.<br />

Ejemplo 7.7<br />

1. Sólo se lee un carácter a la vez.<br />

2. Los espacios en blanco (espacios, tabuladores, líneas nuevas, retorno de carro, etc.) son<br />

ignorados por cin cuando se usa el operador >>. Sin embargo, es posible leer el espacio en<br />

blanco usando diferentes funciones cin<br />

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

carácter por separado.<br />

El siguiente programa LECCAR.CPP, ilustra el uso de los conceptos anteriores.<br />

// El siguiente programa: LEECAR.CPP, ilustra la lectura de datos de la clase caracter.<br />

#include //Para cout<br />

void main(void)<br />

{<br />

// Define las variables ingresadas y las inicializa con espacio en blanco.<br />

char calificacion1 = ' ';<br />

char calificacion2 = ' ';<br />

char calificacion3 = ' ';<br />

// Lectura/escritura de los datos del usuario<br />

cout > calificacion1;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

El programa anterior define tres variables (calificacion1, calificacion2, calificacion3) de la clase<br />

carácter, las cuales son inicializadas con espacios en blanco. Después el programa lee las tres<br />

variables de clase carácter desde el teclado y repite las variables en la pantalla del monitor. Ahora<br />

vamos a ver que hará el programa con algunos casos de entrada.<br />

Caso 1:<br />

Introduzca una calificación (R,D,C,B,A): A¿<br />

La calificación introducida fue: A<br />

Introduzca una calificación (R,D,C,B,A): B¿<br />

La calificación introducida fue: B<br />

Introduzca una calificación (R,D,C,B,A): C¿<br />

La calificación introducida fue: C<br />

Se observa la salida que se esperaba. El compilador asigna un solo carácter de entrada a la<br />

variable carácter respectiva listada en el enunciado cin.<br />

Caso 2:<br />

El usuario escribe: ABC¿<br />

El sistema muestra: A<br />

B<br />

C<br />

En este caso, el usuario ha escrito los caracteres ‘A’, ‘B’ y ‘C’ en una sola línea. De esta manera,<br />

los tres caracteres se colocan en la memoria temporal del flujo de entrada. El compilador todavía<br />

asigna el carácter A a calificacion1, el carácter B a calificacion2 y el carácter C a calificacion3.<br />

Veamos como funciona: el usuario ha colocado tres caracteres en el flujo de entrada. El primer<br />

enunciado cin extrae solamente el primer carácter (‘A’) y el subsecuente enunciado cout repite el<br />

carácter en la pantalla. Como resultado, el carácter ‘A’ se extrae del flujo, quedando los caracteres<br />

‘B’ y ‘C’. El segundo enunciado cin extrae del flujo el segundo carácter (‘B’), dejando el carácter<br />

‘C’ en el flujo. El siguiente enunciado cout repite ‘B’ en la pantalla. Por último, el tercer<br />

enunciado cin extrae el último carácter (‘C’) del flujo. Este carácter se repite entonces en la<br />

pantalla.<br />

Caso 3:<br />

El usuario escribe: A B C¿<br />

El sistema muestra: A<br />

B<br />

C<br />

Observe que hay espacios en blanco entre los caracteres ‘A’, ‘B’ y ‘C’. Este espacio en blanco es<br />

ignorado por el operador >>. De esta manera, el compilador todavía hace las asignaciones<br />

correctas a las variables respectivas.<br />

Caso 4:<br />

El usuario escribe: 75 92 88¿<br />

El sistema muestra: 7<br />

5<br />

9<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-11


MIGUEL Á. TOLEDO MARTÍNEZ<br />

En este caso, el usuario ha escrito, en una sola línea, tres calificaciones de examen con números y<br />

no con letra. Sin embargo, dado que las variables están definidas como objeto carácter, el sistema<br />

trata los dígitos como caracteres durante la operación de lectura; ve a cada dígito de un número<br />

como un carácter por separado. De esta manera, asigna el carácter ‘7’ a calificacion1, el carácter<br />

‘5’ a calificacion2 y el carácter ‘9’ a calificacion3. Los datos restantes (2 88) no son extraídos del<br />

flujo por los tres enunciados cin, sino que permanecen en la memoria temporal de flujo. Por<br />

supuesto cualquier enunciado cin posterior extraerá todos o parte de los datos restantes. Lo que es<br />

importante recordar es utilizar siempre variables numéricas (entero o punto flotante) para leer<br />

datos numéricos. Como se ha visto, los datos pueden corromperse con facilidad cuando se usan<br />

variables de carácter para leer datos numéricos.<br />

Caso 5:<br />

El usuario escribe: 97.5 73 84¿<br />

El sistema muestra: 9<br />

7<br />

.<br />

Una vez más el usuario ha escrito tres calificaciones numéricas, las cuales son tratadas como<br />

datos de carácter por el programa. De esta manera, se asignan los primeros tres caracteres y la<br />

información restante se deja en la memoria temporal de flujo de entrada. Como puede ver en la<br />

repetición, el carácter ‘9’ se asigna a calificacion1, el carácter ‘7’ a calificacion2 y el punto<br />

decimal se asigna a calificacion3.<br />

USO <strong>DE</strong> <strong>LA</strong> FUNCIÓN MIEMBRO get() PARA LEER DATOS <strong>DE</strong> UN SOLO Carácter<br />

La función miembro get(), sin argumento introduce un carácter desde el flujo designado<br />

(aunque sea espacio en blanco) y devuelve dicho carácter como valor de la llamada de la<br />

función. Esta versión de get devuelve EOF cuando se llega al final del archivo de flujo.<br />

Habrá ocasiones en que quiera congelar la pantalla para que el usuario tome alguna<br />

acción, como presionar la tecla ENTER (¿) Se puede lograr insertando una llamada a la función<br />

get() dentro de su programa junto con una instrucción adecuada, en el punto donde quiera<br />

congelar la salida. El código C++ que se necesita para realizar esta tarea es el siguiente:<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

caso contrario devuelve una referencia al objeto istream para el cual se llamó la función<br />

miembro get()<br />

Ejemplo 7.8<br />

El siguiente programa: CIN4.CPP, ilustra el operador de extracción de flujo devolviendo false (0)<br />

al encontrar el fin de archivo.<br />

/* El siguiente programa: CIN4.CPP, ilustra el uso del operador de extracción de flujo<br />

devolviendo false al encontrar el fin de archivo.<br />

*/<br />

#include // Para cout y cin<br />

void main(void)<br />

{<br />

Ejemplo 7.9<br />

int calificacion, maximaCalificacion = -1;<br />

cout > calificacion)<br />

{<br />

if(calificacion > maximaCalificacion)<br />

maximaCalificacion = calificacion;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

El programa primero imprime el valor de cin.eof(), es decir, false (0 en la salida) para<br />

mostrar que no ha sucedido el fin de archivo en cin. Después el usuario introduce una línea de<br />

texto y oprime Enter (¿) seguido por un fin de archivo (-z en sistemas compatibles con<br />

IBM PC, -d en sistemas UNIX o Macintosh) El programa lee cada carácter y lo manda a<br />

cout utilizando la función miembro put. Cuando se encuentra el fin de archivo, termina el while y<br />

cin.eof(), que ahora es true, se vuelve a imprimir (1 en la salida) para mostrar que el fin de<br />

archivo se estableció en cin. Observe que este programa utiliza la versión de la función miembro<br />

get de istream que no toma argumentos y devuelve el carácter que se introduce.<br />

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

caracteres, un límite de tamaño y un delimitador (con un valor predeterminado de ‘\n’) Esta<br />

versión lee caracteres desde el flujo de entrada. Lee hasta 1 menos que el número máximo<br />

especificado de caracteres y termina, o termina tan pronto como se lee el delimitador. Se inserta<br />

un carácter nulo para terminar la cadena de entrada en el arreglo de caracteres que el programa<br />

utiliza como búfer. El delimitador no se coloca en el arreglo de caracteres, pero permanece en el<br />

flujo de entrada ya que será el siguiente carácter que se lea. Por lo tanto, el resultado de un<br />

segundo get() consecutivo es una línea vacía, a menos que el carácter delimitador se vacíe del<br />

flujo de entrada.<br />

Ejemplo 7.10<br />

El siguiente programa: COMCINGET.CPP, compara la entrada utilizando cin con la extracción<br />

de flujo (la cual lee caracteres hasta que se encuentra un carácter de espacio en blanco) contra la<br />

entrada cin.get() Observe que la llamada a cin.get() no especifica un carácter delimitador y, por lo<br />

tanto, se utiliza el predeterminado ‘\n’.<br />

/* El siguiente programa: COMCINGET.CPP, compara la entrada de una cadena mediante cin y<br />

cin.get().<br />

*/<br />

#include //Para cout y cin<br />

void main(void)<br />

{<br />

Resumiendo:<br />

const int TAMANO = 80;<br />

char bufer1[TAMANO], bufer2[TAMANO];<br />

cout > bufer1;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

una función llamada get() para este propósito. La función get() extraerá cualquier carácter<br />

individual, incluyendo espacios en blanco, desde el flujo de entrada. A esta función se le llama<br />

usando el objeto cin a través del siguiente formato.<br />

cin.get(variable de tipo carácter)<br />

Para llamar a la función get(), preceda cin con un punto, ., seguido por la función get() usando<br />

una variable de clase carácter como argumento. Cuando se le llama, get() extrae un carácter<br />

individual desde el flujo de entrada y lo asigna a la variable carácter listada como su argumento.<br />

En uno de los casos considerados anteriormente para el uso del operador >>, el usuario escribe<br />

tres calificaciones separadas por espacios en blanco, como éstas:<br />

A B C¿<br />

¿Cuántos caracteres en total ocupan estos caracteres en la memoria temporal de flujo? Correcto, 6<br />

caracteres. Hay tres caracteres distintos de espacios en blanco y tres caracteres de espacios en<br />

blanco. Para leer todos estos caracteres se necesitarán 6 llamadas get() como sigue:<br />

cin.get(char1);<br />

cin.get(char2);<br />

cin.get(char3);<br />

cin.get(char4);<br />

cin.get(char5);<br />

cin.get(char6);<br />

Por supuesto, esto supone que las variables, char1, ..., char6, han sido previamente definidos de<br />

clase carácter.<br />

En contraparte con la función get() existe la función put() La función put() inserta un solo<br />

carácter dentro del flujo de salida y es llamada por el objeto cout como sigue:<br />

cout.put(variable de tipo carácter)<br />

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

cout.put(char1);<br />

cout.put(char2);<br />

cout.put(char3);<br />

cout.put(char4);<br />

cout.put(char5);<br />

cout.put(char6);<br />

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

A B C¿<br />

Por supuesto, no se ve el carácter CRLF, pero éste deberá forzar el cursor a la siguiente líneas de<br />

la pantalla. Esto es prueba suficiente de que get() lee espacios en blanco así como caracteres que<br />

no son espacios en blanco.<br />

Como puede ver, la lectura de un solo carácter a la vez impone una limitación severa sobre la<br />

entrada de datos de tipo carácter. Se necesita una variable separada para cada carácter individual<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-15


MIGUEL Á. TOLEDO MARTÍNEZ<br />

que se lee. Debido a que la mayoría de la información de caracteres del mundo real aparece en<br />

forma de cadenas, necesitaremos una forma para leer cadenas.<br />

LECTURA <strong>DE</strong> CA<strong>DE</strong>NA <strong>DE</strong> CARACTERES<br />

Ejemplo 7.11<br />

El siguiente programa CA<strong>DE</strong>NAS1.CPP, ilustra las dificultades que se tienen cuando trata de<br />

usar el objeto cin con el operador >> para leer cadenas de caracteres.<br />

/* El siguiente programa: CA<strong>DE</strong>NAS1.CPP, ilustra como cin lee las cadenas de caracteres<br />

usando el operador >>.<br />

*/<br />

#include //Para cout<br />

const int TAMANO = 31; // Declara el tamaño del arreglo.<br />

void main(void)<br />

{<br />

char nombre[TAMANO] = "\0"; // Define el arreglo de caracteres<br />

cout > nombre;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

.<br />

[30]<br />

Como puede ver, las posiciones del arreglo [0] a [5] contienen la cadena Miguel formada por los<br />

caracteres individuales M, i, g, u, e, l. La posición del arreglo [6] contiene el carácter terminador<br />

nulo ‘\0’. El operador >> inserta el terminador nulo en el arreglo y termina la operación de lectura<br />

cuando encuentra el carácter blanco (espacio en blanco) De esta manera, los demás caracteres que<br />

se ingresaron no son extraídos del flujo, como se muestra por las posiciones restantes del arreglo<br />

que contienen los terminadores nulos de la inicialización. De hecho, si a ésta siguiera otro<br />

enunciado cin, deberá leer el segundo nombre de Miguel (Ángel), dado que el usuario escribió el<br />

nombre completo, coloca Ángel en la memoria temporal de flujo.<br />

Hay algunas formas para resolver el problema anterior. Una de ellas es definir por separado<br />

arreglos de caracteres para cada una de las palabras que serán leídas. En este caso, podrá crear los<br />

siguientes arreglos: primerNombre, segudoNombre, apellidoPaterno y apellidoMaterno, y usar<br />

cuatro enunciados cin >> para leer el primero y el segundo nombre, los apellidos paterno y<br />

materno respectivamente.<br />

USO <strong>DE</strong> getline() PARA LEER CA<strong>DE</strong>NAS <strong>DE</strong> CARACTERES<br />

La función miembro getline() opera en forma similar a la tercera versión de la función<br />

miembro get() e inserta un carácter nulo después de la línea en el arreglo de caracteres. La<br />

función getline() elimina del flujo al delimitador (es decir, lee el carácter y lo descarta), pero no<br />

lo almacena en el arreglo de caracteres.<br />

Ejemplo 7.12<br />

El siguiente programa GETLINE.CPP, ilustra el uso de entrada mediante la función miembro<br />

getline()<br />

/* El siguiente programa: GETLINE.CPP, da entrada a caracteres mediante la función miembro<br />

getline()<br />

*/<br />

#include //Para cout y cin<br />

void main(void)<br />

{<br />

const TAMANO = 80;<br />

char bufer[TAMANO];<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

La función getline() usa tres argumentos. El primero es el identificador de la variable<br />

clase char. Este es el nombre del arreglo de caracteres que se define para almacenar la cadena. El<br />

segundo es el tamaño del arreglo dentro de la cual se leerá la cadena. Recuerde, el tamaño de la<br />

cadena más grande que se leerá dentro de este arreglo es en realidad uno menos que el tamaño<br />

del arreglo, con objeto de dejar espacio para el carácter del terminador nulo ‘\0’. La función<br />

getline() inserta automáticamente el terminador nulo como el último carácter de la cadena. Por<br />

último, el carácter delimitador le dice a la función getline() cuándo terminar la operación de<br />

lectura. El carácter delimitador termina la entrada de la cadena y no se almacenará como parte<br />

de la cadena.<br />

La función getline() lee los caracteres del arreglo, uno a uno, hasta que encuentre el<br />

delimitador específico. Una vez que encuentra el delimitador, la función lo extrae del flujo de<br />

entrada y lo descarta para que no se almacene como parte de la cadena. Si no se especifica<br />

ningún carácter delimitador, su valor predeterminado es el carácter de secuencia de escape ‘\n’<br />

(CRLF)<br />

Ejemplo 7.13<br />

El siguiente programa CA<strong>DE</strong>NAS2.CPP, es una mejora al programa CA<strong>DE</strong>NAS1.CPP, en la que<br />

se utiliza la función cin.getline()<br />

/* El siguiente programa: CA<strong>DE</strong>NAS2.CPP, ilustra como cin lee las cadenas de caracteres<br />

usando la función miembro getline().<br />

*/<br />

#include //Para cout<br />

const int TAMANO = 31; // Declara el tamaño del arreglo.<br />

void main(void)<br />

{<br />

char nombre[TAMANO] = "\0"; // Define el arreglo de caracteres<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

[5] ‘l’ [21] ‘a’<br />

[6] ‘ ‘ [22] ‘r’<br />

[7] ‘A’ [23] ‘t’<br />

[8] ‘n’ [24] ‘í’<br />

[9] ‘g’ [25] ‘n’<br />

[10] ‘e’ [26] ‘e’<br />

[11] ‘l’ [27] ‘z’<br />

[12] ‘ ’ [28] ‘\0’<br />

[13] ‘T’ [29] ‘\0’<br />

[14] ‘o’ [30] ‘\0’<br />

[15] ‘l’ [31] ‘\0’<br />

Observe que el arreglo ahora conserva la cadena completa, incluyendo el espacio en blanco entre<br />

los nombres y apellidos. También note que no se especifica ningún carácter delimitador para la<br />

función getline() Como resultado, la tecla ENTER (CRLF) termina la operación. Sin embargo, el<br />

carácter CRLF (‘\n’) no se almacena en el arreglo como parte de la cadena. Este carácter fue<br />

extraído del flujo y descartado por la función getline()Veamos un ejemplo más de uso del<br />

getline()<br />

Ejemplo 7.14<br />

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

/* El siguiente programa: DIREC.CPP, lee y escribe los datos del usuario:<br />

Nombre, dirección y número telefónico.<br />

*/<br />

#include // Para cout<br />

const TAMANO = 31 // Tamaño del arreglo<br />

void main(void)<br />

{<br />

// Definición de las cadenas de caracteres<br />

char nombre[TAMANO] = "\0";<br />

char calle[TAMANO] = "\0";<br />

char ciudad[21] = "\0";<br />

char estado[12] = "\0";<br />

char codPostal[11] = "\0";<br />

char telefono[14] = "\0";<br />

// Pide al usuario y muestra las cadenas leídas<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Primero observe que cada una de las cadenas se han definido como un arreglo de caracteres. Se ha<br />

definido cada longitud de los arreglos para que sea un carácter más grande que lo necesario. Esto<br />

es para dejar espacio para el carácter del terminador nulo ‘\0’.<br />

UN POSIBLE PROBLEMA CUANDO SE USA getline()<br />

Aunque getline() trabaja cuando se leen cadenas de caracteres en forma consecutiva,<br />

tendrá problemas al tratar de usarla para leer una cadena de caracteres después de haber usado<br />

cin para leer una variable de tipo carácter o una variable numérica.<br />

Ejemplo 7.15<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Cuándo se ejecuta este programa, parece como si C++ saltara sobre el enunciado cin.getline()<br />

¿Cómo puede ser esto? Bien, si vemos el contenido de numero y nombre esto es lo que veríamos<br />

(suponiendo que se introdujo el entero 123):<br />

numero<br />

int 123<br />

nombre<br />

[0] ‘\0’<br />

[1] ‘\0’<br />

Observe que la variable de cadena nombre tiene el terminador nulo en la posición [0] del arreglo<br />

porque cuando se escribe el número 123, se debe oprimir la tecla ENTER (¿) Esto coloca un<br />

carácter CRLF en la memoria temporal de flujo. Sin embargo, el enunciado cin>>numero no<br />

extrae el carácter CRLF (porque es un espacio en blanco) y permanece en la memoria temporal.<br />

Cuando se ejecuta el enunciado cin.getline(nombre,TAMANO), lee la memoria temporal y ve el<br />

carácter CRLF. Como el valor predeterminado del carácter delimitador es justamente CRLF, la<br />

función termina de ejecutarse y agrega el carácter terminador nulo en el arreglo. De este modo, el<br />

usuario nunca tiene la oportunidad de ingresar un nombre.<br />

Existen tres formas básicas de resolver el problema anterior. Una de ellas es especificar un<br />

carácter delimitador diferente en la función getline() Sin embargo, el usuario deberá escribir este<br />

carácter para terminar la operación, con las consecuentes molestias.<br />

Una segunda forma es limpiar la memoria temporal leyendo el carácter CRLF dentro de una<br />

variable temporal después de cualquier dato numérico o carácter y antes de leer cualquier cadena<br />

de datos. Para hacer esto, deberá definirse una variable temporal como arreglo de dos caracteres,<br />

por ejemplo, temporal[2]<br />

Después de usar cin para leer cualquier dato numérico o de un solo carácter emplearemos el<br />

enunciado cin.getline(temporal,2) para leer el caracteres CRLF restante en la memoria temporal<br />

del teclado, de esta manera se limpia la memoria temporal.<br />

Ejemplo 7.16<br />

El siguiente programa: CINGET3.CPP, modifica el programa CINGET2.CPP, empleando una<br />

variable temporal.<br />

/* El siguiente programa: CINGET3.CPP, ilustra como usar una variable temporal<br />

para limpiar la memoria temporal (buffer) del teclado, después de haber leído<br />

una variable numérica.<br />

*/<br />

#include // Para cout<br />

const int TAMANO = 31; // Define el tamaño del arreglo<br />

void main(void)<br />

{<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-21


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Un tercer método es emplear el manipulador de E/S ws (espacio en blanco – whitespace) para<br />

leer cualquier espacio en blanco antes de getline() El manejador ws extrae cualquier espacio en<br />

blanco que haya quedado en la memoria temporal de entrada. Para usarlo, simplemente coloque<br />

en su programa un enunciado cin >> ws antes del enunciado cin.getline.<br />

Ejemplo 7.17<br />

// Definición de variables de entrada<br />

int numero = 0;<br />

char temporal[2] = "\0";<br />

char nombre[TAMANO] = "\0";<br />

// Pregunta al usuario y lee/muestra los datos ingresados<br />

cout > numero;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

USO <strong>DE</strong> gets() Y fgets() PARA LEER CA<strong>DE</strong>NAS<br />

Hay una serie de funciones que manejan la E/S de cadenas en C y C++. Dos de estas<br />

funciones se listan en la tabla.7.1. Estas funciones se proporcionan en el archivo de encabezado<br />

stdio.h. De hecho, hay muchas funciones de E/S en este archivo, pero en este curso solo<br />

necesitaremos emplear las funciones de E/S para cadenas. Las funciones gets() y fgets()<br />

solucionarán el problema que tenemos con getline()<br />

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

Función Descripción<br />

gets() Lee la cadena de entrada. Convierte el CRLF en un<br />

terminador nulo.<br />

fgets() Lee la cadena de entrada. Lee el CRLF y le adiciona<br />

un terminador nulo<br />

Para usar gets() y fgets(), deberá incluir el archivo de cabecera stdio.h y pasar uno o mas<br />

argumentos a la función respectiva. Este es el formato para ambas funciones:<br />

gets();<br />

fgets(, , stdin);<br />

La función gets() sólo necesita que el identificador de la variable cadena pase a la<br />

función. La función fgets() necesita el identificador de la cadena, el tamaño del arreglo de<br />

caracteres y la palabra stdin.<br />

En la tabla 7.1 se puede ver que gets() convertirá el carácter CRLF (producido por la<br />

tecla ENTER) en un terminador nulo. De esta manera, ningún CRLF se almacenará en la cadena<br />

con gets() Sin embargo, fgets() lee el carácter CRLF almacenándolo en el arreglo de caracteres y<br />

adiciona el terminador nulo. Como resultado, los caracteres CRLF y el terminador nulo siempre<br />

ocuparán al menos 2 bytes de la cadena.<br />

Ejemplo 7.18<br />

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

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-23


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: GETSFGETS.CPP, ilustra el uso de gets() y fgets() */<br />

#include // Para cout<br />

#include // Para gets() y fgets()<br />

const int TAMANO = 31; // Define el tamaño del arreglo<br />

void main(void)<br />

{<br />

// Define arreglos de cadenas<br />

char nombre[TAMANO] = "\0";<br />

char direccion[TAMANO] = "\0";<br />

// Instrucción y lectura de datos del usuario<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

<strong>LA</strong>S FUNCIONES MIEMBRO peek(), putback() e ignore() <strong>DE</strong> istream<br />

La función miembro ignore() se salta un número designado de caracteres (el número<br />

predeterminado es un carácter) o termina al encontrar un delimitador designado (el delimitador<br />

predeterminado es EOF el cual causa que ignore() salte al final del archivo cuando está leyendo<br />

de un archivo)<br />

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

flujo de entrada de vuelta en dicho flujo. Esta función es útil para aplicaciones que revisan un<br />

flujo de entrada buscando un campo que comience con un carácter específico. Cuando se recibe<br />

ese carácter, la aplicación lo devuelve al flujo para que pueda incluirse en los datos que serán<br />

introducidos.<br />

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

elimina el carácter del flujo.<br />

E/S A PRUEBA <strong>DE</strong> TIPOS<br />

C++ proporciona E/S a prueba de tipos. Los operadores > están sobrecargados<br />

para aceptar elementos de datos de tipos específicos. Si se procesa un dato inesperado, se<br />

establecen diversos indicadores de error con los cuales el usuario puede probar para determinar si<br />

una operación de E/S terminó satisfactoriamente o si falló. De esta forma el programa conserva<br />

el control.<br />

PROCESAMIENTO <strong>DE</strong> ARCHIVOS<br />

<strong>LA</strong> JERARQUÍA <strong>DE</strong> DATOS<br />

Al final de cuentas, todos los elementos de datos procesados por computadoras digitales<br />

se reducen a combinaciones de ceros y unos. Esto ocurre debido a que resulta simple y<br />

económico construir dispositivos electrónicos que pueden asumir dos estados estables –uno<br />

representa a 0 y el otro representa a 1. Es de admirar que las funciones impresionantes que<br />

realizan las computadoras involucren solamente las manipulaciones fundamentales de ceros y<br />

unos.<br />

El elemento de datos más pequeño en una computadora puede asumir el valor 0 o el valor<br />

1. A tal elemento de datos se le llama bit (abreviatura de dígito binario, un dígito que sólo puede<br />

asumir uno de dos valores) El sistema de circuitos de la computadora realiza diversas<br />

manipulaciones de bits simples, tales como examinar el valor de un bit, establecer el valor de un<br />

bit e invertir un bit ( de 1 a 0 o de 0 a 1)<br />

Para los programadores es problemático trabajar con los datos en la forma de bajo nivel<br />

de bits. En vez de ello, los programadores prefieren trabajar con los datos en formas tales como<br />

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<br />

símbolos especiales (esto es $, @, %, &,, *, -, +, “, :, ?, / y muchos otros). A los dígitos, letras y<br />

símbolos especiales se les conoce como caracteres. Al conjunto de todos los caracteres que se<br />

utilizan para escribir programas y representar elementos de datos en una computadora particular<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-25


MIGUEL Á. TOLEDO MARTÍNEZ<br />

se le conoce como el conjunto de caracteres de la computadora. Debido a que las computadoras<br />

sólo pueden procesar unos y ceros, cada carácter del conjunto de caracteres de la computadora<br />

está representado como una secuencia de unos y ceros (llamada byte) Los bytes se componen<br />

comúnmente de 8 bits. Los programadores crean programas y elementos de datos con caracteres,<br />

y las computadoras manipulan y procesan estos caracteres como patrones de bits.<br />

Así como los caracteres están compuestos de bits, los campos están compuestos de<br />

caracteres (o bytes) Un campo es un grupo de caracteres que tienen significado. Por ejemplo, un<br />

campo que consista solamente de letras mayúsculas y minúsculas pueden utilizarse para<br />

representar el nombre de una persona.<br />

Los elementos de datos que son procesados por las computadoras forman una jerarquía<br />

de datos, en la cual los elementos de datos llegan a ser más grandes y más complejos en<br />

estructura conforme avanzamos de bits a caracteres (o bytes), a campos, y así sucesivamente.<br />

Un registro (es decir, una struct o una class en C++) está compuesto de varios campos<br />

(llamados miembros en C++) Por ejemplo, en un sistema de nómina un registro para un<br />

empleado particular puede consistir de los siguientes campos:<br />

1. Número de identificación del empleado.<br />

2. Nombre.<br />

3. Dirección.<br />

4. Salario por hora.<br />

5. Número de exenciones.<br />

6. Ingresos acumulados.<br />

7. Cantidad de impuestos federales retenidos, etcétera.<br />

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

uno de los campos pertenece al mismo empleado. Por supuesto, una compañía particular puede<br />

tener muchos empleados, y tendrá un registro de nómina por cada empleado. Un archivo es un<br />

grupo de registros relacionados. Un archivo de nómina de una compañía contiene normalmente<br />

un registro para cada empleado. Por lo tanto, un archivo de nómina para una compañía pequeña<br />

podría contener sólo 22 registros y, en cambio, un archivo de nómina para una compañía grande<br />

podría contener 100,000 registros. No es poco usual que una compañía tenga muchos archivos, y<br />

que cada uno contenga millones de caracteres de información. La figura 7.3 ilustra la jerarquía<br />

de datos.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-26


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Miguel Negro<br />

José Azul<br />

Judith Verde<br />

Iris Naranja<br />

Raúl Rojo<br />

Judith Verde<br />

Judith Campo<br />

01001010 Byte (carácter ASCII J)<br />

1 Bit<br />

Figura 7.3. La jerarquía de datos.<br />

Archivo<br />

Registro<br />

Archivo<br />

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

de cada registro se escoge como clave del registro. Una clave de registro especifica que un<br />

registro pertenece a una persona o entidad particular que es única con respecto a todos los demás<br />

registros del archivo. En el registro de nómina descrito anteriormente, normalmente el número de<br />

identificación del empleado es el que se elige como clave del registro.<br />

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

organización se llama archivo secuencial y en él los registros se almacenan típicamente en orden<br />

de acuerdo al campo de clave del registro. En un archivo de nómina los registros se colocan, por<br />

lo general, en orden por número de identificación de empleado. El primer registro de empleado<br />

del archivo contiene el número más pequeño de identificación del empleado, y los registros<br />

subsecuentes contienen números de identificación de empleado cada vez más grandes.<br />

La mayoría de los negocios utilizan muchos archivos diferentes para almacenar datos. Por<br />

ejemplo, las compañías pueden tener archivos de nómina, archivos de cuentas por cobrar (que<br />

listan dinero que deben los clientes), archivos de cuentas por pagar (que listan el dinero que se<br />

debe a los proveedores), archivos de inventario (que tienen hechos acerca de los artículos que<br />

maneja el negocio) y muchos otros tipos de archivos. A un grupo de archivos relacionados a<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-27


MIGUEL Á. TOLEDO MARTÍNEZ<br />

veces de le llama base de datos. A un conjunto de programas que está diseñado para crear y<br />

manejar bases de datos se le llama DBMS (Sistema de administración de bases de datos)<br />

ARCHIVOS Y FLUJOS<br />

C++ ve a cada archivo simplemente como una secuencia de bytes (figura 7.4) Todo<br />

archivo termina ya sea con un marcador de fin de archivos o con un número de byte específico<br />

que está registrado en una estructura de datos administrativa mantenida por el sistema. Cuando<br />

se abre el archivo, se crea un objeto y se asocia un flujo con ese objeto. En otra lección hemos<br />

visto que existen cuatro objetos que se crean automáticamente para nosotros: cin, cout, cerr y<br />

clog. Los flujos asociados con ellos proporcionan canales de comunicación entre un programa y<br />

un archivo o dispositivo particular. Por ejemplo, el objeto cin (el objeto de flujo de entrada<br />

estándar) permite que un programa reciba datos desde el teclado; el objeto cout (el objeto de<br />

flujo de salida estándar) permite que un programa envíe datos a la pantalla, y los objetos cerr y<br />

clog (objetos de flujo de error estándar) permiten que un programa envíe mensajes de error a la<br />

pantalla.<br />

0 1 2 3 4 5 6 7 8 9 ... n - 1<br />

Figura 7.4. La vista de C++ de un archivo de n bytes.<br />

Marcador<br />

de fin de<br />

archivo<br />

Para realizar el procesamiento de archivos en C++ se deben incluir los archivos de<br />

encabezado y . incluye las definiciones para las clases de<br />

flujo ifstream (para entrada desde un archivo), ofstream (para salida hacia un archivo) y fstream<br />

(para entrada y salida de un archivo) Los archivos se abren mediante la creación de objetos de<br />

estas clases de flujo, las cuales se derivan de (es decir, heredan la funcionalidad de) las clases<br />

istream, ostream e iostream, respectivamente. Por lo tanto, todas las funciones miembro,<br />

operadores y manipuladores que se han descrito en la lección 5 (COMO ENVIAR MENSAJES<br />

A <strong>LA</strong> PANTAL<strong>LA</strong>) y la primera parte de esta lección, también pueden aplicarse a los flujos de<br />

archivo. En la figura 7.5 se resumen las relaciones de herencia de las clases de E/S tratadas hasta<br />

este momento.<br />

ios<br />

istream ostream<br />

ifstream iostream ofstream<br />

fstream<br />

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

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-28


MIGUEL Á. TOLEDO MARTÍNEZ<br />

C<strong>LA</strong>SES: <strong>LA</strong> BASE <strong>DE</strong> LOS ARCHIVOS C++<br />

Los objetos cin y cout que ha usado en sus programas para entrada desde el teclado y<br />

salida a la pantalla son objetos de la clase iostream. Cómo estará de acuerdo, los objetos cin y<br />

cout invocan flujos de archivos predefinidos. De esta manera, decimos que la entrada (input)<br />

estándar se lee desde el flujo cin y la salida (output) estándar se escribe al flujo cout. Cuando se<br />

incluye el archivo de encabezado iostream.h en el programa, los flujos de archivo cin y cout se<br />

definen en forma automática. Por supuesto, los únicos archivos a los que puede tener acceso en<br />

forma conveniente con cin y cout son los archivos de teclado y pantalla que están unidos a estos<br />

flujos de archivos.<br />

Cuando elabore su propio flujo de archivos para leer/escribir archivos a disco, lo primero<br />

que debe hacer es definir un objeto para una de las clases de archivo. Los objetos del flujo de<br />

archivos que se usan exclusivamente para entrada se definen como objetos de la clase ifstream.<br />

De esta manera, el enunciado:<br />

ifstream entrada;<br />

define a entrada como un objeto del flujo de archivo de entrada. Use la clase ofstream para<br />

definir objetos del flujo de archivo que se usen exclusivamente para la salida. De esta manera, el<br />

enunciado:<br />

ofstream salida;<br />

define salida como un objeto del flujo de archivo de salida. Por último, deberá usar la clase<br />

fstream cuando defina objetos que se utilizarán para la entrada y salida de archivos.<br />

El enunciado:<br />

fstream entradaSalida;<br />

define entradaSalida como un objeto del flujo de archivos para entrada y salida.<br />

Después, deberá unir o conectar el objeto del flujo de archivos a un archivo físico del<br />

disco. Cuando un objeto de flujo de archivos está unido a un archivo físico del disco, el archivo<br />

en disco se abre para ser accesado. Para ello se necesita la función open(), que es heredada por<br />

todas las clases del flujo de archivo. Aquí esta el formato necesario para llamar esta función:<br />

.open (, );<br />

Lo primero que debe especificar es el objeto del flujo de archivos. El nombre del objeto<br />

es seguido por un punto y después por la función open() y sus argumentos necesarios. Este<br />

enunciado simplemente llama la función open() definida en la clase del flujo de archivos<br />

respectivo.<br />

La función open() tiene dos argumentos: un nombre del archivo en disco y un designador<br />

del modo de apertura. El nombre del archivo en disco se debe ajustar a los requerimientos del<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-29


MIGUEL Á. TOLEDO MARTÍNEZ<br />

sistema operativo. Para el sistema DOS, el nombre del archivo no puede exceder de ocho<br />

caracteres. Una extensión de tres caracteres, separados por un punto del nombre del archivo, es<br />

opcional. De esta manera, nombres de archivos de DOS como ejemplo, ejemplo.dat y<br />

ejemplo1.dat son todos nombres legales para archivos. El nombre del archivo en el disco físico<br />

se puede especificar directamente dentro de dobles comillas (por ejemplo “ejemplo.dat”) o en<br />

forma indirecta como una variable tipo cadena.<br />

El argumento indicador del modo de apertura define que tipo de acceso se realizará<br />

dentro del archivo. Aunque hay más, por ahora sólo nos interesan los indicadores ios::in e<br />

ios::out.<br />

Suponga que queremos abrir un flujo de archivos llamado entradaSalida. El flujo de<br />

archivos se define por la clase fstream y será asignado a un archivo en disco llamado prueba.dat.<br />

Además, el programa leerá y escribirá en el archivo. El enunciado de apertura adecuado será:<br />

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

El (los) modo(s) de lectura/escritura siempre debe(n) especificarse cuando un objeto de<br />

flujo de archivos se define con la clase fstream, pues por definición, esta clase se usa para ambos<br />

accesos a archivos de entrada y salida (lectura/escritura) Los modos lectura/escritura no<br />

necesitan especificarse cuando se abren archivos definidos por las clases ifstream u ofstream,<br />

porque los archivos son predeterminados como entrada y salida respectivamente. Por ejemplo, si<br />

se define salida como un objeto de flujo de archivo de la clase ofstream, el enunciado de<br />

apertura simplemente será:<br />

salida.open(“prueba.dat”);<br />

por otra parte, si entrada se define como un objeto de flujo de archivo de la clase ifstream, el<br />

enunciado de apertura será:<br />

Ejemplo 7.19<br />

Solución:<br />

entrada.open(“prueba.dat”);<br />

Escriba los enunciados para crear los siguientes archivos en disco:<br />

a. Un flujo de archivos llamada lectura que lea de un archivo de disco llamado ejemplo.doc.<br />

b. Un flujo de archivo llamado escritura que escriba en un archivo de disco llamado ejemplo.doc.<br />

c. Un flujo de archivo llamado lecturaEscritura que lea y escriba un archivo de disco llamado<br />

ejemplo.doc.<br />

a. ifstream lectura;<br />

lectura.open(“ejemplo.doc”);<br />

b. ofstream escritura;<br />

escritura.open(“ejemplo.doc”);<br />

c. fstream lecturaEscritura;<br />

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

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-30


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Una vez que se abre un archivo, estará listo para el procesamiento. Después de terminar<br />

el procesamiento del archivo, deberá cerrar el archivo. Esto se realiza con la función close() Para<br />

cerrar un archivo, lo que necesita hacer es llamar la función close() con su objeto de flujo de<br />

archivo usando el operador punto. Como resultado, los enunciados salidaEntrada.close(),<br />

salida.close() y entrada.close() deberán cerrar los archivos que se abrieron en la explicación<br />

anterior.<br />

LECTURA Y ESCRITURA <strong>DE</strong> UN ARCHIVO EN DISCO<br />

Se lee desde un archivo en disco con el objeto de flujo de archivo de entrada y el<br />

operador de extracción >>, justo como se usa el objeto de flujo de archivo estándar cin y el<br />

operador >>. Por ejemplo, suponga que hemos abierto un archivo de entrada, como éste:<br />

ifstream entrada;<br />

entrada.open(“prueba.dat”);<br />

El objeto de flujo de archivo que creamos se llama entrada. Para obtener una cadena de<br />

datos desde este archivo, todo lo que necesitamos hacer es aplicar el operador >> para nuestro<br />

objeto entrada, como éste:<br />

entrada >> cadena;<br />

Por supuesto, este enunciado supone que cadena se definió como un arreglo de<br />

caracteres.<br />

Escriba un archivo de disco usando su objeto de flujo de archivo de salida y el operador<br />

de inserción


MIGUEL Á. TOLEDO MARTÍNEZ<br />

usando el objeto de archivo salida. Por último, debemos cerrar ambos archivos. El código que se<br />

requiere es el siguiente:<br />

// El siguiente programa: DISCO1.CPP, copia tres cadenas de un archivo a otro<br />

#include // Para archivos de E/S<br />

const int TAMANO = 25; // Tamaño de la cadena de caracteres<br />

void main(void)<br />

{<br />

// Define la variable de cadena<br />

char cadena[TAMANO] = "\0";<br />

// Define los objetos del archivo y los archivos de entrada<br />

ifstream entrada; // Define el objeto de entrada<br />

ofstream salida; // Define el objeto de salida<br />

entrada.open("entrada.dat"); // Abre el archivo de entrada<br />

salida.open("salida.dat"); // Abre el archivo de salida<br />

// Copia los datos del archivo desde un archivo de entrada a un<br />

// archivo de salida<br />

entrada >> cadena; // Lee la primera cadena<br />

salida cadena; // Lee la segunda cadena<br />

salida cadena; // Lee la tercera cadena<br />

salida cadena lee una cadena de datos desde el archivo de disco entrada.dat y la<br />

coloca en el arreglo de caracteres llamado cadena. Recuerde que el operador >> termina cuando<br />

encuentra un espacio en blanco. Como resultado, no se leerá ningún espacio en blanco que forme<br />

parte de una cadena determinada. ¿Cómo se leen los espacios en blanco como parte de una<br />

cadena? Al utilizar la función getline() en lugar del operador >> con su objeto de archivo de<br />

entrada. El enunciado será entrada.getline(cadena,TAMANO). Lo mismo se aplica cuando se<br />

leen cadenas desde un archivo en disco usando su propio objeto como cuando se leen cadenas<br />

desde el teclado usando el objeto cin.<br />

Una vez que se lee una cadena, el enunciado salida


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo 7.21<br />

El siguiente programa: DISCO2.CPP, lee dos enteros desde un archivo denominado enteros y<br />

escriba la suma en un archivo de nombre suma.<br />

/* El siguiente programa: DISCO2.CPP, lee dos enteros de un archivo y escribe<br />

la suma en otro archivo.<br />

*/<br />

#include // Para archivos de E/S<br />

const int TAMANO = 25; // Tamaño de la cadena de caracteres<br />

void main(void)<br />

{<br />

// Define las variables<br />

int entero1 = 0; // Primera variable entera<br />

int entero2 = 0; // Segunda variable entera<br />

int suma = 0; // Variable suma<br />

// Define los objetos del archivo y los archivos de entrada<br />

ifstream entrada; // Define el objeto de entrada<br />

ofstream salida; // Define el objeto de salida<br />

entrada.open("enteros"); // Abre el archivo de entrada<br />

salida.open("suma "); // Abre el archivo de salida<br />

// Copia los datos del archivo desde un archivo de entrada a un<br />

// archivo de salida<br />

entrada >> entero1; // Lee el primer entero<br />

entrada >> entero2; // Lee la segundo entero<br />

suma = entero1 + entero2; // Calcula la suma<br />

salida . El objeto salida y el operador


MIGUEL Á. TOLEDO MARTÍNEZ<br />

El formato general para un ciclo manejando archivos es:<br />

<br />

while(!entrada.eof())<br />

{ // comienza ciclo<br />

< proceso de elementos de datos ><br />

<br />

}<br />

Un enunciado debe preceder al ciclo while para leer el primer elemento de datos en el<br />

archivo entrada. Entonces el ciclo se codifica para leer los datos restantes en el archivo. El ciclo<br />

dice mientras no (!) encuentre la marca de fin de archivo (eof()), procese el actual elemento de<br />

datos y lea otro elemento de datos. Así cuando el ciclo se ejecuta, procesará y leerá los elementos<br />

de datos del archivo en forma repetida, uno por uno, hasta encontrar el marcador de fin de<br />

archivo. Todos los archivos en disco contienen una marca de fin de archivo, llamado EOF.<br />

Cuando se lee un archivo en disco, instruimos al compilador para buscar una marca de EOF y<br />

terminar las operaciones de lectura cuando lo encuentre. Esto se logra utilizando el ciclo while y<br />

la prueba para el marcador EOF usando la función estándar eof()<br />

Observe que los enunciados de procesamiento y lectura de archivos se colocan dentro del<br />

cuerpo del ciclo, que es enmarcado con las llaves ({ })<br />

Ejemplo 7.22<br />

El siguiente programa DISCO3.CPP, es otra versión del programa DISCO1.CPP, en donde se<br />

emplea un ciclo para copiar el archivo entrada.dat al archivo salida.dat.<br />

// El siguiente programa: DISCO3.CPP, copia un archivo a otro.<br />

#include // Para archivos de E/S<br />

const int TAMANO = 25; // Tamaño de la cadena de caracteres<br />

void main(void)<br />

{<br />

// Define la variable de cadena<br />

char cadena[TAMANO] = "\0";<br />

// Define los objetos del archivo y los archivos de entrada<br />

ifstream entrada; // Define el objeto de entrada<br />

ofstream salida; // Define el objeto de salida<br />

entrada.open("entrada.dat"); // Abre el archivo de entrada<br />

salida.open("salida.dat"); // Abre el archivo de salida<br />

// Lee la primera cadena<br />

entrada >> cadena;<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-34


MIGUEL Á. TOLEDO MARTÍNEZ<br />

// Copia los datos del archivo desde un archivo de entrada a un<br />

// archivo de salida usando un ciclo<br />

while (!entrada.eof())<br />

{<br />

// Empieza el ciclo<br />

salida cadena; // Lee una cadena desde el archivo entrada<br />

}<br />

// Cierra los archivos<br />

entrada.close(); // Cierra el archivo de entrada<br />

salida.close(); // Cierra el archivo de salida<br />

} //Fin de main()<br />

Note que la operación de lectura y escritura del archivo están colocados dentro del ciclo. Esto es<br />

porque simplemente leemos un elemento de datos desde el archivo de entrada y lo repetimos al<br />

archivo de salida como nuestra operación de procesamiento. El ciclo leerá una cadena desde el<br />

archivo de entrada repetidamente, después escribirá la cadena al archivo de salida hasta que<br />

encuentre el marcador de fin de archivo dentro del archivo de entrada.<br />

Otra ventaja de usar un ciclo para leer un archivo es que a menudo no se sabe cuántos elementos<br />

de datos contiene un archivo. Al utilizar un ciclo y verificar el marcador de EOF, no necesitará<br />

saber cuantos elementos de datos están en el archivo, porque el ciclo continuará leyendo el<br />

archivo hasta encontrar el final de éste.<br />

CREACIÓN <strong>DE</strong> UN ARCHIVO <strong>DE</strong> ACCESO SECUENCIAL<br />

C++ no impone estructura sobre un archivo. Por lo tanto, en los archivos C++ no existen<br />

los conceptos como registro. Debido a esto, el programador debe estructurar los archivos para<br />

satisfacer los requerimientos de las aplicaciones. En el siguiente ejemplo vemos cómo el<br />

programador puede imponer una estructura de registro simple en un archivo. Primero<br />

presentamos el programa y luego lo analizamos a detalle.<br />

Ejemplo 7.23<br />

EXAMEN BREVE 18<br />

El siguiente programa: SECUENCIAL.CPP, crea un archivo de acceso secuencial simple que<br />

puede utilizarse en un sistema de cuentas por cobrar para ayudar en la administración del dinero<br />

que deben los clientes a una compañía. Para cada cliente, el programa obtiene el número de<br />

cuenta, el nombre y el saldo del cliente (es decir, la cantidad que el cliente todavía debe a la<br />

compañía por bienes y servicios recibidos en el pasado) Los datos obtenidos para cada cliente<br />

constituyen un registro para ese cliente. En esta aplicación el número de cuenta se utiliza como<br />

clave del registro. Esto es, el archivo será creado y mantenido con relación al número de cuenta.<br />

Este programa asume que el usuario introduce registros en orden de número de cuenta. En un<br />

sistema de cuentas por cobrar completo se proporcionaría una capacidad de ordenamiento para<br />

que el usuario pueda dar los registros en cualquier orden, y éstos se ordenarían y escribirían en<br />

el archivo.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-35


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: SECUENCIAL.CPP, crea un archivo secuencial .*/<br />

#include //Para cout y cin<br />

#include //Para crear un objeto de E/S<br />

#include //Para exit()<br />

int main(void)<br />

{<br />

//El constructor ofstream abre un archivo<br />

ofstream archivoSalida("clientes.dat", ios::out);<br />

if(!archivoSalida) //Operador ! sobrecargado<br />

{<br />

cerr nombre >> balance)<br />

{<br />

archivoSalida


MIGUEL Á. TOLEDO MARTÍNEZ<br />

ofstream archivoSalida(“cliente.dat”);<br />

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

Modo Descripción<br />

ios::app Escribe toda la salida al final del archivo.<br />

ios::ate Abre un archivo para salida y se mueve al final del archivo (normalmente se utiliza<br />

para agregarle datos a un archivo) Los datos pueden escribirse en cualquier lugar<br />

del archivo.<br />

ios::in Abre un archivo para entrada.<br />

ios::out Abre un archivo para salida.<br />

ios::trunc Descarta el contenido del archivo en caso de que exista (esto también es una acción<br />

predeterminada para ios::out).<br />

ios::nocreate Si el archivo no existe, la operación de apertura falla.<br />

ios::noreplace Si el archivo existe, la operación de apertura falla.<br />

Figura 7.6. Modos de apertura de archivos.<br />

Es posible crear un objeto ofstream sin abrir un archivo específico, ya que posteriormente se<br />

puede asociar un archivo hacia el objeto. Por ejemplo, la declaración:<br />

Ofstream.archivoSalida;<br />

crea un objeto ofstream llamado archivoSalida. La función miembro open de ofstream abre un<br />

archivo y lo asocia con un objeto ofstream existente de la siguiente manera:<br />

archivoSalida.open(“clientes.dat”, ios::out);<br />

Después de crear un objeto ofstream y de intentar abrirlo, el programa prueba para ver si la<br />

operación de apertura fue satisfactoria. La condición de la estructura if:<br />

if( !archivoSalida)<br />

{<br />

cerr


MIGUEL Á. TOLEDO MARTÍNEZ<br />

valor del apuntador) Si failbit o badbit están establecidos para el flujo, se devuelve 0 (false) La<br />

condición en el siguiente encabezado de while llama automáticamente a la función miembro<br />

operator void *.<br />

while( cin >> cuenta >> nombre >> balance)<br />

La condición seguirá siendo true mientras failbit o badbit no se hayan establecido para cin. Al<br />

dar el marcador de fin de archivo se establece failbit para cin. La función operator void * puede<br />

utilizarse para probar si hay fin de archivo en un objeto de entrada, en vez de llamar<br />

explícitamente a la función miembro eof() del objeto de entrada.<br />

Si el archivo se abre satisfactoriamente, el programa comienza a procesar datos. La siguiente<br />

instrucción le pide al usuario que dé los diversos campos para cada registro o que introduzca el<br />

fin de archivo cuando haya terminado la entrada de datos:<br />

cout cuenta >> nombre >> balance ) introduce cada conjunto de datos y<br />

determina si se ha dado el fin de archivo. Cuando éste se introduce, o si se introducen datos<br />

erróneos, la operación >> de extracción de flujo de cin devuelve 0 (normalmente esta extracción<br />

de flujo devuelve cin) y la estructura while termina. El usuario introduce el fin de archivo para<br />

informarle al programa que ya no hay más datos a procesar. El marcador de fin de archivo se<br />

establece cuando el usuario da la combinación de teclas de fin de archivo. La estructura while<br />

continúa el ciclo mientras no se haya dado dicho marcador.<br />

La línea archivoSalida


MIGUEL Á. TOLEDO MARTÍNEZ<br />

LECTURA <strong>DE</strong> DATOS <strong>DE</strong> UN ARCHIVO <strong>DE</strong> ACCESO SECUENCIAL<br />

Los datos se almacenan en archivos para que puedan ser recuperados y procesarlos<br />

cuando se necesite. La sección anterior mostró la manera de crear un archivo para acceso<br />

secuencial. En esta sección trataremos la manera de leer datos secuencialmente desde un archivo.<br />

Ejemplo 7.24<br />

El siguiente programa LECTURA.CPP, lee registros del archivo clientes.dat creado por el<br />

programa SECUENCIAL.CPP, e imprime el contenido de los registros.<br />

/* El siguiente programa: LECTURA.CPP, realiza lectura e impresión de un archivo secuencial. */<br />

#include //Para cout y cin<br />

#include //Para crear el objeto archivoEntrada.<br />

#include //Para setw()<br />

#include //Para exit()<br />

void lineaSalida(int, const char *, double);<br />

int main(void)<br />

{<br />

//El constructor de ifstream abre el archivo<br />

ifstream archivoEntrada("clientes.dat", ios::in);<br />

if(!archivoEntrada)<br />

{<br />

cerr


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Los archivos se abren para entrada mediante la creación de un objeto de la clase ifstream. Se<br />

pasan dos argumentos al objeto –el nombre del archivo y el modo de apertura del archivo. La<br />

declaración:<br />

ifstream archivoEntrada(“clientes.dat”, ios::in);<br />

crea un objeto ifstream llamado archivoEntrada y lo asocia con el archivo clientes.dat que se<br />

abrirá para entrada. Los argumentos que están entre paréntesis se pasan a la función<br />

constructora de ifstream, la cual abre el archivo y establece una línea de comunicación con el<br />

archivo.<br />

De manera predeterminada los objetos de la clase ifstream se abren para entrada, por lo que se<br />

podría haber utilizado la instrucción:<br />

ifstream archivoEntrada(“clientes.dat”);<br />

para abrir clientes.dat para entrada. Al igual que como sucede con un objeto ofstream, se puede<br />

crear un objeto ifstream sin abrir un archivo específico y posteriormente se le puede asociar un<br />

archivo.<br />

El programa utiliza la condición !archivoEntrada para determinar si el archivo se abrió<br />

satisfactoriamente antes de intentar la recuperación de datos del archivo.<br />

La declaración while( archivoEntrada >> cuenta >> nombre >> balance ) lee un conjunto de<br />

datos (es decir, un registro) desde el archivo. Cada vez que se ejecuta la línea, se lee otro<br />

registro desde el archivo hacia las variables contador, nombre y balance. Los registros se<br />

despliegan utilizando la función lineaSalida(), la cual usa manipuladores de flujo con<br />

parámetros para formatear los datos para su despliegue. Cuando se llega al fin de archivo, la<br />

secuencia de entrada de la estructura while devuelve 0 (normalmente se devuelve el flujo<br />

archivoEntrada), el archivo se cierra por medio de la función destructor ifstream y el programa<br />

termina.<br />

Para recuperar datos secuencialmente desde un archivo, los programas comienzan, por lo<br />

general, leyendo desde el inicio del archivo y leen todos los datos en forma consecutiva hasta que<br />

se encuentra el dato deseado. Tal vez sea necesario procesar el archivo secuencialmente varias<br />

veces (desde su inicio) durante la ejecución del programa. Las clases istream y ostream<br />

proporcionan funciones miembro para reubicar el apuntador de posición de archivo (el número<br />

de byte del siguiente byte del archivo a leer o escribir). Estas funciones miembro son seekg<br />

(buscar y obtener), para la clase istream y seekp (buscar y colocar), para la clase ostream. Cada<br />

objeto istream tiene un apuntador obtener que indica el número de bytes del archivo en donde<br />

sucederá la siguiente entrada, y cada objeto ostream tiene un apuntador colocar que indica el<br />

número de byte del archivo en el cual se colocará la siguiente salida. La instrucción:<br />

archivoEntrada.seekg(0);<br />

ubica el apuntador de posición de archivo en el inicio del archivo (localidad 0) que está asociado<br />

a archivoEntrada. El argumento seekg es normalmente un entero long. Es posible especificar un<br />

segundo argumento para indicar la dirección de búsqueda, la cual puede ser ios::beg (lo<br />

predeterminado) para ubicación con relación al inicio de un flujo, ios::cur para ubicación<br />

relación a la posición actual en un flujo, e ios::end para ubicación con relación al final del flujo.<br />

El apuntador de posición de archivo es un valor entero que especifica la localidad en el archivo<br />

como un número de bytes a partir de la localidad inicial del archivo (a esto también se le conoce<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-40


MIGUEL Á. TOLEDO MARTÍNEZ<br />

como desplazamiento a partir del inicio del archivo). Algunos ejemplos de la ubicación del<br />

apuntador de posición de archivo obtener son:<br />

//Ubica en el enésimo byte de objetoArchivo<br />

//asume ios::beg<br />

objetoArchivo.seekg(n);<br />

//Ubica n bytes hacia delante en objetoArchivo<br />

objetoArchivo.seekg(n, ios::cur);<br />

//Ubica y bytes hacia atrás con respecto al final de objetoArchivo<br />

objetoArchivo.seekg(y, ios::end);<br />

//Ubica al final de objetoArchivo<br />

objetoArchivo.seekg(0, ios::end);<br />

Es posible realizar las mismas operaciones con la función miembro seekp de ostream. Se<br />

proporcionan las funciones miembro tellg y tellp para devolver las localidades actuales de los<br />

apuntadores obtener y colocar, respectivamente. La siguiente instrucción asigna el valor del<br />

apuntador de posición de archivo obtener a la variable localidad de tipo long.<br />

Ejemplo 7.25<br />

localidad = objetoArchivo.tellg();<br />

El siguiente programa: CONSULTA.CPP, permite que un gerente de crédito despliegue la información de<br />

las cuentas para los clientes que tienen su saldo en cero (es decir, los clientes que no le deben dinero a la<br />

compañía), los saldos a favor (es decir, los clientes a los cuales la compañía les debe dinero) y los saldos<br />

en contra (es decir, los clientes que deben dinero a la compañía por bienes y servicios recibidos en el<br />

pasado) El programa despliega un menú y permite que el gerente de crédito dé alguna de tres opciones<br />

para obtener información de crédito. La opción 1 produce un listado de cuentas con saldo en cero. La<br />

opción 2 produce una lista de cuentas con saldos a favor. La opción 3 produce una lista de cuentas con<br />

saldos en contra. La opción 4 termina la ejecución del programa. Al dar una opción inválida simplemente<br />

se despliega la petición para que se dé otra selección.<br />

/* El siguiente programa: CONSULTA.CPP, ilustra el uso de consultas hechas a un archivo<br />

de crédito.<br />

*/<br />

#include //Para cout y ci<br />

#include //Para crear el objeto archivoCliente<br />

#include //Para setw()<br />

#include //Para exit()<br />

enum tipoRespuesta{balanceCero = 1, balanceCredito, balanceDebito, fin};<br />

int obtenerRespuesta();<br />

bool debeDesplegar(int, double);<br />

void lineaSalida(int, const char *, double);<br />

int main(void)<br />

{<br />

//El constructor de ifstream abre el archivo<br />

ifstream archivoCliente("clientes.dat", ios::in);<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-41


MIGUEL Á. TOLEDO MARTÍNEZ<br />

if(!archivoCliente)<br />

{<br />

cerr nombre >> balance;<br />

}//Fin de while interno<br />

archivoCliente.clear(); //Restablece el eof() para la siguiente entrada.<br />

archivoCliente.seekg(0); //Se mueve al inicio del archivo<br />

respuesta = obtenerRespuesta();<br />

}//Fin de while externo<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

{<br />

int respuesta;<br />

do<br />

{<br />

cout > respuesta;<br />

}<br />

while(respuesta < balanceCero && respuesta > fin);<br />

return respuesta;<br />

}//Fin de obtenerRespuesta()<br />

bool debeDesplegar(int tipo, double balance)<br />

{<br />

if(tipo == balanceCredito && balance < 0)<br />

return true;<br />

if(tipo == balanceDebito && balance > 0)<br />

return true;<br />

if(tipo == balanceCero && balance == 0)<br />

return true;<br />

return false;<br />

}//Fin de deboDesplegar()<br />

void lineaSalida(int cuenta, const char *nombre, double balance)<br />

{<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

siguiente registro secuencial en el archivo. El problema que hay aquí es que en el modelo de<br />

entrada/salida formateada en que se utiliza el operador de inserción >, los campos –y por lo tanto los registros- pueden variar en tamaño. Por ejemplo: 7,<br />

14, -117, 2074 y 27383 son int y cada uno se almacena de manera interna en el mismo número<br />

de bytes de datos sin formato, pero cuando estos enteros se envían como texto formateado a la<br />

pantalla o a un archivo en disco, se convierten en campos de diferentes tamaño. Por lo tanto, el<br />

modelo de entrada/salida formateada no se utiliza generalmente para actualizar registros en su<br />

lugar.<br />

Tal actualización se puede hacer, pero es problemática. Por ejemplo, para hacer el cambio<br />

de nombre anterior se podrían copiar los registros que están antes de 300 Juan 0.00 en un archivo<br />

de acceso secuencial hacia un nuevo archivo, luego se escribiría el registro actualizado en el<br />

nuevo archivo y se copiarían los registros que están después de 300 Juan 0.00 hacia el nuevo<br />

archivo. Esto requiere el procesamiento de todos los registros del archivo para actualizar un<br />

registro. Esta técnica puede ser aceptable si se están actualizando muchos registros en un solo<br />

paso por el archivo.<br />

ARCHIVOS <strong>DE</strong> ACCESO ALEATORIO<br />

Hasta ahora hemos visto la manera de crear archivos de acceso secuencial y buscar en<br />

ellos para localizar información particular. Los archivos de acceso secuencial son inadecuados<br />

para las llamadas aplicaciones de acceso instantáneo en donde se debe localizar inmediatamente<br />

un registro de información particular. Algunas aplicaciones populares de acceso instantáneo son<br />

los sistemas de reservación de línea aéreas, los sistemas de bancos, los sistemas de punto de<br />

venta, las máquinas de cajero automático y otros tipos de sistemas de procesamiento de<br />

transacciones que requieren un acceso rápido de datos específicos. El banco en donde uno tiene<br />

su cuenta puede tener cientos, miles o hasta millones de otros clientes, pero cuando se utiliza una<br />

máquina de cajero automático la cuenta se revisa en segundos para ver si hay fondos suficientes.<br />

Este tipo de acceso instantáneo es posible con los archivos de acceso aleatorio. Los registros<br />

individuales de un archivo de acceso aleatorio se pueden acceder directamente (y en forma<br />

rápida) sin buscar por todos los demás registros.<br />

Como hemos dicho, C++ no impone ninguna estructura a un archivo. Por lo tanto, la<br />

aplicación que necesite utilizar archivos de acceso aleatorio literalmente debe crearlos. Se<br />

pueden utilizar una diversidad de técnicas para crear archivos de acceso aleatorio. Tal vez la más<br />

simple es la que requiere que todos los registros de un archivo sean de la misma longitud. El uso<br />

de registros de longitud fija hace que para un programa sea fácil calcular (en función del tamaño<br />

y la clave del registro) la localidad exacta de cualquier registro con relación al inicio del archivo.<br />

Pronto veremos la manera en que esto facilita el acceso inmediato a registros específicos, incluso<br />

en archivos grandes.<br />

La figura 7.8 ilustra la visión de C++ de un archivo de acceso aleatorio que está<br />

compuesto de registros de longitud fija (cada registro es de 100 bytes de largo) Un archivo de<br />

acceso aleatorio es como un tren con muchos carros, algunos vacíos y otros llenos.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-44


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Desplazamiento de bytes<br />

0 100 200 300 400 500 600<br />

100<br />

bytes<br />

100<br />

bytes<br />

100<br />

bytes<br />

100<br />

bytes<br />

100<br />

bytes<br />

100<br />

bytes<br />

Figura 7.8. La visión de C++ de un archivo de acceso aleatorio<br />

100<br />

bytes<br />

Es posible insertar los datos en un archivo de acceso aleatorio sin destruir otros datos que<br />

estén en el archivo. Los datos que han sido almacenados previamente también pueden<br />

actualizarse o borrarse sin tener que volver a escribir todo el archivo. En las siguientes secciones<br />

explicaremos la manera de crear un archivo de acceso aleatorio, introducir datos, leer los datos<br />

en forma secuencial y aleatoria, actualizar los datos y borrar datos que ya no se necesiten.<br />

CREACIÓN <strong>DE</strong> UN ARCHIVO <strong>DE</strong> ACCESO ALEATORIO<br />

La función miembro write de ostream da salida a un número fijo de bytes, que se inicia<br />

en una localidad de memoria específica, hacia un flujo especificado. Cuando el flujo está<br />

asociado con un archivo, los datos se escriben comenzando en la localidad del archivo que está<br />

especificada por el apuntador de posición de archivo colocar. La función miembro read de<br />

istream introduce un número fijo de bytes desde un flujo especificado hacia un área en memoria<br />

que comienza en una dirección especificada. Si el flujo está asociado con un archivo, los bytes se<br />

introducen comenzando en la localidad de archivo que está especificada por el apuntador de<br />

posición de archivo obtener.<br />

Ahora, para escribir una variable entera numero a un archivo, en vez de utilizar:<br />

archivoSalida


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Ejemplo 7.26<br />

Considere el siguiente planteamiento de un problema:<br />

Cree un programa de procesamiento de crédito que sea capaz de almacenar hasta 100 registros<br />

de longitud fija para una compañía que puede tener hasta 100 clientes. Cada registro deberá<br />

consistir de un número de cuenta que se utilizará como clave del registro, un apellido, un nombre<br />

y un saldo. El programa deberá ser capaz de actualizar una cuenta, agregar una nueva cuenta,<br />

borrar una cuenta y listar todos los registros de cuenta en un archivo de texto formateado para<br />

impresión.<br />

Las siguientes secciones presentan las técnicas necesarias para crear este programa de<br />

procesamiento de crédito. Llamemos al programa ALEATORIO.CPP.<br />

/* Este es un archivo de cabecera llamado clntdata.h utilizados por los programas.<br />

Se define la estructura datoCliente que se utiliza en los programas ALEATORIO.CPP,<br />

ESCRIALEA.CPP, LECSEC.CPP.<br />

*/<br />

#ifndef CLNDATA_H<br />

#define CLNDATA_H<br />

struct datoCliente<br />

{<br />

int numeroCuenta;<br />

char apellido[15];<br />

char nombre[10];<br />

float balance;<br />

};<br />

#endif<br />

/* El siguiente programa: ALEATORIO.CPP, crea secuencialmente un archivo accesado<br />

aleatoriamente.<br />

El archivo de cabecer clntdata.h se encuentra definido en otro archivo.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para crear el objeto creditoSalida<br />

#include //Para exit()<br />

#include "c:\apuntes1 c++\programas fuentes\leccion 07\clntdata.h"<br />

int main(void)<br />

{<br />

ofstream creditoSalida("credito.dat", ios::out);<br />

if(!creditoSalida)<br />

{<br />

cerr


MIGUEL Á. TOLEDO MARTÍNEZ<br />

El programa ALEATORIO.CPP, ilustra la apertura de un archivo de acceso aleatorio, la<br />

definición del formato del registro utilizando una struct (definida en el archivo de encabezado<br />

clntdata.h) y la escritura de datos en el disco. Este programa inicializa los 100 registros del<br />

archivo credito.dat con struct vacías mediante la función write. Cada struct vacía contiene 0 en<br />

el número de cuenta, la cadena nula (representada por comillas vacías) para el apellido, la<br />

cadena nula para el nombre y 0.0 para el saldo. El archivo se inicializa con la cantidad<br />

adecuada de espacio vacío en donde se almacenarán los datos de las cuentas y para determinar<br />

en programas subsecuentes si cada registro está vacío o contiene datos.<br />

En dicho programa, las instrucciones:<br />

creditoSalida.write(reinterpret_cast(&clienteBlanco),<br />

sizeof(datoCliente));<br />

causan que la estructura clienteBlanco de tamaño sizeof(datoCliente) se escriba en el archivo<br />

credito.dat que está asociado con el objeto creditoSalida de ofstream. Recuerde que el operador<br />

sizeof devuelve el tamaño en byte del objeto que está contenido entre paréntesis. Observe que el<br />

primer argumento de la función write debe ser de tipo const char *. Sin embargo, el tipo de dato<br />

de &clienteBlanco es datoCliente *. Para convertir &clienteBlanco al tipo de apuntador<br />

adecuado, la expresión:<br />

reinterpret_cast(&clienteBlanco)<br />

utiliza el operador de conversión mediante cast reinterpret_cast para convertir la dirección de<br />

clienteBlanco a const char *, de modo que la llamada write compile sin emitir un error de<br />

sintaxis.<br />

ESCRITURA ALEATORIA <strong>DE</strong> DATOS A UN ARCHIVO <strong>DE</strong> ACCESO ALEATORIO<br />

Ejemplo 7.27<br />

for(int i = 0; i < 100; i++)<br />

creditoSalida.write(reinterpret_cast(&clienteBlanco),<br />

sizeof(datoCliente));<br />

return 0;<br />

}//Fin de main()<br />

El siguiente programa: ESCRIALEA.CPP, escribe datos hacia el archivo credito.dat. Utiliza la<br />

combinación de las funciones seekp y write de ostream para almacenar datos en localidades<br />

exactas del archivo. La función seekp establece el apuntador de posición de archivo colocar a<br />

una localidad específica del archivo y luego write envía los datos a la salida. Observe que el<br />

programa incluye el archivo de encabezado clntdata.h.<br />

/* El siguiente programa: ESCRIALEA.CPP, ilustra la escritura a un archivo de acceso aleatorio.*/<br />

#include //Para cout y cin<br />

#include //Para crear el objeto creditoSalida<br />

#include //Para exit()<br />

#include "c:\apuntes1 c++\programas fuentes\leccion 07\clntdata.h"<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-47


MIGUEL Á. TOLEDO MARTÍNEZ<br />

int main(void)<br />

{<br />

La línea creditoSalida.seekp( (cliente.numeroCuenta - 1 ) * sizeof(datoCliente) ); ubica el<br />

apuntador de posición colocar para el objeto creditoSalida en la localidad de bytes calculada<br />

por (cliente.numeroCuenta - 1 ) * sizeof(datoCliente). Debido a que el número de cuenta está<br />

entre 1 y 100, se resta 1 al número de cuenta cuando se calcula la localidad, en bytes, del<br />

registro. Por lo tanto, para el registro 1 el apuntador de posición de archivo se establece al byte<br />

0 del archivo. Observe que el objeto creditoSalida de ofstream se abre mediante el modo de<br />

apertura de archivo ios::ate. El apuntador de posición de archivo colocar se establece<br />

inicialmente al final del archivo, pero los datos se pueden escribir en cualquier lugar de él.<br />

LECTURA SECUENCIAL <strong>DE</strong> DATOS <strong>DE</strong>S<strong>DE</strong> UN ARCHIVO <strong>DE</strong> ACCESO ALEATORIO<br />

Ejemplo 7.28<br />

ofstream creditoSalida("credito.dat", ios::ate);<br />

if(!creditoSalida)<br />

{<br />

cerr > cliente.nombre >> cliente.balance;<br />

creditoSalida.seekp( (cliente.numeroCuenta - 1 ) * sizeof(datoCliente) );<br />

creditoSalida.write(reinterpret_cast(&cliente),<br />

sizeof(datoCliente));<br />

cout > cliente.numeroCuenta;<br />

}//Fin de while<br />

En las secciones anteriores creamos un archivo de acceso aleatorio y escribimos datos en ese<br />

archivo. En esta sección desarrollaremos un programa al que llamaremos: LECSEC.CPP, que<br />

lee el archivo de manera secuencial e imprime solamente los registros que contienen datos. Estos<br />

programas producen un beneficio adicional: los registros se encuentran ordenados.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-48


MIGUEL Á. TOLEDO MARTÍNEZ<br />

/* El siguiente programa: LECSEC.CPP, ilustra la lectura secuencial de un archivo<br />

de acceso aleatorio.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para setw()<br />

#include //Para crear el objeto creditoEntrada<br />

#include //Para exit()<br />

#include "c:\apuntes1 c++\programas fuentes\leccion 07\clntdata.h"<br />

void lineaSalida(ostream&, const datoCliente &);<br />

int main(void)<br />

{<br />

ifstream creditoEntrada("credito.dat", ios::in);<br />

if(!creditoEntrada)<br />

{<br />

cerr


MIGUEL Á. TOLEDO MARTÍNEZ<br />

La función read de istream introduce un número específico de bytes desde la posición actual en<br />

el flujo especificado hacia un objeto. Así:<br />

creditoEntrada.read(reinterpret_cast(&cliente),<br />

sizeof(datoCliente));<br />

lee el número de bytes especificados por sizeof(datoCliente) desde el archivo asociado con el<br />

objeto creditoEntrada de ifstream y almacena los datos en la estructura cliente. Observe que la<br />

función read requiere un primer argumento de tipo char *. Debido a que &cliente es de tipo<br />

datoCliente *, debe convertirse a char * por medio del operador de conversión mediante cast<br />

reinterpre_cast. Observe que el programa incluye el archivo de encabezado clntdata.h.<br />

El programa lee secuencialmente cada uno de los registros del archivo credito.dat, revisa cada<br />

registro para ver si contiene datos y despliega salidas formateadas para los registros que<br />

contienen datos. La condición:<br />

while(creditoEntrada && !creditoEntrada.eof())<br />

utiliza la función miembro eof() de ios para determinar si se ha llegado al final del archivo y<br />

causa que termine la ejecución de la estructura while. Asimismo, si hay algún error en la lectura<br />

del archivo, el ciclo terminará debido a que creditoEntrada se evaluará como false. lineaSalida<br />

(que toma dos argumentos, un objeto ostream y una estructura datoCliente que se enviará a la<br />

salida) envía a la salida la entrada de datos del archivo. El tipo del parámetro de ostream es<br />

interesante, debido a que cualquier objeto ostream (tal como cout) o cualquier objeto de una<br />

clase derivada de ostream (tal como un objeto de tipo ofstream) se puede proporcionar como<br />

argumento. Esto significa que la misma función puede utilizarse, por ejemplo, para realizar la<br />

salida hacia el flujo de salida estándar y hacia un flujo de archivo sin escribir funciones<br />

separadas.<br />

¿Qué hay acerca del beneficio adicional? Si examina la ventana de salida, ¡observará que los<br />

registros están listados en orden (por número de cuenta)! Esta es una consecuencia simple de la<br />

forma en que almacenamos estos registros en el archivo mediante técnicas de acceso directo. En<br />

comparación al ordenamiento de burbuja que hemos visto, el ordenamiento con técnicas de<br />

acceso directo es extremadamente rápido. La velocidad se logra haciendo que el archivo sea lo<br />

suficientemente grande como para almacenar todo registro posible que pueda crearse. Esto<br />

significa, por supuesto, que el archivo podría estar ocupado en forma dispersa la mayor parte del<br />

tiempo, lo que es un desperdicio de almacenamiento. Por lo tanto , éste es otro ejemplo del<br />

compromiso del espacio contra el tiempo: mediante el uso de grandes cantidades de espacio<br />

somos capaces de desarrollar un algoritmo de ordenamiento mucho más rápido.<br />

EJEMPLO: UN PROGRAMA <strong>DE</strong> PROCESAMIENTO <strong>DE</strong> TRANSACCIONES<br />

Ejemplo 7.29<br />

Ahora presentaremos un programa sustancial: TRANSAC.CPP, de procesamiento de<br />

transacciones, que utiliza un archivo de acceso aleatorio para lograr un procesamiento de<br />

acceso instantáneo. El programa mantiene información de cuentas bancarias, actualiza cuentas<br />

existentes, agrega nuevas cuentas, borra cuentas y almacena un listado formateado de todas las<br />

cuentas actuales en un archivo de texto para impresión. Asumimos que ya se ejecutaron los<br />

programa ALEATORIO.CPP para crear el archivo credito.dat y el programa ESCRIALEA.CPP<br />

para insertar los datos iniciales.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-50


MIGUEL Á. TOLEDO MARTÍNEZ<br />

El programa tiene cinco opciones (la opción 5 es para terminar el programa). La opción 1 llama<br />

a la función archivoTexto() para almacenar una lista formateada de toda la información de<br />

cuentas en un archivo de texto, llamado imprimir.txt, que puede imprimirse posteriormente. La<br />

función archivoTexto() toma un objeto fstream como argumento para utilizarlo a fin de<br />

introducir datos desde el archivo credito.dat. La función archivoTexto() utiliza la función<br />

miembro read de istream y las técnicas de acceso de archivo secuencial para introducir los datos<br />

de credito.dat. La función lineaSalida(), ya en programas anteriores estudiada se utiliza para<br />

enviar los datos al archivo imprimir.txt. Observe que archivoTexto() utiliza la función miembro<br />

seekg de istream para asegurarse de que el apuntador de posición de archivo esté al inicio del<br />

archivo.<br />

La opción 2 llama a la función actualizarRegistro() para actualizar una cuenta. La función sólo<br />

actualizará un registro existente, por lo que la función primero determina si el registro<br />

especificado está vacío. El registro se lee en la estructura cliente mediante la función miembro<br />

read de istream, después cliente.numeroCuenta se compara contra cero para determinar si el<br />

registro contiene información. Si cliente.numeroCuenta es cero, se imprime un mensaje<br />

indicando que el registro está vacío y se despliegan las selecciones del menú. Si el registro<br />

contiene información, la función actualizarRegistro() despliega el registro en pantalla mediante<br />

la función lineaSalida(), introduce la cantidad de transacciones, calcula el nuevo saldo y vuelve<br />

a escribir el registro en el archivo.<br />

La opción 3 llama a la función nuevoRegistro() para agregarle una nueva cuenta al archivo. Si<br />

el usuario introduce el número de una cuenta existente, nuevoRegistro() despliega un mensaje<br />

indicando que la cuenta existe y despliega las alternativas del menú.<br />

La opción 4 llama a la función borrarRegistro() para borrar un registro del archivo. Al usuario<br />

se le pide que introduzca un número de cuenta. Sólo puede borrarse un registro existente, por lo<br />

que si la cuenta especificada está vacía, se emite un mensaje de error. Si la cuenta existe, se<br />

reinicializa copiando un registro vacío (clienteBlanco) al archivo. Se despliega un mensaje para<br />

informar al usuario que se ha borrado el registro.<br />

El archivo credito.dat se abre mediante la creación de un objeto fstream para lectura y escritura<br />

usando los modos ios::in e ios::out a los que se les aplicó un OR.<br />

/* El siguiente programa: TRANSAC.CPP, lee de manera secuencial un archivo de acceso<br />

aleatorio,<br />

actualiza los datos que ya están escritos en el archivo, crea nuevos datos a colocarse en el<br />

archivo y borra datos que ya están en el archivo.<br />

*/<br />

#include //Para cout y cin<br />

#include //Para los objetos entraSaleCredito, archivoImprimir<br />

#include //Para setw()<br />

#include //Para exit()<br />

#include "c:\apuntes1 c++\programas fuentes\leccion 07\clntdata.h"<br />

int pedirSeleccion();<br />

void archivoTexto(fstream &);<br />

void actualizarRegistro(fstream &);<br />

void nuevoRegistro(fstream &);<br />

void borrarRegistro(fstream &);<br />

void lineaSalida(ostream &, const datoCliente &);<br />

int obtenerCuenta(const char *);<br />

enum seleccion{ARCHIVOTEXTO = 1, ACTUALIZAR, NUEVO, BORRAR, FIN};<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-51


MIGUEL Á. TOLEDO MARTÍNEZ<br />

int main(void)<br />

{<br />

fstream entraSaleCredito("credito.dat", ios::in | ios::out);<br />

if(!entraSaleCredito)<br />

{<br />

cerr


MIGUEL Á. TOLEDO MARTÍNEZ<br />

int seleccionMenu;<br />

cin >> seleccionMenu;<br />

return seleccionMenu;<br />

}//Fin de pedirSeleccion()<br />


MIGUEL Á. TOLEDO MARTÍNEZ<br />

{<br />

lineaSalida(cout, cliente);<br />

cout > transaccion; //Debe validar<br />

cliente.balance += transaccion;<br />

lineaSalida(cout, cliente);<br />

actualizarArchivo.seekp(( cuenta - 1 ) * sizeof(datoCliente));<br />

actualizarArchivo.write(reinterpret_cast(&cliente),<br />

sizeof(datoCliente));<br />

}//Fin de if<br />

else<br />

cerr > cliente.balance;<br />

cliente.numeroCuenta = cuenta;<br />

insertarEnArchivo.seekp((cuenta - 1) * sizeof(datoCliente));<br />

else<br />

insertarEnArchivo.write(reinterpret_cast(&cliente), sizeof(datoCliente));<br />

}//Fin de if<br />

cerr


MIGUEL Á. TOLEDO MARTÍNEZ<br />

//Envía a la salida una línea con la información del cliente<br />

void lineaSalida(ostream &output, const datoCliente &c)<br />

{<br />

output


MIGUEL Á. TOLEDO MARTÍNEZ<br />

P<strong>LA</strong>NEACIÓN <strong>DE</strong> <strong>LA</strong> SOLUCIÓN<br />

El siguiente paso es construir una serie de algoritmos a partir de la definición del problema. Usando el<br />

diseño estructurado de programa, dividiremos el problema en subproblemas individuales para<br />

solucionar el problema general. Identifique las diferentes tareas que se deben realizar para la solución<br />

del problema. Primero, el programa debe obtener del usuario los datos requeridos. Una vez que los<br />

datos se ingresan, el programa debe mostrar los resultados. De esta manera, podemos identificar tres<br />

tareas o funciones del programa como sigue:<br />

• Obtención del nombre del usuario, fecha de ejecución del programa, valores de corriente y<br />

resistencia.<br />

• Multiplicación de corriente y resistencia para obtener el voltaje.<br />

• Despliegue del nombre del usuario, de la fecha de ejecución del programa y de la tabla de<br />

resultados.<br />

El diagrama de estructura siguiente muestra la estructura en bloques necesarios para resolver el<br />

problema.<br />

obtenerDato()<br />

Obtenga los datos de<br />

entrada del usuario<br />

Debido a que estamos usando la técnica estructurada de bloque para diseñar el programa,<br />

debemos emplear el refinamiento paso a paso para desarrollar los algoritmos. El algoritmo de<br />

nivel inicial main(), simplemente manifestará la definición del problema y llamará a las funciones<br />

o subprogramas individuales como sigue:<br />

ALGORITMO INICIAL<br />

voltaje()<br />

Determine el voltaje de un circuito usando<br />

la ley de Ohm<br />

calcularVoltaje()<br />

Calcule el voltaje<br />

visualizarResultado()<br />

Muestre resultados<br />

voltaje()<br />

INICIO<br />

Llamada a la función para obtener del usuario los datos de entrada.<br />

Llamada a la función para calcular el voltaje de circuito usando la ley<br />

de Ohm.<br />

Llamada a la función para mostrar los resultados.<br />

FIN.<br />

El primer nivel de refinamiento necesita que mostremos un algoritmo detallado para cada módulo<br />

de subprograma o función. Estos son como sigue:<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-56


MIGUEL Á. TOLEDO MARTÍNEZ<br />

PRIMER NIVEL <strong>DE</strong> REFINAMIENTO<br />

obtenerDato()<br />

Inicio<br />

Fin.<br />

calcularVoltaje()<br />

Inicio<br />

Fin.<br />

Escribir(“Favor de introducir su nombre: ”)<br />

Leer(nombre).<br />

Escribir(“Favor de introducir la fecha: ”)<br />

Leer(fecha).<br />

Escribir/”Favor de introducir el valor de la corriente: ”.<br />

Leer(corriente)<br />

Escribir(“Favor de pedir el valor de la resistencia: ”)<br />

Leer(resistencia)<br />

voltaje = corriente x resistencia.<br />

visualizarResultado()<br />

Inicio<br />

Escribir(“Nombre del usuario: ”, nombre).<br />

Escribir(“Fecha: ”, fecha).<br />

Escribir(“RESISTENCIA CORRIENTE VOLTAJE ”)<br />

voltaje.<br />

Escribir(resistencia, corriente, voltaje)<br />

Fin.<br />

CODIFICACIÓN <strong>DE</strong>L PROGRAMA<br />

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

//* Nombre del problema: SOLPROB.CPP<br />

Salida:<br />

El problema de salida deber una tabla mostrando los valores de voltaje junto<br />

con los valores de resistencia y corriente usados en el cálculo. El nombre<br />

del usuario y la fecha de ejecución del programa aparecerán en la parte su-<br />

perior de la pantalla.<br />

Entrada:<br />

La entrada es el nombre del usuario y fecha, un valor para corriente y uno<br />

para resistencia<br />

Procesamiento:<br />

Ley de Ohm: voltaje = corriente * resistencia<br />

*/<br />

#include // Para cin y cout<br />

#include // Para setw()<br />

const int TAMANO_NOMBRE = 31; // Tamano del arreglo para el nombre del usuario<br />

const int TAMANO_FECHA = 9; // Tamano del arreglo de la fecha<br />

void main(void)<br />

{<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-57


MIGUEL Á. TOLEDO MARTÍNEZ<br />

// Definición de variables<br />

char nombre[TAMANO_NOMBRE] = "\0";<br />

char fecha[TAMANO_FECHA] = "\0";<br />

float voltaje = 0.0;<br />

float corriente = 0.0;<br />

float resistencia = 0.0;<br />

// Mensaje de descripción del problema<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

LO QUE NECESITA SABER<br />

Antes de que continúe con la siguiente lección, asegúrese de haber comprendido los<br />

siguientes conceptos:<br />

C++ proporciona el flujo de entrada cin para introducir datos por el teclado.<br />

El objeto de flujo de entrada cin (conectado normalmente al teclado) se utiliza para introducir datos.<br />

Es posible introducir varios elementos de datos concatenando operadores de extracción de flujo (>>)<br />

Cuando su programa utiliza cin para leer datos, su programa debe especificar una o más variables en<br />

la que cin colocará los datos.<br />

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

Cuando su programa usa cin para leer múltiples valores, cin utiliza espacios en blanco para<br />

determinar donde un valor termina y comienza un segundo valor.<br />

Si el usuario no introduce datos correctos, pueden ocurrir errores, y el valor que cin asigna a las<br />

variables de su programa puede ser incorrecto.<br />

Para leer los datos en C++ emplee el objeto de flujo de entrada cin. Como cout, cin es un objeto de<br />

flujo de archivo predefinido en C++.<br />

El enunciado cin debe incluir una(s) variable (s) para lectura. Cualquier variable listada dentro de un<br />

enunciado cin se debe definir antes de usarse en el programa.<br />

Además, la clase de datos ingresada para una variable determinada deberá coincidir con la clase de<br />

datos definida para esa variable.<br />

El operador de extracción de flujo >> se usa dentro del enunciado cin para extraer datos desde el<br />

flujo de entrada y asignar estos datos a la(s) variable(s) listadas en el enunciado cin.<br />

Cuando lea los datos de un solo carácter, el operador >> ignora los espacios en blanco. Sin embargo,<br />

puede usar la función get() con cin para leer datos de espacio en blanco de un solo carácter.<br />

La función miembro get() sin argumentos introduce un carácter y lo devuelve; devuelve EOF si<br />

encuentra el fin de archivo del flujo.<br />

La función miembro get() con un argumento de clase char introduce un carácter. Se devuelve EOF<br />

cuando encuentra el final del archivo, en caso contrario se devuelve el objeto istream para el que se<br />

llamó la función miembro get()<br />

La función miembro get() con tres argumentos –un arreglo de caracteres, un límite de tamaño y un<br />

delimitador (que tiene un valor predeterminado de nueva línea) –lee caracteres desde el flujo de<br />

entrada hasta un máximo del límite de caracteres – 1 y termina, o termina cuando se lee el delimitador.<br />

La cadena de entrada se termina con un carácter nulo. El delimitador no se coloca en el arreglo de<br />

caracteres, sino que permanece en el flujo de entrada.<br />

La función miembro getline() opera en forma similar a la función get() con tres argumentos. La<br />

función getline() elimina al delimitador de flujo de entrada pero no lo almacena en la cadena.<br />

Cuando lea datos de cadena de caracteres, el operador >> terminará en un espacio en blanco. Como<br />

resultado, debe usar la función getline() junto con cin cuando lea un arreglo de caracteres o cadenas.<br />

Cuando use getline(), deberá descartar los caracteres restantes “\n” en la memoria temporal del teclado<br />

a partir de una operación de lectura anterior. Para lograr esto, use un carácter delimitador diferente para<br />

getline(), una variable temporal o el manejador ws.<br />

La función miembro ignore() se salta un número especificado de caracteres (el número<br />

predeterminado es un carácter) en el flujo de entrada, y termina si encuentra al delimitador<br />

especificado (el delimitador predeterminado es EOF)<br />

La función miembro putback() devuelve al flujo el carácter anterior obtenido mediante un get()<br />

La función miembro peek() devuelve el siguiente carácter de un flujo de entrada, pero no elimina el<br />

carácter del flujo.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-59


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Las funciones gets() y fgets() están contenidas en el archivo de encabezado stdio.h, también se usan<br />

para leer cadenas. La función gets() convierte un CRLF en un terminador nulo cuando se almacena la<br />

cadena y fgets() lee y almacena el CRLF mientras se adiciona el terminador nulo al final de la cadena.<br />

Todas las E/S del programa están soportadas por archivos que operan sobre clases predefinidas en<br />

C++. Para tener acceso a los archivos en disco, use una de las siguientes tres clases: ifstream, ofstream<br />

o fstream. La clase ifstream se usa para realizas operaciones de entrada o lectura, desde archivos en<br />

disco; La clase ofstream se usa para realizar operaciones de salida o escritura, en archivos en disco. La<br />

clase fstream se puede usar para realizar ambas operaciones de lectura y escritura de archivos en disco.<br />

Todas estas tres clases se declaran en el archivo de encabezado fstream.h.<br />

Hay tres tareas que siempre se ejecutarán para manejar archivos en disco en C++:<br />

1. Defina un objeto de flujo de archivo para una de las clases de archivo fstream.h.<br />

2. Para abrir el archivo, una el objeto de flujo de archivo a un archivo en disco en particular.<br />

3. Cierre el archivo.<br />

Todos los archivos en disco contienen una marca de fin de archivo, llamado EOF, para señalar el final<br />

de un archivo determinado en el disco. Al leer un archivo en disco, instruimos al compilador a buscar<br />

un marcador de EOF y cuando se detecta termina la operación de lectura. Esto se logra con el uso de<br />

un ciclo y la verificación del marcador de EOF usando una función estándar llamada eof()<br />

Los programas amigables requieren interacción entre el programa y el usuario. Como mínimo un<br />

programa amigable debe hacer lo siguiente:<br />

1. Escribir un mensaje de descripción del programa para el usuario.<br />

2. Instruir al usuario antes de cualquier operación de lectura.<br />

3. Generar un buen formato de salida cuyo significado sea fácilmente entendible para el usuario.<br />

Todos los elementos de datos que son procesados por una computadora están reducidos a<br />

combinaciones de ceros y unos.<br />

El elemento de datos más pequeño en una computadora puede asumir el valor 0 o el valor 1. A tal<br />

elemento de dato se le llama bit.<br />

A los dígitos, las letras y los símbolos especiales se les conoce como caracteres. Al conjunto de todos<br />

los caracteres que pueden utilizarse para escribir programas y representar elementos de datos en una<br />

computadora particular se le llama el conjunto de caracteres de la computadora. Cada carácter dentro<br />

del conjunto de caracteres de la computadora está representado como un patrón de ocho uno y ceros<br />

(lla mado un byte)<br />

Un campo es un grupo de caracteres (0 bytes) que tiene significado.<br />

Un registro es un grupo de campos relacionados.<br />

Al menos un campo de un registro se elige como clave de registro para identificarlo como<br />

perteneciente a una persona o entidad particular que es única entre todos los registros del archivo.<br />

El acceso secuencial es el método más popular para acceder datos en un archivo.<br />

A un conjunto de programas que está diseñado para crear y administrar bases de datos se le llama<br />

DBMS (sistema de administración de bases de datos)<br />

C++ ve a cada archivo como un flujo secuencial de bytes.<br />

Cada archivo termina en alguna forma de marcador de fin de archivo dependiente de la máquina.<br />

Los flujos proporcionan canales de comunicación entre archivos y programas.<br />

Los archivos de encabezado y se deben incluir en un programa para<br />

realizar la E/S de archivos de C++. El archivo de encabezado incluye las defin iciones<br />

para las clases de flujo ifstream, ofstream y fstream.<br />

Los archivos se abren simplemente instanciando objetos de las clases de flujo ifstream, ofstream y<br />

fstream.<br />

C++ no impone ninguna estructura a un archivo. Por lo tanto, los conceptos como registro no existen<br />

en C++. El programador debe estructurar un archivo para satisfacer los requerimientos de una<br />

aplicación en particular.<br />

Los archivos se abren para salida creando un objeto de la clase ofstream. Se pasan dos argumentos al<br />

objeto, el nombre del archivo y el modo de apertura del archivo. Para un objeto ofstream el modo de<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-60


MIGUEL Á. TOLEDO MARTÍNEZ<br />

apertura de archivo puede ser ios::out para enviar los datos hacia el archivo o ios::app para agregar<br />

datos al final del archivo. Los archivos existentes que se abren con el modo ios::out se truncan. Los<br />

archivos que no existen se crean.<br />

La función miembro de operador operator! de ios devuelve un valor diferente de cero (true) si se ha<br />

establecido el indicador failbit o badbit para un flujo en la operación open.<br />

La función miembro de operador operator void * de ios convierte el flujo hacia un apuntador para<br />

compararlo con 0 (el apuntador nulo) Si no se han establecido ni failbit ni badbit para el flujo, se<br />

devuelve 0 (false)<br />

Los programas pueden procesar ningún archivo, un archivo o varios archivos. Cada archivo tiene un<br />

nombre único y está asociado con un objeto de flujo de archivo adecuado. Todas las funciones de<br />

procesamiento de archivo deben referirse a un archivo mediante el objeto adecuado.<br />

Un apuntador obtener indica la posición del archivo a partir de la cual va a suceder la siguiente<br />

entrada, y un apuntador colocar indica la posición del archivo en la cual se colocará la siguiente<br />

salida. Las clases istream y ostream proporcionan funciones miembro para reubicar el apuntador de<br />

posición de archivo. Las funciones son seeg() (buscar obtener) para la clase istream, y sep() (buscar<br />

colocar) para la clase ostream.<br />

Las funciones miembro tellp() y tellg() devuelven las localidades actuales de los apuntadores colocar<br />

y obtener, respectivamente.<br />

Una forma conveniente para implementar los archivos de acceso aleatorio es utilizar solamente<br />

registros de longitud fija. Mediante el uso de esta técnica un programa puede calcular rápidamente la<br />

localidad exacta de un registro con relación al inicio del archivo.<br />

Los datos se pueden insertar en un archivo de acceso aleatorio sin destruir otros datos del archivo. Los<br />

datos se pueden actualizar o borrar sin volver a escribir el archivo completo.<br />

La función miembro write() de ostream da salida, hacia un flujo especificado, a un número de bytes<br />

que comienzan en una localidad de memoria indicada. Cuando el flujo está asociado con un archivo,<br />

los datos se escriben en la localidad especificada por el apuntador de posición de archivo colocar.<br />

La función miembro read() de istream introduce un número de bytes desde el flujo especificado hacia<br />

un área en memoria que comienza en una dirección indicada. Los bytes se introducen comenzando en<br />

la localidad especificada por el apuntador de posición de archivo obtener.<br />

La función write() espera un primer argumento de tipo const char *, por lo que este argumento se debe<br />

convertir mediante cast a un const char *, en caso de que sea de algún otro tipo de apuntador. El<br />

segundo argumento es un entero que especifica el número de bytes a escribir.<br />

El operador unario sizeof() de tiempo de compilación devuelve el tamaño en bytes del objeto que está<br />

contenido entre paréntesis, sizeof() devuelve un entero sin signo.<br />

La función miembro read()e istream introduce un número especificado de bytes desde el flujo<br />

indicado hacia un objeto, read() requiere un primer argumento de tipo char *.<br />

La función miembro eof() de ios determina si se ha establecido el marcador de fin de archivo para el<br />

flujo indicado. El fin de archivo se establece después de que falla un intento de lectura.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-61


MIGUEL Á. TOLEDO MARTÍNEZ<br />

PREGUNTAS Y PROBLEMAS<br />

PREGUNTAS<br />

1. Considere el siguiente segmento de programa:<br />

char A = ‘ ’;<br />

char B = ‘ ’;<br />

cin >> A;<br />

cin >> B;<br />

cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

b. Asigne el producto de las variables b y c a la variable a.<br />

c. Indique que el programa efectúa un cálculo de nómina ejemplo (es decir, ponga un texto que ayuda a documentar el<br />

programa)<br />

d. Introduzca tres valores enteros desde el teclado y cárguelos en las variables a, b y c.<br />

22. ¿Qué es lo que se imprime (si es que se imprime algo) cuando se ejecutan las siguientes instrucciones de C++?<br />

Si no se imprime nada, entonces responda “nada”. Suponga que x = 2 e y = 3.<br />

a. cout > e >> f;<br />

b. p = i + j + k + 7;<br />

c. cout


MIGUEL Á. TOLEDO MARTÍNEZ<br />

k. Las funciones miembro ______________ y ____________ de las clases istream y ostream colocan el apuntador de<br />

posición adecuado a una ubicación especifica en un flujo de entrada o salida, respectivamente.<br />

26. Indique cuáles de las siguientes afirmaciones son verdaderas y cuáles falsas (si la respuesta es falso, explique<br />

por qué):<br />

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

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

c. Un programa debe llamar explícitamente a la función close para cerrar un archivo que esté asociado con un objeto<br />

ifstream, ofstream o fstream.<br />

d. Si el apuntador de posición de archivo apunta a una posición de un archivo secuencial diferente al inicio del archivo,<br />

el archivo debe cerrarse y volverse a abrir para leer desde el inicio del archivo.<br />

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

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

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

h. Los registros en los archivos de acceso aleatorio deben ser de longitud fija.<br />

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

27. Suponga que cada uno de los siguientes enunciados se aplica al mismo programa.<br />

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

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

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

ofstream.<br />

d. Escriba una instrucción que lea un registro desde el archivo oldmast.dat. El registro consiste del entero accountNum,<br />

la cadena name y el punto flotante currenBalance. Utilice el objeto inOldMaster de ifstream.<br />

e. Escriba una instrucción que lea un registro desde el archivo trans.dat. El registro consiste del entero accountNum y<br />

un punto flotante dollarAmount. Utilice el objeto inTransaction de ifstream.<br />

f. Escriba una instrucción que escriba un registro en el archivo newmast.dat. El registro consiste de un entero<br />

accountNum, una cadena name y un punto flotante currentBalance. Utilice el objeto outNewMaster de ofstream.<br />

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

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

salidaPagable compania >> importe;<br />

c. El archivo herramientas.dat debe estar abierto para agregar datos al archivo sin descartar los datos actuales.<br />

Ofstream salidaHerramientas(“herramientas.dat”, ios::out);<br />

29. Llene los espacios en blanco en cada una de las siguientes frases.<br />

a. Las computadoras almacenan grandes cantidades de datos en dispositivos de almacenamiento secundario como<br />

__________.<br />

b. Un ____________ está compuesto de varios campos.<br />

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

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

______________.<br />

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

___________.<br />

f. A un grupo de caracteres relacionados que tienen significado se le llama un ________________.<br />

g. Los objetos de flujo estándar que están declarados en el archivo de encabezado son ____________.<br />

_________ y ____________.<br />

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

i. La función miembro _____________ de ostream se utiliza generalmente para escribir datos a un archivo accedido en<br />

forma aleatoria.<br />

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

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-64


MIGUEL Á. TOLEDO MARTÍNEZ<br />

30. Indique cuáles de las siguientes afirmaciones son verdaderas y cuáles falsas (si la respuesta es falso, explique<br />

por qué):<br />

a. Las funciones impresionantes que realizan las computadoras involucran esencialmente la manipulación de ceros y<br />

unos.<br />

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

c. La gente especifica programas y elementos de datos como caracteres, y luego las computadoras manipulan y procesan<br />

estos caracteres como grupos de ceros y unos.<br />

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

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

computadora.<br />

f. Los elementos de datos que están representados en las computadoras forman una jerarquía de datos en la cual los<br />

elementos de datos se hacen cada vez más grandes y más complejos conforme se avanza de campos a caracteres, a<br />

bits, etcétera.<br />

g. Una clave de registro identifica a un registro como perteneciente a un campo particular.<br />

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

en computadora.<br />

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

nombre.<br />

j. Cuando un programa crea un archivo, la computadora almacena automáticamente dicho archivo para referencia<br />

futura.<br />

PROBLEMAS<br />

1. Escriba un programa que le pida al usuario que introduzca dos números, que obtenga dichos números y que<br />

imprima la suma, el producto, la diferencia y el cociente de ambos.<br />

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<br />

12.5%. Dé formato a su salida en forma apropiada, mostrando el importe del préstamo, el periodo de tiempo, la<br />

tasa de interés y la importe del interés (Nota: establezca la bandera ios::showpoint para asegurar en forma<br />

apropiada el formato de pesos y centavos para una salida monetaria).<br />

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

al revés.<br />

4. La fuerza eléctrica en un circuito de corriente directa se define como el producto del voltaje y la corriente. En<br />

símbolos, fuerza = voltaje x corriente. Escriba un programa para calcular la fuerza eléctrica de un valor de<br />

voltaje de 12 volts y un valor de corriente de 0.00125 amperes. Genere una tabla de valores de entrada y salida<br />

en forma decimal.<br />

5. Escriba un programa que emplee un ciclo para leer un archivo de carácter de nombre letminus que consista en<br />

todos los caracteres minúsculos. Convierta los caracteres minúsculos a mayúsculas restado 32 a cada carácter y<br />

escriba los caracteres mayúsculos a un archivo de nombre letmayus. (Nota: deberá crear el archivo letminus<br />

usando su editor de texto ASCII)<br />

6. Escriba un programa que calcule los valores de fuerza y voltaje escritos por el usuario. Genere una tabla de<br />

valores de entrada y salida en formato decimal.<br />

7. Escriba un programa que calcule el pago semanal bruto para un empleado, determinando el salario mínimo y el<br />

número de horas trabajadas. Suponga que el empleado es de medio tiempo y además trabaja menos de 40 horas<br />

a la semana. Genere una pantalla mostrando el nombre del empleado, el pago por hora, horas trabajadas y pago<br />

bruto. Proporcione los títulos apropiados en pantalla. (Nota: establezca la bandera ios::showpoint para asegurar<br />

la salida adecuada en formato monetario de pesos y centavos.)<br />

8. Escriba un programa para calcular la circunferencia y área de un círculo a partir de una entrada del radio escrita<br />

por el usuario. Genere una pantalla tabular mo strando el radio del círculo, la circunferencia y el área. (Nota: La<br />

circunferencia de un círculo es igual a 2 p r. El área de un círculo = p r 2.<br />

9. Escriba un programa que permita a un estudiante calcular el promedio de cuatro registros de exámenes. Genere<br />

una pantalla tabular con el nombre del estudiante, nombre del curso, registros individuales de los exámenes y<br />

promedio de exámenes.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-65


MIGUEL Á. TOLEDO MARTÍNEZ<br />

10. El equipo de boliche “cuatro escuadras” tiene cuatro jugadores. En una noche de juego, cada miembro hace tres<br />

juegos. Escriba un programa que lea la fecha, nombre del jugador y registro individual de juego para cada<br />

participante. Usando la información de entrada, muestre una pantalla del informe de boliche. El informe debe<br />

mostrar la fecha en la parte superior de la pantalla y después una tabla mostrando los registros, total y promedio<br />

entero de cada uno de los jugadores.<br />

11. Escriba un programa que calcule la resistencia equivalente de un circuito de cinco resistencias en serie escritas<br />

por el usuario. Genere una tabla de valores de entrada y salida en formato decimal. (Nota: la resistencia<br />

equivalente de un circuito en serie se obtiene sumando las resistencias individuales.)<br />

12. Escriba un programa que calcule la resistencia equivalente de dos resistencias paralelas escritas por el usuario.<br />

Genere una tabla de valores de entrada y salida en formato decimal. Use la siguiente fórmula para calcular el<br />

valor de resistencia equivalente:<br />

Requiv = (R1 x R2) / (R1 + R2)<br />

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

13. Escriba un programa que pruebe la entrada de valores enteros en formato decimal, octal y hexadecimal. Envíe a<br />

la salida a cada entero leído por el programa en los tres formatos. Pruebe el programa con los siguientes datos<br />

de entrada: 10, 010, 0x10.<br />

14. Escriba un programa que introduzca una cadena desde el teclado y determine su longitud. Imprima dicha<br />

cadena utilizando una longitud del doble de la anchura del campo.<br />

15. En algunos lenguajes de programación se introducen cadenas rodeadas de comillas sencillas o dobles. Escriba<br />

un programa que lea las tres cadenas Susana, “Susana” y ‘Susana’. ¿Las comillas sencillas o dobles se ignoran<br />

o se leen como parte de la cadena?<br />

16. Escriba un programa para mostrar que las funciones miembro getline() y el get() de tres argumentos de istream<br />

hacen que la cadena de entrada termine con un carácter nulo de terminación de cadena. También muestre que<br />

get() deja el carácter delimitador en el flujo de entrada y, en cambio getline() extrae el carácter delimitador y lo<br />

descarta. ¿Qué pasa con los caracteres que no son leídos en el flujo?<br />

17. Escriba un programa que cree el manipulador saltaBlanco definido por el usuario para saltarse los caracteres<br />

de espacio en blanco iniciales en el flujo de entrada. El manipulador deberá utilizar la función isspace() de la<br />

biblioteca ctype.h para revisar si el carácter es un espacio en blanco. A cada carácter se le deberá introducir<br />

mediante la función miembro get() de istream. Cuando encuentre un carácter que no sea espacio en blanco, el<br />

manipulador saltaBlanco terminará su trabajo colocando el carácter de devolución en el flujo de entrada y<br />

devolviendo una referencia a istream.<br />

18. La pregunta 27 le pide al lector que escriba una serie de instrucciones. De hecho, estas instrucciones forman la<br />

parte medular de un tipo importante de programa de procesamiento de archivos, es decir, un programa de<br />

concordancia de archivos. En el procesamiento de datos comerciales es común tener varios archivos en cada<br />

sistema de aplicación. Por ejemplo, en un sistema de cuentas por cobrar hay generalmente un sistema de archivo<br />

maestro que contiene información detallada acerca de cada cliente, tal como el nombre, dirección, número<br />

telefónico, saldo pendiente, límite de crédito, condiciones de descuento, acuerdos contractuales y, posiblemente,<br />

una historia condensada de las compras recientes y sus pagos realizados.<br />

Conforme suceden transacciones (es decir, se hacen ventas y llegan por correo los pagos en efectivo), éstas se<br />

introducen en un archivo. Al final de cada periodo del negocios (es decir, un mes para algunas compañías, una<br />

semana para otras y un día en algunos casos) se aplica el archivo de transacciones (llamado trans.dat en la<br />

pregunta 27) al archivo maestro (llamado oldmast.dat en la pregunta 27) y, por lo tanto, se actualiza el registro<br />

de compras y pagos de cada cuenta. Durante una ejecución de actualización se vuelve a escribir el archivo<br />

maestro como un nuevo archivo (newmast.dat), el cual luego se utiliza al final del siguiente periodo del negocio<br />

para iniciar nuevamente el proceso de actualización.<br />

Los programas de concordancia de archivo deben manejar determinados problemas que no existen en los<br />

programas de un solo archivo. Por ejemplo, no siempre sucede una concordancia. Puede ser que un cliente que<br />

esté en el archivo maestro no haya hecho ninguna compra o pago en efectivo en el periodo actual del negocio y,<br />

por lo tanto, no aparecerá ningún registro para este cliente en un archivo de transacciones. En forma similar, un<br />

cliente que hizo alguna compra o pago en efectivo puede haberse cambiado a esta comunidad, y puede ser que<br />

la compañía todavía no haya tenido oportunidad de crear un registro maestro para este cliente.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-66


MIGUEL Á. TOLEDO MARTÍNEZ<br />

Utilice las instrucciones que se escribieron en la pregunta 27 como base para escribir un programa completo de<br />

concordancia de archivo de cuentas por cobrar. Utilice el número de cuenta de cada archivo como clave de<br />

registro para efectos de concordancia. Suponga que cada archivo es un archivo secuencial con los registros<br />

almacenados en orden ascendente por número de cuenta.<br />

Cuando suceda una concordancia (es decir, cuando aparezcan registros con el mismo número de cuenta en el<br />

archivo maestro y en el archivo de transacciones) súmele el importe en dólares que está en el archivo de<br />

transacciones al saldo actual, que está en el archivo maestro, y escriba el registro del newmast.dat. (Suponga<br />

que las compras están indicadas por cantidades positivas en el archivo de transacciones y que los pagos están<br />

indicados por cantidades negativas). Cuando hay un registro maestro para una cuenta particular, pero no hay<br />

registro de transacciones correspondiente, escriba simplemente el registro maestro a newmast.dat. Cuando haya<br />

un registro de transacción, pero no hay registro maestro correspondiente, imprima el mensaje registro de<br />

transacción no concuerda con uno maestro... (llene el número de cuenta a partir del registro de transacciones)<br />

19. Después de escribir el programa del problema 18 escriba un programa simple para crear algunos datos de<br />

prueba para la revisión del programa. Utilice los siguientes datos de cuentas por ejemplo:<br />

Número de cuenta<br />

En archivo maestro<br />

Nombre<br />

Saldo<br />

100 Juan Pérez 348.17<br />

300 María Candelaria 27.19<br />

500 Samuel Hernández 0.00<br />

700 Susana Dosamantes -14.22<br />

Número de cuenta<br />

En archivo de transacciones<br />

Cantidad de la transacción<br />

100 27.14<br />

300 62.11<br />

400 100.56<br />

900 82.17<br />

20. Ejecute el programa del problema 18 utilizando los archivos de datos de prueba que se crearon en el problema<br />

19. Imprima el nuevo archivo maestro. Revise que las cuentas se hayan actualizado correctamente.<br />

21. Es posible (y de hecho es común) tener varios registros de transacciones que tengan la misma clave de registro.<br />

Esto sucede debido a que un cliente particular puede hacer varias compras y pagos en efectivo durante un<br />

periodo del negocio. Vuelva a escribir el programa de concordancia de archivos de cuentas por cobrar del<br />

problema 18 para tomar en cuenta la posibilidad de manejar varios registros de transacciones que tengan la<br />

misma clave de registro. Modifique los datos de prueba del problema 19 para incluir los registros de<br />

transacciones adicionales siguientes:<br />

Número de cuenta<br />

En archivo de transacciones<br />

Cantidad de la transacción<br />

300 83.79<br />

700 80.78<br />

700 1.53<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-67


MIGUEL Á. TOLEDO MARTÍNEZ<br />

22. Escriba una serie de instrucciones que logren cada una de las siguientes cosas. Suponga que se ha definido la<br />

estructura:<br />

struct persona<br />

{<br />

};<br />

char apellido[15];<br />

char nombre[15];<br />

char edad[2];<br />

y que el archivo de acceso aleatorio se ha abierto adecuadamente.<br />

a. Inicialice el archivo nombreEdad.dat con 100 registros que contengan apellido = “no asignado”, nombre = “ “ y<br />

edad = “0”.<br />

b. Introduzca 10 apellidos, nombres y edades y escríbalos en el archivo.<br />

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

d. Borre el registro que tenga información volviendo a inicializar ese registro particular.<br />

23. Usted es el propietario de una ferretería y necesita llevar un inventario que pueda decirle qué tantas<br />

herramientas diferentes tiene, qué tantas hay en existencia y el costo de cada una. Escriba un programa que<br />

inicialice el archivo de acceso aleatorio hardware.dat con 100 registros vacíos, permita que se introduzcan los<br />

datos relacionados con cada herramienta, que se listen todas las herramientas, que se borre un registro para una<br />

herramienta que ya no se tenga y que se actualice cualquier información que haya en el archivo. El número de<br />

identificación de cada herramienta deberá ser el número de registro. Utilice la siguiente información para iniciar<br />

el archivo.<br />

# de registro Nombre de la herramienta Cantidad Costo<br />

3 Lijadora eléctrica 7 57.98<br />

17 Martillo 76 11.99<br />

24 Sierra 21 11.00<br />

39 Cortadora de césped 3 79.50<br />

56 Pinzas 18 99.99<br />

68 Desarmador 106 6.99<br />

77 Almádena 11 21.50<br />

83 Llave de tuerca 34 7.50<br />

24. Escriba un programa que utilice el operador sizeof() para determinar los tamaños en bytes de los diversos tipos<br />

de datos que hay en el sistema de cómputo que usted tiene. Escriba los resultados en el archivo tamdato.dat<br />

para que pueda imprimir posteriormente el resultado. El formato de los resultados en un archivo debe ser:<br />

Tipo de dato Tamaño<br />

char 1<br />

unsigned char 1<br />

short int 2<br />

unsigned short int 2<br />

int 4<br />

unsigned int 4<br />

long int 4<br />

unsigned long int 4<br />

float 4<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-68


MIGUEL Á. TOLEDO MARTÍNEZ<br />

double 8<br />

long double 16<br />

Nota: Los tamaños de los tipos de datos integrados en su computadora pueden ser diferentes de los que se<br />

listan anteriormente.<br />

EXAMEN BREVE 17<br />

1. El operador que deberá emplear para extraer datos del flujo de entrada cin es el operador<br />

___________________________.<br />

2. Escriba los enunciados para pedir al usuario que ingrese el valor para una variable entera llamada<br />

numero. Use cin para leer la entrada del usuario.<br />

3. Dé algunos ejemplos de espacios en blanco.<br />

4. Escriba un enunciado para leer un carácter de espacio en blanco y almacénelo en una variable<br />

llamada espacioBlanco.<br />

5. Escriba un enunciado para mostrar el carácter de espacio en blanco leído en la pregunta 4.<br />

6. Cierto o falso: cuando se leen datos de un solo carácter, cin >> sólo leerá un carácter a la vez.<br />

7. ¿Cuándo termina el enunciado cin, si se usa el operador >> para leer datos de cadenas de carácter?<br />

8. ¿Qué función puede emplearse con cin para leer datos de cadena que incluyan espacios en blanco?<br />

9. ¿Cuándo debe usarse gets() o fgets() en lugar de cin para leer datos de cadenas de caracteres?<br />

10. Utilice la función gets() para leer una cadena de hasta 25 caracteres y almacenarla en una variable<br />

denominada nombre.<br />

11. ¿Cómo trata gets() el carácter CRLF?<br />

12. ¿Cómo trata fgets() el carácter CRLF?<br />

EXAMEN BREVE 18<br />

1. ¿Qué archivo de encabezado deberá incluirse para leer/escribir archivos en disco?<br />

2. ¿Qué clase se usa para definir objetos de archivo de entrada?<br />

3. La clase que se usa para definir objetos de archivo de salida es: ________________.<br />

4. ¿Cuáles son las tres tareas que deben ejecutarse siempre para leer o escribir cualquier archivo en<br />

disco?<br />

5. Defina un objeto de nombre miEntrada como un objeto de archivo de entrada y uno de nombre<br />

miSalida como un objeto de archivo de salida.<br />

6. Escriba los enunciados para abrir un archivo de disco llamado datos y procesarlo por medio del<br />

objeto de archivo de entrada de la pregunta 5 y a un archivo en disco llamado resultado para<br />

procesarlo durante el uso del objeto de archivo de salida de la pregunta 5.<br />

7. Suponga que el archivo llamado datos en la pregunta 6 contiene un número desconocido de enteros.<br />

Escriba el código que se necesita para leer cada entero en el archivo, multiplicarlo por 10 y escriba el<br />

producto en el archivo resultado en la pregunta 6.<br />

8. Combine todas sus respuestas para las preguntas anteriores dentro de un programa sencillo en C++<br />

que realizará las tareas de procesamiento de archivos indicadas.<br />

FUNDAMENTOS – <strong>LECCIÓN</strong> 7 7-69


MIGUEL Á. TOLEDO MARTÍNEZ<br />

RESPUESTAS EXAMEN BREVE 17<br />

1. El operador que deberá emplearse para extraer datos del flujo de entrada cin es el operador >>.<br />

2. Los siguientes enunciados pedirán al usuario que introduzca un valor para una variable entera<br />

llamada numero usando cin para leer la entrada del usuario.<br />

cout numero;<br />

3. Los espacios , los tabuladores, las líneas nuevas (CRLF) y los retornos de carro se consideran<br />

espacios en blanco.<br />

4. cin.get(espacioBlanco);<br />

5. cout.put(espacioBlanco)<br />

6. Cierto. Cuando se lee un dato de tipo carácter, cin leerá solamente un carácter a la vez.<br />

7. Cuándo encuentre el espacio en blanco<br />

8. La función getline() se puede usar como cin.getline() para incluir espacio en blanco cuando se lea<br />

una cadena de datos.<br />

9. La función gets o fgets() se debe usar en lugar de cin cuando se lea una cadena de datos después de<br />

leer datos numéricos o de carácter.<br />

10. char nombre[26];<br />

gets(nombre);<br />

11. Convierte el carácter CRLF en un terminador nulo.<br />

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

RESPUESTAS EXAMEN BREVE 18<br />

1. El archivo de cabecera fstream.h debe incluirse para leer o escribir archivos en disco.<br />

2. La clase que se utiliza para definir los objetos de archivo de entrada es la ifstream.<br />

3. La clase que se utiliza para definir los objetos de archivo de salida es la ofstream.<br />

4. Las tareas que siempre debe ejecutar para procesar cualquier archivo en disco son: (1) definir el<br />

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

5. ifstream miEntrada;<br />

ofstream miSalida;<br />

6. miEntrada.open(“datos”);<br />

miSalida.open(“resultado”);<br />

7. // Lee el primer entero<br />

miEntrada >> entero;<br />

// Lee y procesa los datos del archivo usando un ciclo<br />

while(!miEntrada.eof())<br />

{ // Inicio del ciclo<br />

vecesDiez = 10 * entero; // multiplica el entero por 10<br />

miEntrada >> entero; // Lee un entero del archivo de entrada.<br />

miSalida


MIGUEL Á. TOLEDO MARTÍNEZ<br />

}<br />

8. #include // Para archivo de E/S<br />

void main(void)<br />

}<br />

{<br />

// Define variables<br />

int entero; // Variable entera para datos de entrada<br />

int vecesDiez; // Variable entera para el producto 10 veces<br />

// Define los archivos objeto y archivos abiertos<br />

ifstream miEntrada; // Define objeto entrada<br />

ofstream miSalida; // Define objeto salida<br />

miEntrada.open(“datos”); // Abre archivo de entrada<br />

miSalida.open(“resultado”); // Abre archivo de salida<br />

// Lee el primer entero<br />

miEntrada >> entero;<br />

// Lee y procesa los datos del archivo utilizando un ciclo<br />

while(!miEntrada.eof())<br />

{ // Inicio del ciclo<br />

vecesDiez = 10 * entero; // multiplica el entero por 10<br />

miEntrada >> entero; // Lee un entero del archivo de entrada.<br />

miSalida

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!