- Métodos de almacenamiento de la información: texto y binario
- Escritura y lectura de archivos en modo simplificado
- Abrir y cerrar archivos
- Gestión de descriptores de archivo
- Seleccionar una codificación para el modo texto
- Escritura y lectura de arrays
- Escritura y lectura de estructuras (archivos binarios)
- Escritura y lectura de variables (archivos binarios)
- Escritura y lectura de variables (archivos de texto)
- Gestión de la posición en un expediente
- Obtención de las propiedades de un archivo
- Forzar escritura de caché en disco
- Eliminación de un archivo y comprobación de su existencia
- Copia y desplazamiento de archivos
- Búsqueda de archivos y carpetas
- Trabajar con carpetas
- Cuadro de diálogo de selección de archivos o carpetas
Escritura y lectura de estructuras (archivos binarios)
En la sección anterior aprendimos a realizar operaciones de E/S en arrays de estructuras. Cuando la lectura o escritura está relacionada con una estructura independiente, es más conveniente utilizar el par de funciones FileWriteStruct y FileReadStruct.
uint FileWriteStruct(int handle, const void &data, int size = -1)
La función escribe el contenido de una estructura simple data en un archivo binario con el descriptor handle. Como sabemos, estas estructuras sólo pueden contener campos de tipos de no cadena integrados y estructuras simples anidadas.
La característica principal de la función es el parámetro size, que ayuda a establecer el número de bytes que se van a escribir, lo que nos permite descartar alguna parte de la estructura (su final). Por defecto, el parámetro es -1, lo que significa que se guarda toda la estructura. Si size es mayor que el tamaño de la estructura, se ignora el exceso, es decir, sólo se escribe la estructura, sizeof(data) bytes.
En caso de éxito, la función devuelve el número de bytes escritos; en caso de error devuelve 0.
uint FileReadStruct(int handle, void &data, int size = -1)
La función lee el contenido de un archivo binario con el descriptor handle en la estructura data. El parámetro size especifica el número de bytes que se van a leer. Si no se especifica el tamaño de la estructura o éste se supera, se utiliza el tamaño exacto de la estructura especificada.
En caso de éxito, la función devuelve el número de bytes leídos; en caso de error devuelve 0.
La opción de cortar el final de la estructura sólo está presente en las funciones FileWriteStruct y FileReadStruct. Por lo tanto, su uso en un bucle se convierte en la alternativa más adecuada para guardar y leer un array de estructuras recortadas: las funciones FileWriteArray y FileReadArray no tienen esta capacidad, y la escritura y lectura por campos individuales puede consumir más recursos (veremos las funciones correspondientes en los siguientes apartados).
Hay que tener en cuenta que, para poder utilizar esta función, debe diseñar sus estructuras de forma que todos los campos de cálculo temporales e intermedios que no deban guardarse se encuentren al final de la estructura.
Veamos ejemplos de uso de estas dos funciones en el script FileStruct.mq5.
Supongamos que queremos archivar las últimas cotizaciones de vez en cuando, para poder comprobar su invariabilidad en el futuro o comparar con periodos similares de otros proveedores. Básicamente, esto se puede hacer manualmente a través del cuadro de diálogo Símbolos (en la pestaña Barras) en MetaTrader 5. Pero esto requeriría un esfuerzo adicional y cumplir un calendario. Es mucho más fácil hacerlo automáticamente, desde el programa. Además, la exportación manual de las cotizaciones se realiza en formato de texto CSV, y es posible que tengamos que enviar los archivos a un servidor externo. Por lo tanto, es deseable guardarlos en una forma binaria compacta. Además, supongamos que no nos interesa la información sobre ticks, diferencial y volúmenes reales (que siempre están vacíos para los símbolos Forex).
En la sección Comparar, ordenar y buscar en arrays, consideramos la estructura MqlRates y la función CopyRates. Se describirán detalladamente más adelante, mientras que ahora las utilizaremos una vez más como campo de pruebas para las operaciones con archivos.
Utilizando el parámetro size en FileWriteStruct podemos guardar sólo una parte de la estructura MqlRates, sin los últimos campos.
Al principio del script definimos las macros y el nombre del archivo de prueba.
#define BARLIMIT 10 // number of bars to write
|
De particular interés es la constante HEADSIZE. Como se ha mencionado anteriormente, las funciones de archivo como tales no son responsables de la coherencia de los datos en el archivo, y de los tipos de estructuras en las que se leen estos datos. El programador debe proporcionar dicho control en su código. Por lo tanto, al principio del archivo se suele escribir un determinado encabezado, con ayuda del cual puede, en primer lugar, asegurarse de que se trata de un archivo con el formato requerido y, en segundo lugar, guardar en él la metainformación necesaria para su correcta lectura.
En concreto, el título puede indicar el número de entradas. En sentido estricto, esto último no siempre es necesario, ya que podemos leer el archivo gradualmente hasta que termine. No obstante, es más eficiente asignar memoria para todos los registros esperados a la vez, basándose en el contador del encabezado.
Para nuestros fines, hemos desarrollado una estructura sencilla FileHeader.
struct FileHeader
|
Comienza con la firma de texto «CANDLES» (en el campo signature), el número de versión «1.0» (en el mismo lugar) y el número de entradas (en el campo n). Como no podemos utilizar un campo de cadena para la firma (entonces la estructura dejaría de ser simple y cumpliría los requisitos de las funciones de archivo), el texto se empaqueta en realidad en el array uchar del tamaño fijo HEADSIZE. Su inicialización en la instancia la realiza el constructor basándose en la copia estática local.
En la función OnStart solicitamos el BARLIMIT de las últimas barras, abrimos el archivo en modo FILE_WRITE y escribimos en el archivo el encabezado seguido de las cotizaciones resultantes de forma truncada.
void OnStart()
|
Como valor del parámetro size en la función FileWriteStruct utilizamos una expresión con el ya conocido operador offsetof: offsetof(MqlRates, tick_volume) es decir, todos los campos que empiezan por tick_volume se descartan al escribir en el archivo.
Para probar la lectura de datos, vamos a abrir el mismo archivo en modo FILE_READ y a leer la estructura FileHeader.
handle = PRTF(FileOpen(filename, FILE_BIN | FILE_READ)); // 1 / ok
|
La estructura reference contiene el encabezado por defecto sin cambios (firma). La estructura reader obtuvo 14 bytes del archivo. Si las dos firmas coinciden, podemos seguir trabajando, ya que el formato del archivo ha resultado ser correcto y el campo reader.n contiene el número de entradas leídas del archivo. Asignamos y ponemos a cero el tamaño de memoria necesario para el array receptor candles y, a continuación, leemos en él todas las entradas.
PrintFormat("Reading %d candles...", reader.n);
|
La puesta a cero era necesaria porque las estructuras MqlRates se leen parcialmente, y los campos restantes contendrían basura sin la puesta a cero.
Este es el registro que muestra los datos iniciales (en conjunto) para XAUUSD,H1:
[hora] [apertura] [máximo] [mínimo] [cierre] [volumen_tic] [diferencial] [volumen_real] [0] 2021.08.16 03:00:00 1778.86 1780.58 1778.12 1780.56 3049 5 0 [1] 2021.08.16 04:00:00 1780.61 1782.58 1777.10 1777.13 4633 5 0 [2] 2021.08.16 05:00:00 1777.13 1780.25 1776.99 1779.21 3592 5 0 [3] 2021.08.16 06:00:00 1779.26 1779.26 1776.67 1776.79 2535 5 0 [4] 2021.08.16 07:00:00 1776.79 1777.59 1775.50 1777.05 2052 6 0 [5] 2021.08.16 08:00:00 1777.03 1777.19 1772.93 1774.35 3213 5 0 [6] 2021.08.16 09:00:00 1774.38 1775.41 1771.84 1773.33 4527 5 0 [7] 2021.08.16 10:00:00 1773.26 1777.42 1772.84 1774.57 4514 5 0 [8] 2021.08.16 11:00:00 1774.61 1776.67 1773.69 1775.95 3500 5 0 [9] 2021.08.16 12:00:00 1775.96 1776.12 1773.68 1774.44 2425 5 0 |
Veamos ahora qué se ha leído.
[hora] [apertura] [máximo] [mínimo] [cierre] [volumen_tic] [diferencial] [volumen_real] [0] 2021.08.16 03:00:00 1778.86 1780.58 1778.12 1780.56 0 0 0 [1] 2021.08.16 04:00:00 1780.61 1782.58 1777.10 1777.13 0 0 0 [2] 2021.08.16 05:00:00 1777.13 1780.25 1776.99 1779.21 0 0 0 [3] 2021.08.16 06:00:00 1779.26 1779.26 1776.67 1776.79 0 0 0 [4] 2021.08.16 07:00:00 1776.79 1777.59 1775.50 1777.05 0 0 0 [5] 2021.08.16 08:00:00 1777.03 1777.19 1772.93 1774.35 0 0 0 [6] 2021.08.16 09:00:00 1774.38 1775.41 1771.84 1773.33 0 0 0 [7] 2021.08.16 10:00:00 1773.26 1777.42 1772.84 1774.57 0 0 0 [8] 2021.08.16 11:00:00 1774.61 1776.67 1773.69 1775.95 0 0 0 [9] 2021.08.16 12:00:00 1775.96 1776.12 1773.68 1774.44 0 0 0 |
Las cotizaciones coinciden, pero los tres últimos campos de cada estructura están vacíos.
Puede abrir la carpeta MQL5/Files/MQL5Book y examinar la representación interna del archivo struct.raw (utilice un visor que admita el modo binario; a continuación se muestra un ejemplo).
Opciones para presentar un archivo binario con cotizaciones en un visor externo
Esta es una forma típica de visualizar archivos binarios: la columna de la izquierda muestra las direcciones (desplazamientos desde el principio del archivo), los códigos de bytes están en la columna central y las representaciones simbólicas de los bytes correspondientes se muestran en la columna de la derecha. La primera y la segunda columna utilizan la notación hexadecimal para los números. Los caracteres de la columna derecha pueden variar en función de la página de códigos ANSI seleccionada. Tiene sentido prestarles atención sólo en aquellos fragmentos en los que se conoce la presencia de texto. En nuestro caso, la firma «CANDLES1.0» se «manifiesta» claramente al principio. Los números deben ser analizados por la columna central. En esta columna, por ejemplo, después de la firma, se puede ver el valor de 4 bytes 0x0A000000, es decir, 0x0000000A de forma invertida (recuerde la sección Control de la codificación endian de números enteros): es 10, el número de estructuras escritas.