Archivos y Cadenas
Archivos y Cadenas
Archivos y Cadenas
La informacin se almacena para posteriormente utilizarla en beneficio de las entidades, instituciones o personas particulares que la requieran, con el fin de elaborar informes, consultas para una buena toma de decisiones. Por eso se hace necesario el cmo guardar la informacin en los diferentes medios de almacenamiento. Este captulo trata de cmo se puede almacenar en C++ los datos y su posterior uso. Los archivos se pueden abrir para: Aadir datos Leer datos Reemplazar datos En C++ se utilizan las siguientes clases para cada una de las anteriores acciones, teniendo en cuenta que se debe incluir el archivo de cabecera fstream: Aadir datos Leer datos Reemplazar datos Ofstream Ifstream Ofstream
Se debe dar nombre al flujo de datos sobre el cual viajan los datos, es un nombre de su eleccin, as:
Accin
Clase
de de
Este flujo de datos permite acceder al archivo fsico como tal, este archivo tiene un nombre como en la siguiente tabla: Accin Clase Ejemplo de Nombre de Nombre flujo de archivo fsico datos Agrega (nombre_archivo) Lee Remplaza (nombre_archivo) (nombre_archivo)
El nombre del archivo se acompaa con el modo de apertura: Accin Clase Ejemplo de Nombre de archivo fsico Nombre flujo de datos Agrega (nombre_archivo,ios::app) Lee Remplaza (nombre_archivo) (nombre_archivo,ios::out)
42 43 44 45 endl; 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
int op=2,code,asigna,nota; ofstream grabar("datos.txt",ios::app); do{ cout << "Codigo estudiante " << cin >> code; cout << "Asignatura " << endl; cin >> asigna; cout << "nota " << endl; cin >> nota; grabar << code << " " << asigna << " " << nota << endl; cout << "Para terminar digite 3 "; cin >> op; }while(op!=3); grabar.close(); } int leeDatos(){ ifstream leer("datos.txt"); int k=0; while(leer >> estudia[k].codigo >> estudia[k].asignatura >> estudia[k].nota) k++; leer.close(); return k-1; } void reemplazaDatos(){ int op; ofstream reemplaza("datos.txt",ios::out); do{ cout << "Codigo estudiante " << endl; cin >> estudia[0].codigo; cout << "Asignatura " << endl; cin >> estudia[0].asignatura; cout << "nota " << endl; cin >> estudia[0].nota; reemplaza << estudia[0].codigo << " " << estudia[0].asignatura << " " << estudia[0].nota << endl; cout << "Para terminar digite 3 "; cin >> op; }while(op!=3); reemplaza.close(); } void muestraDatos(int k){ int j=0; cout << "Codigo estudiante " << setw(10) << "Asignatura " << setw(10) << "nota " << endl; while (j<=k) { cout << estudia[j].codigo << setw(20) << estudia[j].asignatura << setw(14)<< estudia[j].nota << endl; j++; } }
78 79 80 81 82 83 84 85 86
87 88
89 90 91
En la lnea 43 hay un ejemplo de apertura de archivo en modo de agregar datos, en la lnea 59 se encuentra un ejemplo apertura de archivo nicamente para lectura de datos, en la lnea 69 tenemos un ejemplo de apertura de archivo para sobre escritura de datos. Analicemos la lnea 43: ofstream grabar("datos.txt",ios::app); ofstream se refiere a la clase que permite crear un flujo de salida, para escritura de datos. grabar es el nombre del flujo de datos datos.txt es el archivo fsico donde se almacenan los datos ios::app el modo de apertura del archivo, en este caso para aadir datos. Tanto la lectura como la escritura de datos se complementan con las instrucciones para tal fin, cuya sintaxis es: Para lectura de datos: Nombre_flujo_datos >> campo1 >> >> campo2 >> >> >> campo_n;
El Nombre_flujo_datos, hace referencia al nombre del flujo cuando se hizo la apertura del archivo para la lectura con la clase ifstream. Para escribir datos: Nombre_flujo_datos << campo1 << << campo2 << << << campo_n; Nombre_flujo_datos, hace referencia al nombre del flujo cuando se hizo la apertura del archivo para la escritura con la clase ofstream. En el ejemplo anterior, observar la lnea nmero 61, donde se estn leyendo los datos almacenados en el archivo datos.txt. El flujo de entrada tiene como nombre leer, creado en la lnea 59 ifstream leer("datos.txt");. As mismo, en la lnea 51, se estn grabando los datos capturados por teclado entre las lneas 45 y 50. El flujo de salida tiene como nombre grabar, creado en la lnea 43 ofstream grabar("datos.txt",ios::app);.
ESTRUCTURAS DE DATOS
Manipulamos datos simples de un solo tipo de datos, tales como int, double, float char, etc., pero hay situaciones de programacin en las cuales necesitamos manejar un conjunto de datos de diferente tipo para hacer referencia a un objeto, en este caso creamos datos compuestos por cada uno de los diferentes tipos de datos necesitados, a travs de una estructura especial que permita almacenar estos datos por cada objeto en estudio. En C++ contamos con la instruccin struct cuya sintaxis es: struct nombre_de_la_estructura{ tipo_dato miembro_1; tipo_dato miembro_2; tipo_dato miembro_3; . . . tipo_dato miembro_n; }; Ejemplo: struct producto{ int codigo; char nombre[15 ]; double precio; }; Donde struct es la palabra reservada para indicarle al compilador que se trata de una estructura compuesta. Producto, corresponde al nombre de la estructura a travs del cual se van crear las variables que nos permiten almacenar datos en forma temporal. Cdigo, nombre, precio se refieren a cada uno de los miembros de la estructura, e , int, char, double son los tipos de datos de cada uno de los miembros de la estructura. Para crear una variable de una estructura se indica el nombre de la estructura y enseguida el nombre de la variable: Por ejemplo: producto unico; la variable nico puede hacer referencia a todos y cada uno de los tipos de datos contenidos en la estructura anteponiendo un punto al tipo de datos, as: unico.codigo; unico.nombre; unico.precio;
Como una estructura simple de datos una estructura compuesta, puede crear variables de tipo arreglo, posponiendo al nombre de la variable el tamao del arreglo:
Por ejemplo: producto x[3]; producto es la estructura x[3] es la variable tipo arreglo que permite almacenar 3 elementos. Para acceder a cada uno de los miembros de la estructura y cada uno de los 3 elementos que esta variable puede contener se hace: X[0].codigo X[1].codigo X[2].codigo X[0].nombre X[1].nombre X[2].nombre X[0].precio X[1].precio X[2].precio
Para acceder a cada uno de los elementos del arreglo se utiliza la posicin relativa del elemento en el mismo, partiendo de la posicin 0, de esta forma, la posicin 0 hace referencia al primer elemento del arreglo, la posicin 1 al segundo y as sucesivamente. Para recorrer los datos de una estructura en un arreglo, se utiliza la posicin relativa del dato en el mismo, dada por su posicin en el arreglo. Ejemplo: El departamento de ventas de un concesionario desea manejar los datos de los 300 autos que tienen en consignacin, con los siguientes datos: El modelo, la marca, la placa, el color y la cedula del dueo. Realizando el planteamiento, extraemos lo siguiente: Tipo de datos Nombre variable Int Modelo,cdueno Char Marca[16],placa[6],color[12] La estructura entonces queda as en C++: Como se observa nuestra estructura autos tiene dos miembros de tipo entero, y tres miembros de tipo cha. Con una pequea modificacin se pueden crear variables, que puedan acceder a los miembros de la estructura, y queda nuestra estructura de la siguiente forma:
Donde las variables nuevos, y usados se han declarado a partir de la estructura, la ventaja es que se pueden utilizar como variables globales en todas las funciones del programa. Para estructura se realiza la siguiente funcin: ingresar datos utilizando la
Para ingresar los datos a cada uno de los miembros de la estructura, se utiliza el arreglo usados, notar que la variable no se ha pasado a la funcin, precisamente porque fue declarada como global, junto a la definicin de la estructura. De acuerdo a definicin, mximo se pueden ingresar 10 datos, por esta razn en el main(), se debe validar la variable pos, como se ver posteriormente, esta (pos), aparece referenciada precisamente porque se guarda un dato y se incrementa con el fin de abrirle espacio a la siguiente informacin. Taller: Complementar el ejercicio agregando las siguientes funciones, al cdigo que se involucra a continuacin: Una funcin que permita buscar los autos de una persona conocida la cedula. Una funcin que muestre todos los autos contenidos en el arreglo. Una funcin que muestre los autos de una marca especfica.
RECURSIVIDAD
La recursividad es la caracterstica que tiene una funcin de llamarse a s misma, en el lenguaje C++, se presenta la facilidad de que una funcin se llame a s misma, con el fin de realizar un clculo repetitivo y especfico. Otras caractersticas son: Una funcin recursiva debe retornar un valor En el caso de tener ramificaciones, es necesario retornar un valor por cada una Al menos debe tener un parmetro. Existen dos tipos de recursividad: La directa, sucede cuando la funcin se llama as misma. La indirecta, sucede cuando la funcin A llama a la funcin B, y esta vuelve a llamar a la funcin A. El siguiente ejemplo sobre el clculo de un nmero elevado a una potencia tiene el siguiente cdigo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> using namespace std; void pidebase(int &); void pideexpo(int &); int potencia(int,int); void muestracum(int,int); void main(){ int base,ponente=0; do{ pidebase(base); pideexpo(ponente); muestracum(base,ponente); }while(base != 0); } void pidebase(int &base){ cout << "Numero base "; cin >> base; } void pideexpo(int &ponente){ cout << "Exponente "; cin >> ponente; } int potencia(int base,int ponente){ if(ponente>1) return base * potencia(base,ponente-1); return base; } void muestracum(int base, int ponente){
36 37 38 39
cout << "La potencia del numero " << base << " es " << potencia(base,ponente) << endl; system("pause"); }
La funcin recursiva se encuentra entre las lneas 29 y 33, donde : int indica que el tipo de dato que retorna es de tipo entero. potencia es el nombre de la funcin (int base, int ponente) los parmetros de la funcin, base es la variable que lleva el valor acumulado y ponente es la variable que se tiene en cuenta para salir de la funcin. return base * potencia(base,ponente 1) retorna el valor de la funcin mientras la variable ponente sea mayor de uno, es la caracterstica que demuestra que esta funcin es recursiva. return base cuando se ha cumplido la potencia, teniendo en cuenta el valor de la variable ponente, se devuelve el valor final calculado, como la potencia del nmero dado. Taller: La serie de Fibonacci es 0,1,1,2,3,5,8,13,21 donde el tercer nmero es la suma de los dos anteriores partiendo de los valores 0 y 1, crear un programa que genere la serie de Fibonacci hasta un numero n ingresado por teclado, utilizando para el clculo de la serie una funcin recursiva.
PASO POR REFERENCIA Hay momentos en los que se necesita modificar el contenido de las variables en una funcin diferente a aquella en la cual se declar, es el momento de utilizar la opcin paso por referencia, pero qu es lo que sucede en realidad?, al pasar los valores de las variables a otra funcin diferente a aquella en la cual fueron declaradas, solo se puede usar su valor mas no modificarlo, veamos el siguiente ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> using namespace std; void pedirvalor(); void duplicavalor(int valor,int doble); void imprimir(int valor,int doble); void main(){ int valor=0,doble=0; cout << "Valor a duplicar "; cin >> valor; duplicavalor(valor,doble); imprimir(valor,doble); } void pedirvalor(){ int valor; } void duplicavalor(int valor,int doble){ doble= valor * 2; } void imprimir(int valor,int doble){ cout << "El doble de " << valor << " es " << doble << endl; system("pause"); }
Como se observa a pesar de que los clculos estn bien elaborados, no cumple con el objetivo de duplicar el nmero ingresado por teclado, el valor impreso de la variable doble, es cero, que paso? En la instruccin 10, se declara y se inicializa en cero la variable doble, en la instruccin 13, se enva la variable como parmetro a la funcin duplicavalor, con el fin de obtener el doble del valor inicial, como se ve en la instruccin 23, cuando el control retorna a la instruccin 14, se enva la variable como parmetro a la funcin imprimir, y en la instruccin 27,se imprime con el valor inicial cuando se inicializ(instruccin 10), porque la variable fue definida localmente a la funcin main(), y su valor no se modifica en otra funcin hasta cuando se le indique lo contrario, esto se puede realizar utilizando el paso por referencia que consiste en pasar la direccin de la variable como parmetro, y le indica a la funcin donde se encuentra sta en la memoria. Para referenciar una variable se antepone el smbolo &(ampersand), a la variable. La sintaxis de una funcin con variable(s) referenciada(s) es: Tipo_dato_retorno nombre_funcin(td par1,td &par2,td parn,td &parm) Donde td significa tipo de datos. Notar que las variables referenciadas, anteponen el smbolo &. Taller
#include <iostream> using namespace std; void pideprecio(double precio); void calculaiva(double precio,double valiva); void imprimeiva(double precio,double valiva); void main(){ double precio=0,valiva=0; pideprecio( precio); calculaiva( precio, valiva); imprimeiva( precio, valiva); } void pideprecio(double precio){ cout << "Escriba el precio del articulo "; cin >> precio; } void calculaiva(double precio,double valiva){ valiva =precio * 0.16; } void imprimeiva(double precio,double valiva){ cout << "Para un precio de " << precio << " su iva es de " << valiva << endl; system("pause");
Agregando un & a las dos variables adecuadas, hacer que el programa cumpla el objetivo de encontrar el iva, a un precio dado. El siguiente programa calcula el costo de una pizza, teniendo en cuenta el tamao y el nmero de ingredientes (pizza personalizada), pero, hay un error y es que existen dos variables que se deben referenciar, encuntrelas, corrjalas y depure el programa.
#include <iostream> #include <iomanip> using namespace std; void pidetamano(char tamano); void ingredientes(int ing); double calculapizza(char tamano); double calculaprecio(double semivalor,int ing); void imprime(double precio); void main(){ double semivalor,precio; int ing; char tamano; pidetamano(tamano); ingredientes(ing); semivalor=calculapizza(tamano); precio=calculaprecio(semivalor,ing); imprime(precio); } void pidetamano(char tamano){ do{ cout << setw(40) << "Escriba la letra correspondiente al tamano" << endl; cout << setw(50) <<"A.Personal" << endl; cout << setw(50) <<"B.Mediana" << endl; cout << setw(50) <<"C.Grande" << endl; cout << setw(50) <<"D.Extra Grande" << endl; cin >> tamano; }while(tamano !='a' && tamano!='b' && tamano!='c' && tamano!='d'); } void ingredientes(int ing){ do{ cout << setw(40) << "Escriba el numero correspondiente " << endl; cout << setw(50) <<"1.Ingrediente" << endl; cout << setw(50) <<"2.Ingredientes" << endl; cout << setw(50) <<"3.Ingredientes" << endl; cout << setw(50) <<"4.Ingredientes" << endl; cin >> ing; }while(ing<1 || ing>4); } double calculapizza(char tamano){ double semivalor; switch(tamano){ case 'A': case 'a': semivalor=10000;
break; case 'B': case 'b': semivalor=13000; break; case 'C': case 'c': semivalor=16000; break; case 'D': case 'd': semivalor=20000; break; default: semivalor=1; break; } return semivalor; } double calculaprecio(double semivalor, int ing){ double total; int y; y=semivalor/1000; switch(y){ case 10: total=10000+ing*3000; break; case 13: total=10000+ing*4500; break; case 16: total=10000+ing*5500; break; case 20: total=10000+ing*7500; } return total; } void imprime(double precio){ cout << "Total= " << precio << endl; system("pause"); }
CADENAS Al manejar cadenas de caracteres se debe tener en cuenta que estas terminan en un carcter NULL, es decir \0. Realmente una cadena de caracteres es un arreglo en donde cada caracter se encuentra posicionado en un elemento del arreglo, se puede hacer referencia a cada caracter, mediante la posicin relativa del mismo en la cadena y/o el arreglo. El siguiente ejemplo muestra esta situacin.
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> using namespace std; void main(){ char palabra[7]; cout << "Escriba una palabra de maximo 6 caracteres "; cin >> palabra; cout << "La cuarta letra es " << palabra[3] << endl; system("pause"); }
En la lnea 6 se declara la variable palabra como arreglo de 1 dimensin para 7 elementos, Primera observacin, porqu se declara de 7 elementos, si en el mensaje de la lnea 8 se pide una palabra de mximo 6 caracteres?, recuerde que todo cadena de caracteres termina en NULL (\0), ahora si se declara como:
char palabra[6];
Es precisamente para evitar este error que el arreglo se debe declarar como de 7 caracteres. Segunda observacin, no hay necesidad de involucrar el nmero de caracteres que recibe el arreglo, en la lnea 9, cuando se pide el dato, el compilador asume el tamao. Qu sucede si se digitan nmeros? Qu pasa si se escriben ms caracteres de los pedidos? Tercera observacin: en la lnea 11, se muestra el cuarto elemento a travs de su posicin en el arreglo, veamos cmo se almacena:
o s
o \0
Tenga en cuenta que en C++, las posiciones de los arreglos se numeran desde cero, luego el cuarto elemento est ubicado en la posicin 3, y si desea imprimir cada caracter, uno a uno, solo se debe indicar la posicin. Qu sucede si se utiliza para imprimir la posicin 6? Porqu sale esa impresin? Manejo de cadenas Hay una situacin especial en el manejo de cadenas y es el hecho que al declarar un arreglo de tipo char, la instruccin cin, para lectura de datos de este tipo corta la lectura cuando encuentra un carcter de espacio, es decir, si se trata de ingresar dos o ms palabras separadas por un espacio, el compilador trunca despus de la palabra inicial, perdiendo el resto. En caso de que la palabra sea mayor que el tamao del arreglo, asume pero emite un mensaje de error similar al mencionado anteriormente. La solucin para evitar este tipo de errores, es utilizar la funcin getline, su sintaxis es: cin.getline(nombre_arreglo, tamao_arreglo,carcter_de_fin); Nombre_arreglo: es el nombre del arreglo que va a contener la cadena. Tamao_del_arreglo:considera la cantidad de caracteres se desea ingresar, tener en cuenta que se debe dejar un carcter para el NULL. Carcter_de_fin:El caracter