Manejo de archivos

Sitio: Facultad de Ingeniería U.Na.M.
Curso: Computación ET-344
Libro: Manejo de archivos
Imprimido por: Invitado
Día: miércoles, 3 de julio de 2024, 06:16

1. Introducción

En C++, se utilizan streams (flujos) para gestionar la lectura y escritura de datos.
Existen cuatro flujos predefinidos.

  1. cin: Entrada Estandar ( normalmente teclado). (les suena?)
  2. cout: Salida Estandar ( normalmente Monitor).(les suena?)
  3. cerr: Salida de Error Estandar no almacenados en Buffer ( normalmente Monitor).
  4. clog: Salida de Error Estandar almacenados en Buffer.

Desde el punto de vista informático, un fichero es una colección de información que almacenamos en un soporte magnético u óptico para poder manipularla en cualquier momento.
Los Flujos o Streams se pueden también asociar a Nombres de ficheros y con ello podemos acceder a manipular un fichero.

Técnicamente, un Stream o Flujo es el enlace lógico utilizado por el programador en C o
C++ para leer o escribir datos desde y hacia los dispositivos estándar conectados a la PC. A modo de recordatorio ya trabajamos con flujos de entrada y salida ellos son: cin y cout.
Ver que existe entre el Archivo y el Stream un buffer de memoria RAM.
Las operaciones de E/S implementadas así son más eficientes ya que el acceso a la memoria RAM consume menos tiempo que el acceso a un dispositivo físico. El buffer hace que el número de accesos al fichero físico sea menor. El uso del buffer permite realizar operaciones de entrada salida de forma más eficiente aún.

Importante : Al igual que los flujos cin y cout , los flujos de E / S solo pueden transferir datos en una dirección , esto significa que se tienen o pueden que definir flujos diferentes para lectura y escritura de datos .

En definitiva,  trabajar con  un fichero lo vemos como trabajar con un stream.
Dicho stream permite la transferencia de datos entre el programa y el fichero en disco, o sea que no trabajamos sobre el archivo, si no mas bien sobre el stream, y los datos del stream son volcados luego al Fichero, es decir que llegamos al fichero de una manera indirecta.

Podemos pensar que los son como dispositivos lógicos streams generan o consumen información.

2. Tipos de Accesos

Los archivos se pueden acceder de distintas maneras y en consecuencia se pueden clasificar según el tipo de acceso:

Acceso Secuencial.

Los datos se almacenan de forma consecutiva , para leer el registro n hay que leer los n-1 registros anteriores. Este tipo de archivos solo son prácticos en algunos casos, pero no  lo son si queremos insertar algún contenido en el medio del mismo.

Acceso Aleatorio.

Se puede acceder a un registro concreto sin necesidad de leer todos los anteriores.
Para ver esto, pensemos por ejemplo en acceder a un tema puntual de un CD de música
(modo Aleatorio) o en caso contrario de un video en Cassette VHS ( acceso Secuencial).

Para poder usar este tipo de archivos se debe crear en el mismo una estructura, donde cada estructura tiene no que se suele conocer como “record” y dentro de este cada dato tiene un tamaño específico o campo de tamaño determinado. Veremos mas adelante el manejo de este tipo de archivos.

Veremos cada uno de estos tipos de acceso.

3. Almacenamiento de Archivos

Disco Rígido ( Informativo)

Vamos a tomar un disco rígido electromecánico como base para explicar el concepto de como se accede a los archivos.

El disco está formado por varios platos con material magnético en ambas caras y cada cara del plato tiene un cabezal que lee los datos.

Hay varios elementos que se pueden utilizar para referirse a zonas del disco:

  • Plato: cada uno de los discos que hay dentro de la unidad de disco duro.
  • Cara: cada uno de los dos lados de un plato.
  • Pista: una circunferencia dentro de una cara; la pista cero (0) está en el borde exterior.
  • Cilindro: conjunto de varias pistas; son todas las circunferencias que están alineadas verticalmente (una de cada cara).
  • Cabezal: número de cabeza o cabezal por cada cara.
  • Sector : cada una de las divisiones de una pista 512 bytes o 4KBytes.
  • Sector Geométrico:son los sectores contiguos pero de pistas diferentes.
  • Cluster: es un conjunto contiguo de sectores.

Vamos a usar para explicar como se accede a un "sectores o bloque de datos" el primer sistema de direccionamiento que se llamó CHS Cilindro-Cabezal-Sector (Cylinder-Head-Sector), con estos tres valores podemos ubicar el bloque inicial de datos.



Los archivos son secuencias de ceros y unos que son almacenados por ejemplo en el disco rígido. Los datos ( ceros y unos ) son separados en "sectores o bloques  de datos" y estos bloques son almacenados en el disco rígido, los bloques NO necesariamente son almacenados en forma contigua.

De manera simplificada podemos decir que en el disco rígido hay una zona que oficia de indice, donde relaciona el nombre del archivo con la ubicación del primer sector de datos del archivo.

El acceso a este principio a cada archivo es aleatorio, es decir podemos "saltar" al inicio del archivo, pero luego debemos en forma secuencial  "recoger" los demás "bloques de datos" del archivo para tener el archivo entero. Este proceso al ser electromecánico.

4. Sobre Archivo

Partes de un Archivo.

En el campo de la informática, se llama “archivo” al elemento de información compuesto por una suma de registros (combinaciones de bytes).
Este registro tiene los siguientes elementos:

Nombre. Cada archivo es identificable con un nombre, que no puede coincidir con otro que esté en la misma ubicación.
Extensión. Los archivos llevan una extensión opcional, que muchas veces indica su formato, esto es usado por el Sistema Operativo para saber con que aplicación se puede abrir el archivo.
Tamaño. Como se dijo, están compuestos por una serie de bytes que determinan su tamaño.
Descripción. Además del nombre y la extensión, suelen tener otras características. Dentro de estas características puede aparecer la protección del archivo, lo que significa el permiso limitado para la lectura o modificación.
Ubicación. Todos los archivos pertenecen a determinado lugar en la computadora. La mayoría se encuentran almacenados en discos rígidos.
Formato. El modo en que el archivo será interpretado depende de su formato, entre los que están los formatos de texto, ejecutable, de datos, de imagen, de audio, de vídeo, entre muchísimos otros.

Byte Mágico.

Para identificar el formato de archivo comunes, normalmente solo es necesario mirar hasta los primeros bytes del archivo en cuestión. Esto es lo que a menudo se denomina "bytes mágicos", un término que se refiere a un bloque de valores de bytes utilizados para designar un tipo de archivo para que las aplicaciones puedan detectar si el archivo que planean analizar y consumir tiene el formato adecuado o no.
La forma más fácil de inspeccionar el archivo en cuestión será examinarlo con un visor/editor hexadecimal.
Como se muestra a continuación, este es un archivo NAT_dinamica.mp4:



Si vemos el contenido del archivo en modo hexadecimal.



Veamos el contenido de otro archivo, también mp4.



Podemos ver que para estos archivos mp4 parte de los primeros bytes son IGUALES!!


Si nos remitimos a: https://en.wikipedia.org/wiki/List_of_file_signatures sitio en el que se  listan las firmas de archivos o Bytes Mágicos, datos utilizados para identificar o verificar el contenido de un archivo. 


Veamos el caso para .odt documento del Write.

Podemos ver el la página:



Veamos un par de archivos de texto creado con editor Mousepad de Linux

 
El Magic Byte:



Creé un archivo con el procesador de Texto Write y lo guardé como  .txt, en ese caso el Byte Mágico no coincide con el anterior EF BB



Fin de Archivo.

En informática, el fin de archivo (EOF: End Of File) es una condición en el sistema operativo de una computadora en la que no se pueden leer más datos de una fuente de datos.

Es importante para nuestro caso por que el flujo va a estar asociado a un archivo, por lo tanto en algún momento el flujo tendrá un EOF.

En UNIX se puede generar un EOF desde el shell (consola) tecleando Ctrl+D para indicar el EOF de datos ingresados por teclado, así como en Microsoft DOS y Windows se genera mediante la combinación Ctrl+Z.

  • Linux –> Control + D
  • Windows –> Control +Z
  • Macintosh –> Control + D

Veamos un caso de ingresar desde una terminal caracteres:

Podemos ver que presionando Ctrl + D generamos un EOF.


Tipos de Archivos.

El contenido de los archivos puede ser de dos tipos, texto o binario.
Archivos Texto.
Los datos que se almacenan en el archivo son código ASCII y por tanto, pueden ser procesados por cualquier editor de texto.
Archivos Binarios.
Los datos se almacenan en binario, por lo tanto NO esta asociado al código ASCII.
Cuando un archivo se abre en modo texto, se deben realizar varias conversiones de caracteres como la conversión de las secuencias de retorno de carro/salto de línea en nuevas líneas.
Sin embargo cuando un archivo se abre en modo binario no se realiza ninguna de estas con versiones.
Cualquier archivo que contenga texto con formato en modo datos binarios, puede abrirse en modo binario o en modo texto. La única diferencia es la conversión de caracteres.



5. Operaciones sobre Archivos

Se pueden realizar varias operaciones , estas serían:

  • Abrir archivo.
    • a) Para Entrada o lectura.
    • b) Para Salida o escritura.
        • En modo Truncado. Borro el contenido que tenía y escribo desde cero.
        • En modo Añadir. Agrego al contenido al existente.
  • Cerrar Archivo.
  • Escribir Archivo.
  • Leer Archivo.
  • Funciones de Control.

Veremos en detalle cada una de ellas

5.1. Sintaxis: Objetos y Métodos.

Antes de comenzar a ver como se utilizan estas clases y métodos de estas clases. Tienen
argumentos similares. Vamos a comentar primero que básicamente existen sintaxis diferentes para declarar objetos, estas formas o sintaxis son iguales para las clases de en estudio:

  • ifstream
  • ofstream
  • iofstream.

Vamos a ver un poco las Sintaxis.

fstreams (para lectura y escritura)

Sintáxis 1) fstream nombre_flujo () ;
Sintáxis 2) fstream nombre_flujo ( const char * , int , int = filebuf :: openprot ) ;
Sintáxis 3) fstream nombre_flujo ( int ) ;
Sintáxis 4) fstream nombre_flujo ( int _f , char * , int ) ;


ifstreams (solo para input o lectura)

Sintáxis 1) ifstream nombre_flujo () ;
Sintáxis 2) ifstream nombre_flujo ( const char * , int , int = filebuf :: openprot );
Sintáxis 3) ifstream nombre_flujo ( int ) ;
Sintáxis 4) ifstream nombre_flujo ( int _f , char * , int ) ;


ofstreams (solo para output o escritura)

Sintáxis 1) ofstream nombre_flujo () ;
Sintáxis 2) ofstream nombre_flujo ( const char * , int , int = filebuf :: openprot );
Sintáxis 3) ofstream nombre_flujo ( int ) ;
Sintáxis 4) ofstream nombre_flujo ( int _f , char * , int ) ;


Se puede notar que el uso de fstream , puede reemplazar a ifstream y ofstream, pero los ejercicios o ejemplos de este apunte usaremos basicamente ifstream y ofstream ya que la idea es trasmitir conceptos, este es el camino mas largo pero conceptualmente queda mas claro.
De todas maneras una vez que se haya entendido el concepto es fácil extrapolar al uso de fstream , solo hay que tener cuidado con los Modos por Omisión o por Defecto de cada método.

5.2. Argumento : nombre

Vamos a Comentar sobre las formas de sintaxis , pero SOLO refiriéndonos AL PRIMER ARGUMENTO , por el momento, que es el nombre.

Sintaxis 1) fstream nombre_flujo () ;

En este tipo de sintaxis , el método constructor crea un objeto que no está (aún) asociado a un archivo en disco (ver que no hay nombre de archivo figura solo paréntesis (), sin argumentos), en estos casos, se tendrá que otro método para esta-
blecer la conexión entre el stream y el archivo en disco, el método open.

  • ofstream carga();// defino un flujo de salida llamado carga, ver que no hay nombre de archivo asociado al flujo.
  • ofstream carga; // defino un flujo de salida llamado carga, ver que no hay nombre de archivo asociado al flujo.
  • fstream prueba; //defino un flujos de entrada salida llamado prueba, ver que no hay
  • nombre de archivo asociado al flujo.
  • ifstream muestra; //defino un flujo de entrada llamado muestra, ver que no hay nombre de archivo asociado al flujo.

Sintaxis 2)  fstream nombre_flujo ( const char * , int , int = filebuf :: openprot )

En el segundo tipo de Sintaxis , el método constructor crea un objeto asociado
a un archivo en disco, en estos casos, el archivo en el disco queda abierto y asociado al stream.

En este, el parámetro char * apunta a una cadena de caracteres con el nombre
del archivo. Respecto del contenido del fichero, si existe, se conserva; los nuevos datos
escritos se añaden al final. Por defecto, el fichero no se crea si no existe. Recordar que
el nombre de un arreglo es el puntero al primer elemento del arreglo, esto es similar, el
nombre del archivo es el puntero al archivo.

  • ofstream salida("prueba.txt", ios::out);//creo flujo de salida y asocio a archivo prueba.txt.
  • fstream salida("/home/usuario/prueba.txt", ios::out);//creo flujo de entrada/salida y asocio a archivo prueba.txt ubicado específicamente en un lugar.
  • fstream mostrar("/home/usuario/prueba.txt", ios::in);//creo flujo de entrada y asocio a archivo prueba.txt ubicado específicamente en un lugar.
  • fstream salida("c:/prueba.txt", ios::in | ios::out);//creo flujo de entrada/salida y asocio a archivo prueba.txt ubicado específicamente en un lugar, sería para el caso de SO Windows.
Estas dos sintaxis 1 y 2 serán las mas usadas.

Sintaxis 3) fstream nombre_flujo ( int ) ;

En la tercer sintaxis , el método crea un stream a raíz de un identificador de archivo y lo conecta al nombre de un archivo abierto previamente.

Sintaxis 4) fstream nombre_flujo ( int _f , char * , int ) ;

En el cuarto tipo de sintaxis, el método crea un stream conectado a un archivo.

5.3. Argumento: modo

El argumento modo, es el segundo argumento utilizado en el tipo de Sintaxis 2, y define
como se abrirá el archivo.

Los valores posibles para este modo son:

ios::app En este modo todas las operaciones de salida se llevan a cabo en el final del archivo.Dado que todas las escrituras son precedidos implícitamente por busca, no hay manera de escribir en otro lugar.

Ejemplo:

ofstream flujo_uno (“a:\misdatos.txt”, ios::app);// flujo de salida, agrego al final del archivo

ios::ate Cuando se establece este modo, la posición inicial será el final del archivo (at the end),pero se puede libremente buscar en cualquier lugar.

ios::binary En este modo se abre el archivo en forma binaria.

ios::in Se usa este modo para indicar que un archivo admite entrada o es solo de entrada.

ios::out Este modo es lo contrario a in, y se usa para indicar que el archivo admite solo salida. Por lo tanto no se usará en ifstream y no sería necesario incluirlo al usar ofstream.


En los casos en que se use ifstream, no será necesario su uso, ya que ifstream es para definir flujos de entrada solamente.

Tampoco será necesario especificarlo si usamos ofstream ya que el flujo definido para esta clase es solo de salida. En ambos casos no es necesario usar estos especificadores.


ios::trunc El uso de este modo hará que el contenido de un archivo , si es que ya existe se destruya, es decir dejará el  archivo vacío,con longitud cero, es decir "limpia" el archivo al usar este modo.


Estas modos  pueden combinarse usando entre ellas el operador OR: |


5.4. Argumento: acceso

El valor de acceso determina como se accederá al archivo.

Su valor implícito o por defecto es : filebuf::openprot, que especifica un archivo normal. ( filebuf: es una clase derivada de streambuf ).


Normalmente dejaremos el valor implícito, pero dependiendo el entorno de programación, podría ser necesario consultar en el manual del compilador.

Este argumento puede ser necesario de especificar un valor que no se el implícito, por ejemplo para el caso de compartir archivos en una red, este caso no será estudiado en este apunte.

6. Acceso Secuencial

Para comenzar a ver los métodos que permiten trabajar con archivos, vamos a usar mas
que nada los archivos del tipo secuencial

6.1. Metodo : close

Manos a la obra. Ahora comenzaremos a ver realmente código que nos permita utilizar los
conceptos citados anteriormente.

Figura 1

  • Es un flujo de salida, si cambiamos ofstream por ifstream al compilar ,tiraría un error, ya que al ser un flujo de entrada no podemos usar el operador “<<” .
    Ver que al ser ofstream, no es necesario indicar el modo como : ios::out, ya que se establece por defecto.
  • Al especificar ios::app, lo que se agregue al archivo; se ubicará al final.
    Ver que no se incluye  #include iostream , no es necesaria en este ejercicio.
    Cuando el programa ha terminado de manipular el fichero, éste debe cerrarse. Para cerrar un archivo, basta con ejecutar la función close sobre el flujo asociado al fichero. Vemos el uso de el método “.close”, este método se aplica al stream llamado “flujo” de manera de cerrar el mismo, esto hace que se vuelque el contenido del flujo al archivo y se cierre el archivo, puede que si omitimos esta línea al finalizar el programa el archivo es cerrado automáticamente y el flujo descartado, de todas maneras ponerlo de manera explícita hace al buen programador.

También podríamos escribir el programa anterior de la siguiente manera:

Figura 2

  • En este caso usamos la clase fstream, e indicamos que el modo será E/S ( ios::in | ios::out) y que se agregue al final con ios::app.
  • Como fstream básicamente trabaja con E/S, los argumentos de modo ios::in | ios::out , se pueden omitir y funcionaría igual el programa, de echo esto se hizo en la figura 2.

Figura 3

  • Este programa agrega al archivo anterior “prueba.txt”que tenía “Hola Mundo”, el texto :Hola1 Mundo1.
  • Si se omite en el programa ios::in | ios::out, y solamente dejamos ios::app ,esto no sucedería.
  • Se debe prestar atención a los argumentos de modo, ya que cuando no indicamos en forma explícita el argumento de modo se suelen tomar argumentos por defecto y estos ocasionan lo indicado en el punto anterior.

Figura 4
Este código tiene el argumento de modo ios::trunc, por lo tanto el contenido del archivo
“prueba.txt” será eliminado y solo quedará “Hola 1” y “Mundo 1”, eliminandose el “Hola
Mundo” anterior.

¿No necesito abrir el flujo?

En algunas condiciones, no debería cerrar el flujo. Imaginemos hice una corrección y quiero guardar esa corrección, pero seguir corrigiendo. En ese caso NO TIENE SENTIDO HACER close! , en cambio vamos a usar el método flush que guarda el contenido del flujo al archivo, pero no lo cierra.

6.2. Método : open

Todas estas clases (fstream,ifstream, ofstream) disponen además del método "open", para abrir el fichero a lo largo de la ejecución del programa.

En los códigos escritos anteriormente, el método open es implícito y se abre al crear la relación entre el stream y el archivo, pero para el caso en que la asociación se realice en otro momento se usa el método open.
El método open es un método público. El prototipo del método sería:

Donde:
name Es el nombre del fichero.
mode Es alguno de los modos citados anteriormente : in, out, ate, app, binary y trunc.


Veamos un ejemplo:

Figura 1



6.3. Método is_open

Se puede chequear archivo ha sido abierto correctamente mediante una llamada a la función miembro is_open():
bool is_open();

Este método retorna un valor de tipo bool indicando true en caso que en realidad el objeto
haya sido correctamente asociado con un archivo abierto o false en otro caso.
Veamos un ejemplo:

 ¿Por que no hago open?

Veamos un ejemplo en el que crearemos un archivo , vamos a introducir datos en el y luego ver los datos que introducimos, con la idea de aplicar los conceptos hasta aquí vistos.


  • Este programa solo abre un flujo de Salida ( Out).
  • Si al ingresar los números flotantes no equivocamos el punto “.” por la coma “,” el programa finalizará ya que el caracter que separa los decimales no es el esperado para un flotante.

Vamos a agregar ahora mas líneas al programa con el objeto de poder leer el contenido del archivo.


  • Este programa abre un flujo ( archivo) escribe y luego lee.
  • Podemos ver que existen varias líneas que controlan que los flujos (archivos) sean abiertos y cerrados como corresponde.
  • Se sugiere al lector que pruebe el mismo, verá que existe un pequeño error en la última del archivo de texto que la misma se repite, se deja como desafío para el lector ver por que sucede y como solucionar.
Tips: Es de buen programador, abrir archivos solo en modo input only (ios::in) , de esta manera se evitan escrituras o alteraciones del contenido del archivo.

Hasta ahora estuvimos trabajando con archivos del tipo secuencial, en estos ejemplo nosotros no controlamos la ubicación de DONDE vamos a escribir, básicamente lo que agregábamos lo hacíamos al final ( usando el modo ios::ate), o al inicio destruyendo el contenido del archivo en caso de que tuviera algo ( usando el modo ios::trunc).
Veremos en la sección siguiente como posicionarnos dentro del archivo, para poder controlar el lugar donde insertemos algo en el fichero.
Ambas clases ofstream e ifstream poseen métodos para obtener o posicionarse dentro del
fichero.




6.4. Ejercicio

Escribir un programa que permita presentar un menú como el siguiente:

1)Crear un flujo asociado a prueba.txt
2)Agregar contenido a un archivo al final
3)Cerrar un flujo asociado a prueba.txt
4)Ver si el flujo asociado a prueba.txt esta abierto.
5)Ver si el flujo asociado a prueba.txt esta cerrado.
6)Mostrar el contenido del archivo.
7)Finalizar

6.5. Modos por Omisión o por Defecto.

Todas las funciones miembro open de las clases ofstream, ifstream y fstream incluyen un
modo por omision o por defecto como ya comentamos, cuando abren archivos , veamos cuales son:

El valor por omision solamente es aplicado si la función es llamada sin especificar un parámetro modo. Si la función es llamada con algún argumento de modo , el parámetro por omisión es saltado, no combinado.

Ya que la primera tarea que es desempeñada sobre un objeto de las clases ofstream, ifstream y fstream es frecuentemente abrir un archivo, estas tres clases incluyen un constructor que directamente llama a la función miembro open y tiene los mismos parámetros que este.

7. Métodos de Clase ofstream

  • seekp
  • tellp

7.1. Método seekp

Método seekp: (Set position in output sequence ) 

Este método setea, pone, determina la posición de la secuencia donde el próximo carácter será insertado en el flujo de salida o output. Existe la posibilidad de pasar argumentos para hacer referencias relativas. Veamos ejemplos.


Es argumento que se pasa para indicar la posición , es del tipo “long int”.

7.2. Método tellp:

Método tellp: (Tell (decir) position in output sequence)

Este método dice, obtiene la posición actual del flujo de salida, no modifica nada solo obtiene la información de la posición actual, podríamos pensar si asociamos a un documento de texto donde se encuentra el cursor. Retorna un long int .
Veamos un ejemplo de uso de ambos.

La salida de este programa será:
10
Y el archivo prueba.txt tendría:
123456 abc

  • Vemos que el cursor queda en la posición 10, luego de escribir 1 en la posición 0, 2 en la posición 1, 3 en la posición 2..etc 9 en la posición 10.
  • Como pos=10 y se le resta 4, 6 será la nueva posición (0,1,2,3,4,5,6) seis es la séptima posición y a partir de allí comienza a agregar abc.

Se propone al alumno modificar el código, de manera de agregar algo al archivo y verificar si es coherente con la nueva posición final indicada.

8. Métodos de Clase ifstream

  • seekg
  • tellg

8.1. Método seekg

Método seekg: (Set position in input sequence ) 

Este método cambia o determina la posición del cursor en la secuencia o flujo de entrada, por eso es parte de ifstream.

Tiene posibilidad de usar una referencia relativa y los argumentos para este caso son los
mismos que para seekp de ofstream, así que no los repetiremos.


Se propone al alumno, modificar el programa utilizado en el capítulo anterio ( tellp) para cambiar la posición en donde se agregará un contenido nuevo, y que este NO sea al final del archivo.

8.2. Método tellg

Método tellg: (Get (obtener)  position in input sequence). Este método permite obtener la posición actual dentro del flujo de entrada de asociado a un archivo.

Ver que longitud es int, esto por que el archivo es chico, en caso contrario debería ser long int.

Se pide al alumno utilizando el código del capítulo anterior : seekg, pedir el ingreso de un número entero, validar que el mismo y luego posicionar el cursor en ese número entero ingresado para que el texto que se ingrese sea insertado en ese lugar.

9. Ejercicios Secuenciales.

Ejercicios sencillos que utilizan los métodos y modos vistos de archivos secuenciales.

Ejercicio 1

Crear un archivo de texto obteniendo el texto desde : https://es.lipsum.com/ , luego hacer un programa en c++ que solicite el ingreso de un caracter y muestre por pantalla cuantas veces aparece el caracter en el archivo de texto.

Ejercicio 2

Crear un archivo de texto obteniendo el texto desde : https://es.lipsum.com/ , luego hacer un programa en c++ que solicite el ingreso de un caracter y muestre por pantalla las posiciones en las que se encuentra ese caracter en el archivo de texto.

Ejercicio 3

Crear un archivo de texto obteniendo el texto desde : https://es.lipsum.com/ , luego hacer un programa en c++ que solicite el ingreso de dos caracteres y que reemplace un caracter por el otro indicando la cantidad de reemplazos realizados y las posicione dentro del archivo de texto.

Ejercicio 4

Escribir un programa en C++, que permita guardar en un archivo los valores de una lista enlazada simple. 

Cuando se ejecute/invoque al programa se debe pasar como argumento el nombre del archivo. 

Luego en la ejecución se debe presentar el siguiente menú. 

1)Cargar n instancias.
2)Listar las instancias.
3)Grabar a archivo
s ó S ) Salir. 

La lista enlazada simple que contiene: temperatura, es administrada por una clase.



10. archivo plantilla

/*
 * Manejo de archivos
 * =====================================
 * Genera un archivo en base a un archivo plantilla.
 * Completa los campos identificados con etiquetas
 * 
 * Germán Andrés Xander 2021>
 *
 * ======================================
 * ejemplo plantilla.txt
 *
 * Yo soy <nombre> <apellido>,
 * tengo <edad> años
 * y me gusta <hobby>
 */


#include <iostream>
#include <fstream>
#include <string>
using namespace std;

bool buscar(ifstream &plantilla, ofstream &salida); // por referencia para no trabajar sobre una copia
bool crear(ofstream &salida, string nombre);

int main(int argc, char **argv){
    ifstream plantilla ("plantilla.txt");   //abro el archivo con la plantilla
    ofstream salida;      // creo objeto salida sin archivo asociado
    if (plantilla.is_open()){
        if(!buscar(plantilla, salida)){   
            cout<<"No se pudo crear el archivo de salida";
            plantilla.close();
            return -1;
        }
        plantilla.close();
        salida.close();
    }
    else cout << "no se puede abrir el archivo"; 
    return 0;
}

bool buscar(ifstream & plantilla, ofstream &salida){ // busco las etiquetas y reemplazo por datos del usuario
    string linea ,etiqueta, dato;
    while (getline(plantilla,linea)) {      //leo linea x linea
        size_t posicion1=0;
        size_t posicion2=0;
        while((posicion1=linea.find("<",posicion1)) != string::npos ){  //encuentro las etiquetas
            posicion2=linea.find(">",posicion1);
            etiqueta=linea.substr(posicion1+1,posicion2-posicion1-1);   //extraigo la etiqueta
            cout<<endl<<"Ingrese su "<<etiqueta<<": "<<endl;
            getline (cin,dato);
            if(etiqueta.compare("nombre")==0){      //si la etiqueta es nombre
                if(!crear(salida,dato)){     // genero un archivo con el nombre ingresado
                    return 0;
                }
            }
            linea.replace(posicion1, posicion2-posicion1+1, dato);  //reemplazo la etiqueta por el dato ingresado
            posicion1=posicion2;
        }
        salida<<linea<<endl;        //grabo la linea
    }
    return 1;
}

bool crear(ofstream &salida, string nombre){
    nombre.append(".txt");
    salida.open(nombre, ofstream::out | ofstream::trunc);   //trunc: piso archivos existentes
    if(salida.is_open()){
        return 1;
    }else{
        return 0;
    }
}

11. Argumento como nombre de archivo

Se plantea crear un archivo pasando el nombre del archivo luego del nombre del programa ejecutable.

Si el programa se llama argumentos.cpp el ejecutable que se genera en Linux será : argumentos* ( que será ejecutable) , veamos como quedaría:

Nos ubicamos en el directorio donde está el ejecutable y ejecutamos desde la terminal:

el argumento pasado es xyw.txt por lo que el scrip, crea un archivo xyw.txt y guarda Hola 1  y luego Mundo 1.

El siguiente sería el programa.


12. Acceso aleatorio

Crear un programa en C++ que permita trabajar sobre un archivo de texto. Cada línea es un registro que incluye nombre y valor. El nombre tiene un ancho fijo de 40 caracteres y el valor un ancho fijo de 8 caracteres.

El programa debe poder:

  • agregar registros nuevos
  • modificar el valor de un registros
  • buscar registros por nombre

12.1. Ejemplo solución acceso aleatorio

Un archivo de texto contiene información de nombre y valor.
El programa permite agregar registros y modificar los valores de registros existentes.

/*
 * manejo aleatorio de archivos
 * 
 * Germán Andrés Xander 2021
 */

#include <iostream>
#include <fstream>
#include <string>

#define ancho1 40
#define ancho2 8

using namespace std;

void agregar(fstream &base_datos);
long int buscar(fstream &base_datos, string nombre);
void modificar(fstream &base_datos, long int posicion);

int main(int argc, char **argv){
    long int posicion=-1;
    char opcion;
    string nombre;
    fstream base_datos("bd.txt", fstream::in | fstream::out);
    do{
        cout<<endl<<endl<<"Ingrese opcion:"<<endl
        		<<"1 agregar"<<endl
        		<<"2 buscar"<<endl
        		<<"3 modificar"<<endl
        		<<"s salir"<<endl;
        cin>>opcion;
        cin.ignore(100, '\n');
        switch (opcion){
            case '1': {
                agregar(base_datos);
                break;
            }
            case '2':{
                cout<<endl<<"Ingrese nombre a buscar: ";
                getline(cin,nombre);
                posicion=buscar(base_datos, nombre);
                if(posicion <0){
                    cout<<endl<<"no se encontró el nombre";
                }else{
                    cout<<endl<<"nombre encontrado en la posición: "<<posicion;
                }
                break;
            }
            case '3':{
                if(posicion>=0){
                    modificar(base_datos,posicion);
                    posicion=-1;
                }else{
                    cout<<endl<<"primero busque un nombre";
                }
                break;
            }
            case 's':{
                base_datos.close();
            }
        }
    }while(opcion!='s');
    return 0;
}

void agregar(fstream &base_datos){
    string nombre;
    string valor;
    cout<<endl<<"Ingrese nombre a agregar: ";
    getline(cin,nombre);
    nombre.resize(ancho1,' ');
    if (buscar(base_datos, nombre)<0){
        cout<<endl<<"Ingrese valor a agregar: ";
        getline(cin,valor);
        valor.resize(ancho2,' ');
        cout<<endl<<"estado error: "<<base_datos.fail();
        base_datos.clear();
        base_datos.seekp(0,ios_base::end);
        base_datos<<endl<<nombre<<valor<<"|";
        base_datos.flush();
    }else{
        cout<<endl<<"ya existe el nombre";
    }
}
    
long int buscar(fstream &base_datos, string nombre){
    string linea;
    base_datos.seekg(0);
    while (getline(base_datos,linea)) {
        if (linea.find(nombre)!= string::npos){
            long int posicion=base_datos.tellp();
            return posicion-ancho1-ancho2-2;
        }
    }
    return -1;
}

void modificar(fstream &base_datos, long int posicion){
    string linea;
    string nombre;
    string valor;
    base_datos.clear();
    base_datos.seekp(posicion);
    cout<<endl<<base_datos.tellp();
    getline(base_datos,linea);
    nombre=linea.substr(0,ancho1);
    cout<<endl<<"Ingrese nuevo valor para  "<<nombre<<": ";
    getline(cin,valor);
    valor.resize(ancho2,' ');
    linea.replace(ancho1,ancho2,valor);
    linea.append("\n");
    cout<<endl<<linea;
    base_datos.seekp(posicion);
    base_datos.write(linea.c_str(), linea.length());
    base_datos.flush();
}

13. Acceso Aleatorio.

Hasta ahora , hemos visto cómo trabajar con archivos secuenciales, crearlos, escribir , leer y buscar.
Primero diremos que se conoce con el nombre de “record” o “registro” un conjunto de datos que se encuentran en una línea , si hacemos una analogía con una planilla de cálculo sería algo así como una fila, y se llama “campo” o “field”, a cada elemento del arreglo, siguiendo la analogía con la planilla de cálculo, sería como una celda dentro de una fila. Aclarado esto, usaremos estos terminos en esta sección.


Los archivos secuenciales son inapropiados para algunos tipos de aplicaciones, primero por que para acceder a un campo hay que recorrer TODOS los campos y segundo por que para insertar algún dato corremos el riesgo de  sobre escribir algún otro.

Veamos esto con algún ejemplo.
Supongamos que tenemos un archivo del tipo secuencial que contiene los siguiente regis-
tros o records, donde se quiere guardar los siguientes datos.

  • 1 Juan 424555
  • 2 Jorge 423555
  • 3 Pedro 402555
  • 4 Anibal 421555

Esto como es un archivo secuencial en realidad se encuentra dispuesto fisicamente de la
siguente manera:
1 Juan 424555 2 Jorge 423555 3 Pedro 402555 4 Anibal 421555
Bien imaginemos que queremos editar este archivo y modificar el campo teléfono del se-
gundo registro :423555 (teléfono de Jorge), el nuevo dato que nos da Jorge que es un número
de teléfono celular: 15123456. El programa , busca la ocurrencia de ese campo en el archivo,
busca en TODOS los campos!! , cuando encuentra esa cadena de caracteres escribe el nuevo
dato, esto es lo que quedaría de la siguiente manera:
1 Juan 424555 2 Jorge 15123456 Pedro 402555 4 Anibal 421555


Observaciones:

1. Podemos ver que se sobreescribió cierta información que es lo mismo que decir que se
perdió.
2. Otro punto a tener en cuenta es que por ejemplo el nro. de orden de esta simple agenda
que está representado con un dígito , es un sencillo número del tipo int , que por ejemplo
generalmente usa 4 bytes, pero cuando superemos los nueve registros, el nro. será de dos
dígitos , pero aún así seguirá siendo un entero de 4 bytes


Podemos ver con punto 2 de la observación ,que el hecho de que trabajemos con caracteres en un archivo (entiendase con archivos de texto ), no ayuda a resolver nuestros problemas.
Bueno el caso expuesto es un caso sencillo, y expone dos características importantes, una sobre escribir en los archivos de acceso secuencial y otra en particular sobre los archivos de texto de acceso aleatorio.
Dado que C++, no tiene definido nada para manejar el formato de los registros o records y en particular para los campos, es el programa el que tiene que realizar estos controles.
Algo que si nos ayuda de C++, es que dispone de algunas funciones que permiten trabajar sobre una cantidad determinadad de bytes, esto implicitamente quiere decir que al leer no importa el tipo de dato ( char ,1 byte, int 4 bytes) , lo que definitivamente quiere decir que los archivos serán tratados como binarios!!.

Esta condición nos ayudaría con el punto 2 de la observación realizada.

13.1. Lectura, Escritura Aleatoria

Introducción a Métodos de lectura y escritura en Archivos de acceso aleatorio. Antes de avanzar con las funciones que permiten escribir y leer vamos a recordar algo de
punteros.

Puntero a Char del tipo constante y puntero a char

Los punteros son variables que guardan una dirección de memoria.

Los punteros son variables especiales por no tener sólo un valor (dirección de memoria), pero también tienen  asociado el valor a que apuntan: tipo de la variable.
Como cualquier variable su valor puede ser constante o no.
La palabra clave const declara que un valor no es modificable por el programa.

No puede hacerse ninguna asignación posterior a un identificador especificado como const
El tipo “const char” es una constante y el valor no puede ser cambiado por lo tanto SIEMPRE debe tener un valor al inicializar, ya que luego no se puede modificar, mientras que una variable , si puede ser cambiado, veamos unos ejemplos:

const char X = ’A’; //X es un Valor constante...no puede ser modificado
char Y = ’B’; // Y es Variable, si puede ser modificado.
Y=’C’; //ok.
X=’D’;// El compilador acusaría error:assignment of read-only varaible ’X’
const int peso = 45; // ok, una constante int
const int altura ;//Error , no se inicializa al declarar la constante
const float talla = 40.2; // ok, una constante float
const float talla; // Error, no se inicializa al declarar la constante

De la misma manera que a las variables, a los punteros se les puede anteponer la palabra const, veamos como sería:


char * const pt1="Sol"; //Puntero Constante a string

char  const * pt2="Luna"; //Puntero a cadena Constante


Puntero a un caracter constante.

Este tipo de definición se usa para definir tipicamente cadenas de caracteres. Con un puntero a constante caracter puedo hacer que apunte a varios elementos del arreglo o strings, pero no puede modificar los strings, ya que son constantes!.


En la función *prt , no puede modificar el caracter al cual apunta.. pero puede apuntar a varios.. es mas a cada uno de los caracteres del string!!.
Un puntero no constante a los datos constantes es un puntero que puede ser modificado para apuntar a cualquier elemento de datos del tipo adecuado, pero los datos a las que apunta no se puede modificar a través de ese puntero.





13.2. Métodos Read and Write

Método Write y read.

Write:

El método write (pertence a ofstream) ,espera recibir un const char * (puntero a una arreglo constante) y en número de n caracteres a insertar.

El puntero a un arreglo constante puede modificarse.. pero NO a lo que apunta.

En este caso entiendase que con caracteres se está pensando en que un caracter es un byte, por lo que podemos pensar que escribe una cantidad de bytes.

En ese contexto el tipo de dato que escribo es irrelevante, ya que escribo de a bytes!.

Read:

El método read ( pertence a ifstream) realiza la operación de lectura y espera recibir un char * (puntero a un arreglo) y un número n de bytes y vuelca el contenido de n bytes al puntero pasado como argumento.


En un archivo de texto, fácilmente podemos usar <<, para sacar una variable, pero recordemos que un entero puede tener 1, o mas dígitos, pero desde el punto de vista de bytes, serán normalmente 4 bytes.
Veamos un ejemplo de uso de read y write que el contenido de un archivo y lo copia a otro, en este caso solo los toma como bytes.


Ahora, vamos a mostrar dos archivos que escriben lo mismo: 123456789 en un archivo, pero en un caso vamos a ingresar el número como “ENTERO” y en otro caso como un “ARREGLO DE CHAR” y veremos como cambia el tamaño de los datos a guardar.

ARREGLO DE CHAR


La salida que resulta de ejecutar este programa, será:

Observaciones:

  • El archivo tiene un total de 10bytes.
  • El archivo es del tipo binario.

ENTERO

Veamos ahora como sería el caso de guardar el mismo nro. pero como entero


La salida que resulta de ejecutar este programa, será:


Observaciones:

El archivo tiene un total de 4 bytes. Seis bytes menos que el anterior y estamos guardando en
mismo nro., esto es un 60 % menos de capacitad para guardar el mismo dato.
El archivo es del tipo binario.

Hay un par de cosas que tenemos que aclarar, sobre el primer argumento del método write y read.
El método write espera recibir un puntero a un arreglo char constante  y un puntero a un arreglo de char para el caso de read, por lo que debemos acondicionar lo que tenemos como arguemento.


Vemos que en el primer caso el nombre del arreglo ya es un puntero a un arreglo del tipo char, en este caso write y read no necesitan mayores modificaciones del argumento.
Para el segundo caso, como el nro. que se tiene es un entero, para el caso de :

write: debemos pasarle un puntero a un arreglo constante, pero tenemos un entero, por lo que obtenemos la direción del entero y le hacemos un casting explicito a const char.


out.write ((const char*)&x, sizeof (int));

read: debemos pasar un puntero a un arreglo de char , hacemos entonces un cast explicito.

in.read ((char*)&x, sizeof (int));



13.3. Método Read Wrire (Continuación)

Mostraremos ahora dos programas que hacen lo mismo escribir, 10000,20000,30000,40000,50000, en un archivo y luego compararemos nuevamente el tamaño. No aportará nada nuevo pero podremos repasar la forma de uso de read y write para el caso de char e int.

Enteros



La salida de este programa sería:

Observaciones:
  • 20 Bytes!!
  • Se recomienda al lector comentar la línea out.close() y ver la salida del programa

ARREGLO DE CHAR

Ahora para el caso de hacer lo mismo pero con Arreglo de Char.


La salida de este programa sería:


Observaciones.

  • 30 Bytes!!


13.4. Método Read Write con Escructuras

Bueno, ahora que vimos las manera de escribir y leer de un archivo de acceso aleatorio y del tipo binario, veamos como darle alguna estructura al mismo usando Estructuras.

Comencemos por algo sencillo.

  • Ahora escribiremos una estructura mas real, es decir que no sea solo una esctructura con un float, esto sería mas parecido a una Estructura un poco mas compleja.

Veamos como quedaría el código:

Veamos como sería la salida cuando corremos este programa:


Observaciones.

Ver que si bien se usa una estructura, es solo para definir el formato y tipo de datos a cargar, en si, las instancias de la estructura no se utilizan luego, por lo que se podría haber creado una sola instancia e ir volcando el contenido de esta a el archivo.
Se deja al lector ver que sucede si el nombre supera a 9 caracteres.

13.5. Argumentos a main.

Argumentos a main

Recodemos que main es una función y por lo tanto puede recibir argumentos.
De hecho cuando Uds. abren con el Zinjai un archivo nuevo aparece:

Podemos ver que seguido de main hay valores que hasta el momento no habíamos prestado atención. Vamos ver un como cada uno de ellos.

  • argc: cantidad de argumentos pasados al momento de ejecutar el programa, es un entero.
  • *arg[]: es un puntero a un arreglo que contiene como elementos los argumentos, cada uno de los elementos es un chat.

Veamos un ejemplo este archivo se llama argumentos_a_main :

Esto puede ser usado por ejemplo para pasar un nombre de archivo.


14. Ejemplo manejo de archivos

#include  <iostream>
#include  <fstream>
using  namespace  std;

int  main(){
    cout <<"Este programa permite crea un archivo con lecturas  de"<<endl;
    cout <<"temperatura y humedad para distintos días de la semana"<<endl;
    cout <<"Todo se guarda en un archivo prueba.txt"<<endl;
    cout <<"ctrl + D finaliza la carga de datos."<<endl;
    int i=0; char  dia[11]; float temp ,humedad;                      // Variables usadas parapasar datos al flujo
    
    ofstream flujo_salida("prueba.txt", ios::out | ios::ate );          // Añade al final
                                                                        // El objeto flujo es abierto implicitamente.
    if(!flujo_salida){                                                  // Controlo que se pueda crear el flujo y asociar alarchivo
        cout<<"Error no se puede abrir archivo. Finalizando";
        return -1;
    }
    cout<<endl<<"Flujo de salida abierto correctamente"<<endl;
    
    cout<<"Ingrese: Dia y luego Temperatura y Humedad dejando un espacio entrecada campo"<<endl;
    while(cin>>dia>>temp>>humedad){
        flujo_salida<<i<<" "<<dia <<" "<<temp <<" "<<humedad <<endl;
        i++;
    }

    flujo_salida.close();
    if(!flujo_salida.is_open ()){
        cout <<"Flujo de Salida: cerrado"<<endl;
    }
    
    ifstream flujo_entrada("prueba.txt");                               //ios::in  implicito !!
    if(!flujo_entrada){                                                // Controlo  que se  pueda  crear  el  flujo y asociar  alarchivo
        cout<<"Error no se puede abrir archivo. Finalizando";
        return -1;
    }
    cout<<endl<<"Flujo de entrada abierto correctamente"<<endl;
    
    cout<<"Nro."<<'\t'<<"Dia"<<'\t'<<"Temp."<<'\t'<<"Humedad"<<endl;
    while (flujo_entrada>>i>>dia>>temp>>humedad){
        cout<<i<<'\t'<<dia <<'\t'<<temp <<'\t'<<humedad<<endl;
    }
    cout<<endl;

    flujo_entrada.close();
    if(!flujo_entrada.is_open ()){
        cout<<"Flujo de Entrada:cerrado"<<endl;
    }
    cout<<"Finalizando el programa!!";
    
    return 0;
}

15. Creación de archivo svg

/*
 * genera una archivo svg con la espiral de los primeros 1000 números primos
 * se representa cada número en coordenadas polares tal que (r,θ)=(p,p)
 * se utiliza como plantilla un archivo svg plano creado con inkscape
 * https://www.di-mgt.com.au/primes1000.txt
 * https://www.youtube.com/watch?v=EK32jo7i5LQ
 * 
 * <Germán Andrés Xander 2022>
 */


#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;

int main(int argc, char **argv){
    ifstream primos ("primos1000.txt");   //abro el archivo con los primeros 1000 primos
    ofstream salida ("espiral.svg", ofstream::out | ofstream::trunc);      // creo objeto salida 

    if (primos.is_open()){
        salida << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"<<endl<<endl;
        salida<< "<!-- Creado por Germán Andrés Xander 2022 con C++ -->"<<endl;
        salida<< "<svg"<<endl;
        salida<< "   width=\"16000\""<<endl;
        salida<< "   height=\"16000\"";
        salida<< "   viewBox=\"0 0 16000 16000\""<<endl;
        salida<< "   version=\"1.1\""<<endl;
        salida<< "   id=\"svg5\""<<endl;
        salida<< "   xmlns=\"http://www.w3.org/2000/svg\""<<endl;
        salida<< "   xmlns:svg=\"http://www.w3.org/2000/svg\">"<<endl;
        salida<< "  <defs"<<endl;
        salida<< "     id=\"defs2\" />"<<endl;
        salida<< "  <text"<<endl;
        salida<< "     xml:space=\"preserve\""<<endl;
        salida<< "     style=\"font-size:276.602px;line-height:1.25;font-family:sans-serif;text-align:start;text-anchor:start;stroke-width:6.91506\""<<endl;
        salida<< "     x=\"173.1696\""<<endl;
        salida<< "     y=\"411.87714\""<<endl;
        salida<< "     id=\"text8850\"><tspan"<<endl;
        salida<< "       id=\"tspan8848\""<<endl;
        salida<< "       x=\"173.1696\""<<endl;
        salida<< "       y=\"411.87714\""<<endl;
        salida<< "       style=\"text-align:start;text-anchor:start;stroke-width:6.91506\">Espiral de los primeros 1000 números primos</tspan></text>"<<endl;
        
        string linea;
        int i=1, primo, x, y;
        while (getline(primos,linea)) {
            primo=stoi(linea);
            x = primo * cos(primo);
            y = primo * sin(primo);
            salida<<endl<< "  <circle"<<endl;
            salida<< "    id=\"path"<<i<<"\""<<endl;
            salida<< "    style=\"fill:#000000;stroke:none;stroke-width:0\""<<endl;
            salida<< "    cx=\""<<x+8000<<"\""<<endl;
            salida<< "    cy=\""<<y+8000<<"\""<<endl;
            salida<< "    r=\"50\" />"<<endl;
        }
        salida<< "</svg>";

        primos.close();
        salida.close();
    }
    else cout << "no se puede abrir el archivo"; 
    return 0;
}

16. Creación de archivo dxf

/*
 * generación de un archivo dxf (autocad).
 * Se busca acomodar la mayor cantidad de agujeros dentro de un círculo
 * simulando las semillas de un girasol. Se utiliza el número áureo
 * 
 * <germán andrés xander 2021>
 */

#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;

int main(void){
    ofstream archivo("agujeros.dxf");
    long double PI = 3.14159;
    int agujero = 1000;
    long double radioAgu = 5.0;
    long double GoldenRatio =(1.0 + sqrt(5.0)) / 2.0;
    long double deltaAng = PI / GoldenRatio;
    long double r;
    archivo<<"0\nSECTION\n2\nENTITIES";
    int horario;
    for(horario = -1;horario <2; horario+=2){
        for(int ii = 0;ii <= agujero;ii ++){
            long double angulo = double(ii) * deltaAng;
            r = radioAgu*sqrt(double(ii)* PI);
            if(r>150){
                long double x = horario * r * cos(angulo);
                long double y = horario * r * sin(angulo);
                archivo<<"\n0\nCIRCLE\n8\nSunflower";
                archivo<<"\n10\n"<< x;
                archivo<<"\n20\n"<< y;
                archivo<<"\n40\n"<< radioAgu;
                archivo<<"\n0\nTEXT";
                archivo<<"\n1\n"<< ii;
                archivo<<"\n11\n"<< x;
                archivo<<"\n21\n"<< y;
                archivo<<"\n40\n"<< 3;
                archivo<<"\n72\n"<< 1;
                archivo<<"\n73\n"<< 2;
            }
        }
    }
    archivo<<"\n0\nCIRCLE\n8\nSunflower";
    archivo<<"\n10\n"<< 0;
    archivo<<"\n20\n"<< 0;
    archivo<<"\n40\n"<< r +5;
    archivo<<"\n0\nENDSEC\n0\nEOF\n";
    archivo.close();
    cout<<"\nFinished\n\n";
    return 0;
}

https://aulavirtual.fio.unam.edu.ar/mod/resource/view.php?id=63088

16.1. Peletizadora


17. Ala NACA


17.1. resultado


18. Incluir código propio

Código propio

La idea es incorporar código que hayan hecho otros usuarios poniendo el mismo en un  archivo. Ejemplo programador A quiere usar lo que el programador B hizo, lo que debe hacer es utilizar la directiva de pre-procesador del compilador #include "archivo.h", veamos un ejemplo.

Para el caso del programa principal sería:


Si el archivo suma.h que está en el mismo directorio, se vería:


Como vemos esto mas que una librería es una manera de usar el código ubicado en otro archivo.

Suele o puede ocurrir un error en otra situación. Supongamos que el Programador A incluye el código del Programador B y del programador C, pero resulta que el programador B, también usaba el código del programador C, y para ello ya había incluido el código de C en B.

Si esto sucede al tratar de compilar usando el #include "archivo.h" va a indicar un cartel con el error de "doble inclusión", esto es por que el mismo código se incluye dos veces. 

Para evitar esto se recurre a una directiva condicional del pre-procesador del compilador.

Directiva include guard

Para presentar el ejemplo vamos a suponer que el código que se intenta incluir dos veces es una estructura definida en dos lugares.

//Programa C guardado como codigoc.h



//Programa B guardado como codigob.h



//Programa A guardado como codigoa.cpp


Si se compila codigoa.cpp el error que va a tirar es:


Esto sucede por que funcion1 esta declarada en C e incluida dos veces en "codigoa.cpp" una con #include "codigoc.h" y de nuevo de manera indirecta  con #include "codigob.h", digo indirectamente por que en "codigob.h" YA había incluid a funcion1() con el #include "codigoc.h"

Veamos como solucionarlo, SOLO en el archivo "codigoc.h" escribimos:


Las líneas 2,3 y 10 crean lo que se conoce como include guard.

En los lenguajes de programación C y C++, un #include guard, a veces llamado macro guard, header guard o file guard, es una construcción particular que se utiliza para evitar el problema de la doble inclusión cuando se trata de la directiva include. El nombre de include guard no es algo oficial y algunos programadores pueden darle otro nombre.