Del básico al intermedio: FileSave y FileLoad
Introducción
En el artículo anterior Del básico al intermedio: SandBox y MetaTrader, se explicó, de la forma más simple y práctica posible, qué es una SandBox y cómo influye en nuestra forma de trabajar en MetaTrader 5 cuando nos centramos en trabajar con las funciones y los procedimientos de la biblioteca de MQL5 para crear, manipular y leer archivos en general.
Aquel contenido solo pretendía explicar qué es una SandBox, no presentar las mejores formas de trabajar con archivos. Esto se debe a que existen formas mucho mejores de hacerlo usando MQL5 puro, sin necesidad de utilizar ninguna función del sistema operativo.
Bien, suponiendo que tú, mi querido lector, hayas logrado comprender qué es una SandBox y cómo afecta nuestra forma de trabajar, podemos empezar a pensar en trabajar con archivos para obtener otro tipo de resultados. Recuerda que aquí no nos centraremos en crear ninguna aplicación en particular. Solo utilizaremos algunas de las funciones y los procedimientos de la biblioteca de MQL5 para mostrar cómo pueden implementarse ciertas actividades. De todos modos, es necesario que te dediques a practicar lo que verás aquí para entender algunos detalles sobre los que, quizá, te queden dudas. Entonces, como ya es costumbre, vamos a pasar a un nuevo tema para empezar a entender algunas cuestiones.
No existe una fórmula única
En el artículo anterior, hicimos una implementación de lectura y escritura con un objetivo bastante simple, y en aquel momento podía dejarse tal como estaba. Pero, en la práctica, a la hora de la verdad, las cosas no siempre se implementan como se mostró allí. Esto se debe a un problema que puede llegar a producirse. Para refrescarte la memoria, veamos de nuevo qué problema es este que acabamos de mencionar. Para ello, vamos a usar uno de los códigos que vimos en el artículo anterior. Puede verse a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. const string szText = "This file was created by a script written in MQL5."; 07. uchar info[]; 08. 09. StringToCharArray(szText, info); 10. 11. FileSave("Hello.txt", info); 12. } 13. //+------------------------------------------------------------------+
Código 01
Cuando se ejecute este código 01, generará un archivo cuyo contenido se ve en la siguiente imagen.

Imagen 01
En esta imagen 01, señalo un símbolo extraño que, en este caso, es un carácter nulo. Este se usa para definir el límite de una string y, dentro del contexto de la aplicación que utilice este tipo de representación de strings, no representa realmente un problema. Pero, en un archivo cuyo objetivo sea contener texto puro, la presencia de ese carácter no tiene ningún sentido y, muchas veces, constituye un problema, según el objetivo o el destino del archivo de texto.
Existen diversas formas de resolver este tipo de inconveniente. Algunas son más directas, mientras que otras son indirectas y requieren algunos pasos adicionales para implementarse correctamente. Pues bien, a pesar de esto, hay otra cuestión relacionada con este código 01. Aunque la línea once es capaz de crear un archivo y guardar datos directamente en él, de una forma muy simple y directa, esta manera de resolver las cosas nos plantea otro problema, que abordaremos con más profundidad en breve: el acceso aleatorio a los datos del archivo.
Aunque pueda parecer extraño decirlo, en la práctica los archivos en disco no se comportan como sistemas de lectura secuencial, como ocurre, por ejemplo, con los archivos en unidades de cinta. Allí, cuando necesitamos leer un archivo o algún dato, tenemos que leer una buena parte de la cinta magnética, lo que hace que el proceso de lectura y escritura sea bastante complicado en algunos momentos. Sin embargo, cuando esto se hace con archivos almacenados en disco, podemos tener tanto acceso secuencial como acceso aleatorio, donde podemos leer o escribir cualquier fragmento del archivo en cualquier momento.
Sin embargo, aunque esto pueda hacerse, FileSave y FileLoad no son funciones adecuadas para el acceso aleatorio, ya que leen o graban un bloque completo del archivo y, normalmente, cargan o guardan en disco el archivo entero. En archivos pequeños esto no representa ningún problema y, si el contenido del archivo puede ignorarse para sobrescribirlo, podemos usar FileSave y FileLoad sin ningún problema en nuestras aplicaciones, ya que son una forma muy práctica y segura de leer y grabar archivos.
Sin embargo, si la idea es modificar el archivo manteniendo intacta parte de su contenido, usar FileSave y FileLoad quizá no sea la mejor solución, ya que necesitamos mantener todo el archivo en memoria. Eso puede ser bastante innecesario, ya que muchas veces solo necesitamos mantener en memoria un pequeño fragmento en el que estamos trabajando. El resto puede quedarse en disco y cargarse cuando sea necesario.
Por esta razón, no existe una fórmula única ni una forma general y definitiva de hacer las cosas. Cada caso es distinto, y la solución que se adopte debe pensarse en función del criterio que deba cumplirse. Para que el uso del propio MetaTrader 5 resulte agradable y práctico para el usuario de tus aplicaciones.
Para que lo que acabamos de decir sea un poco más simple de entender y, al mismo tiempo, más clara, vamos a crear algunos ejemplos sencillos para que tú, mi querido lector, puedas entender adónde queremos llegar cuando decimos que cada implementación debe pensarse de manera individual. porque muchos quizá no tengan una idea clara de cuánto ciertas decisiones pueden e irán a influir en la forma en que una aplicación será y deberá implementarse.
Vamos a empezar intentando agregar nueva información a un archivo que ya esté guardado en disco. En este caso, el archivo que vamos a manipular será exactamente el mismo que crea el código 01. Pero ese mismo código 01 sufrirá algunos cambios para permitir esta manipulación del archivo. Recuerda: el objetivo es agregar nueva información al archivo ya guardado. Bien, en teoría, pensarías en hacer algo parecido a lo que se ve a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. SaveInfo("This file was created by a script written in MQL5."); 07. SaveInfo("New information is being added to the file."); 08. } 09. //+------------------------------------------------------------------+ 10. void SaveInfo(const string szArg) 11. { 12. uchar info[]; 13. 14. StringToCharArray(szArg, info); 15. FileSave("Hello.txt", info); 16. } 17. //+------------------------------------------------------------------+
Código 02
Al ejecutar este código 02, esperas que el archivo Hello.txt contenga lo que se ve en la imagen 01 y también nueva información. Bien, los datos que deberían estar en el archivo se ven en las líneas seis y siete de este código 02. Observa que se trata de algo muy simple de entender. Sin embargo, al ejecutar este script en MetaTrader 5, el resultado no es exactamente el esperado. Puede verse a continuación.

Imagen 02
Pues bien, ahora viene la gran pregunta, junto con una duda igualmente crucial. ¿Qué ocurrió aquí para que el resultado fuera este que podemos ver en la imagen 02? Porque, en principio, ese no sería el resultado esperado, ya que tanto el contenido de la línea seis como el de la línea siete deberían aparecer en el contenido final del archivo. Sin embargo, solo se muestra el mensaje de la línea siete.
O, visto de otra manera, por algún motivo se ignora el contenido de la línea seis y solo se usa el de la línea siete. Entonces, ¿qué ocurre con el contenido de la línea seis? Este es uno de los problemas más complicados de entender sin la ayuda de alguien que realmente te explique qué ocurre aquí. Pero, en la práctica, es algo bastante simple. Cuando ejecutamos la línea seis, la función de la línea quince graba los datos en el archivo. Justo después, cuando ejecutamos la línea siete, ese mismo archivo se sobrescribe y se graba en él un contenido nuevo. Aunque esto es simple de entender, sin que alguien te lo explique resulta difícil encontrar un motivo por el que el código no grabe ciertos datos, lo que da la impresión de que nuestro código tiene un error grave, cuando en realidad el problema es otro.
Bien, pero ¿cómo podríamos resolver este problema? Es decir, ¿cómo podríamos almacenar ambos contenidos, tanto el de la línea seis como el de la línea siete, en el mismo archivo? Bien, mi querido lector, como dice el título de este tema: NO EXISTE UNA FÓRMULA ÚNICA. Existen diferentes propuestas para resolver esto, y cada una tiene ventajas y desventajas. Aquí mostraré solo una con fines de demostración. Pero tú puedes pensar en otras e intentar implementarlas también. Procura hacerlo tomando como base la función FileSave. Esto se debe a que existen otras soluciones mucho más simples, siempre que estén bien planteadas.
Veamos cuál sería una primera solución. Puede verse a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. SaveInfo("This file was created by a script written in MQL5."); 07. SaveInfo("New information is being added to the file."); 08. } 09. //+------------------------------------------------------------------+ 10. void SaveInfo(const string szArg) 11. { 12. const string FileName = "Hello.txt"; 13. uchar info[], tmp[]; 14. 15. FileLoad(FileName, info); 16. StringToCharArray(szArg + "\n", tmp); 17. ArrayCopy(info, tmp, info.Size(), 0, tmp.Size() - 1); 18. FileSave(FileName, info); 19. } 20. //+------------------------------------------------------------------+
Código 03
Al ejecutar este código 03, obtendrás un resultado muy parecido a lo que se ve en la siguiente imagen.

Imagen 03
Pues bien, ahora el resultado es exactamente el esperado. Es decir, tanto el contenido de la línea seis como el de la línea siete se agregan al archivo. Pero no solo eso. Puedes notar que ahora ya no se agrega aquel carácter NULL. ¿Cómo fue posible resolver dos problemas de una sola vez?
Además: si observas y piensas con un poco de calma, acabarás notando que este procedimiento SaveInfo, que aparece en la línea diez del código 03, sería prácticamente el código para crear archivos de log. Es decir, sin mucho trabajo ni coste, puedes crear un sistema para comprobar en un archivo lo que hace tu código. Así, podrás entender y estudiar con calma qué ocurre durante la ejecución. Interesante, ¿no? Algo simple de implementar y con resultados y posibilidades bastante interesantes.
Veamos ahora qué ocurrió aquí para que fuera posible corregir el código 02 y hacer que este código 03 fuera mucho más adecuado y, sobre todo, capaz de guardar tanto el contenido de la línea seis como el de la línea siete.
Para ello, puedes notar que añadí una constante. Esto permite usar una misma referencia para acceder al archivo, ya que en la línea 15 estaremos leyéndolo y en la línea 18 estaremos escribiéndolo. ¿Pero por qué hacer esto? Bien, mi querido lector, el motivo es que, como viste en el código 02, FileSave guardará el contenido del buffer en una sola pasada. Es decir, si el buffer, que en este caso sería nuestro array info, contiene una determinada información, esa información sobrescribirá la información original del archivo. Sin embargo, cuando leemos el archivo, cargamos su contenido en el buffer. Justo después, usando la línea 17, añadimos la nueva información a lo que ya existía en el buffer original del archivo. Es como si estuviéramos creando literalmente el archivo en memoria para solo después almacenarlo en disco.
Este tipo de operación, en el ejemplo que estamos utilizando, es muy rápida. Sin embargo, a medida que el archivo va creciendo, esto se convierte en un problema, ya que esta lectura y escritura, hechas de esta forma, hacen que tu aplicación sea menos eficiente en cuanto al uso de los recursos de la máquina. Y esto termina influyendo en el tiempo de ejecución, ya que, cuanto más datos haya en el archivo, mayor será el tiempo necesario para leer y grabar los datos en disco. Pero, para casos simples como este que estamos tratando aquí, este tipo de solución puede adoptarse.
Bien, pero ¿qué ocurre si volvemos a ejecutar la aplicación? A primera vista, funciona sin ningún problema. Pues bien, en este caso, las líneas seis y siete volverán a grabarse una y otra vez, añadiendo cada vez más líneas con la misma información. Esto seguirá así hasta que elimines el archivo original para que todo el proceso vuelva a iniciarse desde cero. Es decir, tenemos un nuevo trabajo por hacer. Esto puede hacerse, mediante código, básicamente de dos maneras diferentes. Aunque existan más formas, aquí nos centraremos solo en dos.
Primera forma de solución
La primera manera de resolver esto sería eliminar el archivo y empezar a reescribirlo a medida que la aplicación se ejecute. Para ello, tendremos que cambiar el código 03 por algo como lo que se ve a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_FILENAME "Hello.txt" 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. FileDelete(def_FILENAME); 09. SaveInfo("This file was created by a script written in MQL5."); 10. SaveInfo("New information is being added to the file."); 11. } 12. //+------------------------------------------------------------------+ 13. void SaveInfo(const string szArg) 14. { 15. uchar info[], tmp[]; 16. 17. FileLoad(def_FILENAME, info); 18. StringToCharArray(szArg + "\n", tmp); 19. ArrayCopy(info, tmp, info.Size(), 0, tmp.Size() - 1); 20. FileSave(def_FILENAME, info); 21. } 22. //+------------------------------------------------------------------+
Código 04
Muy bien, este código 04 resuelve, en parte, el problema que existía en el código 03. Sin embargo, es importante destacar que esta solución, aunque funciona, es un tanto peligrosa. Esto se debe a que, si tú, como programador, olvidas indicar la SandBox correcta, podrías acabar creando un gran problema. El motivo es que esta línea ocho, que se ve en el código 04, efectivamente eliminará el archivo indicado. Pero, si la lectura que se hace en la línea 17 y la escritura que se hace en la línea 20 no coinciden con esta línea ocho en lo que respecta a la SandBox utilizada, podríamos estar eliminando un archivo que no era exactamente el que pretendíamos.
Como este código 04 estará adjunto, podrás modificarlo para intentar entender qué tipo de problema puede surgir si usamos SandBox diferentes. Pero, para resolver este problema, puedes añadir una definición para sincronizar todos los aspectos de la implementación y evitar, así, los problemas que surgen al usar una SandBox distinta en momentos diferentes. Esto puede hacerse como se ve a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_FILENAME "Hello.txt" 05. #define def_FILE_COMMON //If set uses the shared folder 06. //+------------------------------------------------------------------+ 07. void OnStart(void) 08. { 09. #ifdef def_FILE_COMMON 10. FileDelete(def_FILENAME, FILE_COMMON); 11. #else 12. FileDelete(def_FILENAME); 13. #endif 14. SaveInfo("This file was created by a script written in MQL5."); 15. SaveInfo("New information is being added to the file."); 16. } 17. //+------------------------------------------------------------------+ 18. void SaveInfo(const string szArg) 19. { 20. uchar info[], tmp[]; 21. 22. #ifdef def_FILE_COMMON 23. FileLoad(def_FILENAME, info, FILE_COMMON); 24. #else 25. FileLoad(def_FILENAME, info); 26. #endif 27. StringToCharArray(szArg + "\n", tmp); 28. ArrayCopy(info, tmp, info.Size(), 0, tmp.Size() - 1); 29. #ifdef def_FILE_COMMON 30. FileSave(def_FILENAME, info, FILE_COMMON); 31. #else 32. FileSave(def_FILENAME, info); 33. #endif 34. } 35. //+------------------------------------------------------------------+
Código 05
Observa que aquí, en este código 05, resolvemos el problema de sincronización de una forma muy práctica, simplemente porque definimos en la línea cinco qué código debía compilarse. Esto evita diversos problemas al intentar acceder a un archivo de una forma en un punto y de otra en otro.
Bien, parece que hemos introducido un buen cambio en el código. Pero, si vienes estudiando y procurando practicar lo que se ha mostrado en estos artículos, debes haber pensado lo siguiente: ¿no existiría una forma de colocar esta llamada a FileDelete dentro del procedimiento SaveInfo? Esto evitaría que necesitáramos usar esta implementación que se ve en el código 05, al mismo tiempo que mantenemos cierta sincronización de la propia SandBox, ya que todas las funciones de la biblioteca estarían reunidas, lo que nos permitiría modificar el código con más facilidad y agilidad.
Sí, mi querido lector, existe una forma de hacer esto. Aunque no sea una manera muy convencional, funciona. La forma de hacerlo, a partir de lo que se ha mostrado hasta este momento, sería utilizar una variable estática. Una forma más segura sería usar clases, pero, como todavía no hemos entrado en este concepto, no voy a mostrar cómo implementar la solución mediante una clase. Pero sí puedo mostrar la solución usando una variable estática. Esta se hace utilizando el código que aparece a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. SaveInfo("This file was created by a script written in MQL5."); 07. SaveInfo("New information is being added to the file."); 08. } 09. //+------------------------------------------------------------------+ 10. void SaveInfo(const string szArg) 11. { 12. //+----------------+ 13. #define def_FILENAME "Hello.txt" 14. //+----------------+ 15. static bool b_Clear = false; 16. uchar info[], tmp[]; 17. 18. if (!b_Clear) FileDelete(def_FILENAME); 19. FileLoad(def_FILENAME, info); 20. StringToCharArray(szArg + "\n", tmp); 21. ArrayCopy(info, tmp, info.Size(), 0, tmp.Size() - 1); 22. FileSave(def_FILENAME, info); 23. b_Clear = true; 24. } 25. //+------------------------------------------------------------------+
Código 06
Ahora, presta atención, mi querido lector. Aunque este código 06 es muy similar al código 04, su comportamiento es bastante diferente. Esto se debe, justamente, a la variable estática declarada en la línea quince. Pero el hecho de haber concentrado todas las operaciones que deben realizarse en el archivo en un único lugar, que sería precisamente el procedimiento SaveInfo, hace mucho más sencillo buscar y modificar las cosas según la necesidad que tengamos. Así, no necesitaremos ir buscando en el código puntos que puedan entrar en conflicto con las operaciones previstas en el archivo.
Bien, creo que ya ha quedado claro cómo podríamos trabajar con el sistema eliminando el archivo original. Sin embargo, podemos hacerlo de una forma un poco diferente y, aun así, obtener el mismo tipo de resultado logrado con los cambios hechos y mostrados en este tema. Para mostrarlo, pasemos a un nuevo tema, en el que voy a presentar el segundo tipo de solución.
Segunda forma de solución
Como se dijo anteriormente, existen más formas de resolver el problema. Sin embargo, aquí abordaré solo dos, para que no te aferres a una única solución. Pensar en formas de resolver problemas es el trabajo de todo buen programador. Entonces, veamos cuál sería la segunda forma de resolver el problema. Te sorprenderá el nivel de simplicidad adoptado. Para ser sincero, la solución sería algo intermedio entre el código 03 y el código 06. Sin embargo, para que las cosas tengan sentido, me gustaría que pensaras en la solución que veremos aquí como una variación del código 03, después de un simple cambio. Ese cambio puede verse a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. SaveInfo("This file was created by a script written in MQL5."); 07. SaveInfo("New information is being added to the file."); 08. } 09. //+------------------------------------------------------------------+ 10. void SaveInfo(const string szArg) 11. { 12. const string FileName = "Hello.txt"; 13. static bool b_isFirst = true; 14. uchar info[], tmp[]; 15. 16. if (b_isFirst) 17. { 18. ArrayFree(info); 19. b_isFirst = false; 20. }else 21. FileLoad(FileName, info); 22. StringToCharArray(szArg + "\n", tmp); 23. ArrayCopy(info, tmp, info.Size(), 0, tmp.Size() - 1); 24. FileSave(FileName, info); 25. } 26. //+------------------------------------------------------------------+
Código 07
Ahora, mira este código 07 y responde con sinceridad. ¿Entiendes lo que ocurre allí? Observa que el código en sí se parece mucho a una mezcla entre el código 03 y el código 06. Sin embargo, a diferencia del código 06, donde usábamos la función FileDelete, aquí, en este código 07, usamos el propio sistema de escritura para limpiar el archivo, al mismo tiempo que garantizamos que reciba nueva información a medida que vaya llegando. ¿Pero cómo se logró esto?
Bien, mi querido lector, si tienes dudas sobre cómo fue posible, quizá deberías estudiar los primeros artículos de esta serie, en los que expliqué cómo trabajar con variables estáticas. Aunque muchos consideren que las variables estáticas son un tema complicado, entenderlas es fundamental para poder resolver diversos problemas de forma simple y sin complicaciones, haciendo que el código sea bastante portable entre aplicaciones diferentes, pero con un mismo objetivo.
El artículo en el que expliqué las variables estáticas es Del básico al intermedio: Variables (II). Como este código 07 es muy simple de entender, siempre que entiendas los conceptos implicados en lo que se usa allí, no voy a perder tiempo explicando cómo funciona. En lugar de eso, vamos a ver otra solución, muy parecida, pero con un tipo de resultado bastante interesante y que merece, y puede, explicarse aquí.
En todos estos códigos que vimos hasta aquí, puedes haber notado que siempre estábamos ligados a un único archivo, que se definía en algún punto del código. Incluso puedes imaginar que sería muy bueno poder guardar las cosas en archivos diferentes sin tener que cambiar absolutamente nada del código.
Bien, la mayoría de las veces podrías pensar simplemente en pasar el nombre del archivo que se va a guardar como argumento del procedimiento SaveInfo. Bien, esa sería una forma de resolver el problema, pero, al mismo tiempo, generaría otro tipo de problemas, que pueden ser más o menos complicados de resolver, según, por supuesto, el tipo de actividad que espere y requiera tu aplicación. Sin embargo, existe una forma relativamente elegante de resolver esta cuestión, con la que podemos guardar las cosas en archivos diferentes y sin mucho trabajo.
Esta forma implica programación estructurada. Ya hablamos de esto en un pequeño bloque de artículos dentro de esta misma secuencia actual. Como se trata de un tema abordado en más de un artículo, en este caso no voy a indicarte un enlace. Porque quizá necesites ver el tema desde el principio para entender correctamente cómo se desarrolla realmente la programación estructurada. Sin embargo, no sería justo con quienes vienen siguiendo los artículos y estudiando lo que se ha mostrado que no enseñar cómo resolver esta cuestión relacionada con la forma de seleccionar el nombre del archivo que se va a guardar.
Entonces, para quienes ya entienden los conceptos básicos de programación estructurada, veamos cómo sería el código para tener una solución mucho mejor que la vista hasta aquí, pero que también requiera pocos accesos al disco, como ocurre en el código 07. La solución puede verse a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. struct stFile 05. { 06. private : 07. string FileName; 08. int common_flag; 09. bool b_isFirst; 10. public : 11. //+----------------+ 12. void SetFileName(const string szArg, const int iArg = 0) 13. { 14. FileName = szArg; 15. common_flag = iArg; 16. b_isFirst = true; 17. } 18. //+----------------+ 19. void SaveInfo(const string szArg) 20. { 21. uchar info[], tmp[]; 22. 23. if (FileName == NULL) return; 24. if (b_isFirst) 25. { 26. ArrayFree(info); 27. b_isFirst = false; 28. }else 29. FileLoad(FileName, info, common_flag); 30. StringToCharArray(szArg + "\n", tmp); 31. ArrayCopy(info, tmp, info.Size(), 0, tmp.Size() - 1); 32. FileSave(FileName, info, common_flag); 33. } 34. //+----------------+ 35. }; 36. //+------------------------------------------------------------------+ 37. void OnStart(void) 38. { 39. stFile file; 40. 41. file.SetFileName("Hello.txt"); 42. file.SaveInfo("This file was created by a script written in MQL5.\nStructured programming version."); 43. file.SaveInfo("New information is being added to the file.\nFile saved in the local directory."); 44. 45. file.SetFileName("Hello.txt", FILE_COMMON); 46. file.SaveInfo("This file was created by a script written in MQL5.\nStructured programming version."); 47. file.SaveInfo("New information is being added to the file.\nFile saved in shared directory."); 48. } 49. //+------------------------------------------------------------------+
Código 08
Este código 08 es muy interesante, además de bastante entretenido, ya que aplica diversos conceptos al mismo tiempo. Esto se hace para que tengamos una tarea muy específica. Como muchos, incluso quienes procuraron estudiar los artículos en los que expliqué cómo funciona la programación estructurada, pueden tener dudas sobre cómo funciona este código en algunos puntos, voy a dar una explicación rápida sobre algunos aspectos de interés. Sin embargo, observen que gran parte del código se parece, en muchos aspectos, al código 07. Solo que aquí estamos haciendo algo un poco más elaborado y, por lo tanto, bastante mejor, al menos en mi opinión.
Observa que, en la línea cuatro, declaramos una estructura. Esta servirá como base para lo que vamos a hacer. Dentro de ella, tenemos algunos campos privados y dos procedimientos. El procedimiento de la línea 12 tiene como objetivo indicar cuál es el nombre del archivo con el que trabajaremos en un momento dado. Además, nos permite cambiar la SandBox que estemos utilizando. Bien, ahora viene la parte un poco complicada en este primer momento.
Como estamos trabajando con código estructural y no con una clase, no podemos hacer cierto tipo de cosas. Pero sí podemos hacer otras. En este caso, cuando el compilador crea el mecanismo para que la región de memoria se asigne a las variables que se declaran entre las líneas siete y nueve, también realiza una preinicialización de los valores. Saber qué tipo de valor inicial tendremos en esa región de memoria es muy importante. Por lo general, las strings se inicializan como nulas. Esto nos garantiza una forma de realizar la prueba que se ve en la línea 23.
Esta prueba tiene como objetivo garantizar que haya un archivo indicado. Es decir, si no has indicado qué archivo debe usarse, esta prueba tendrá éxito, haciendo que el procedimiento SaveInfo retorne de inmediato. El resto del código es muy simple de entender, ya que se vio en otras partes de este artículo.
Sin embargo, pasemos al procedimiento OnStart, porque allí es necesario dar cierta explicación para que puedas estudiar correctamente este código 08.
Observa que, en la línea 39, creamos una variable para poder utilizar la estructura definida en la línea cuatro. Ahora, cuando se ejecute la línea 41, indicaremos cuál será el archivo que utilizaremos a partir de ese momento. Presta atención a esto, mi querido lector. Sin esta línea 41, las siguientes llamadas no tendrían ningún efecto práctico.
Pero la parte curiosa aparece justamente en la línea 45. En ese momento, modificaremos el flujo de información que antes se dirigía a un archivo, definido en la línea 41, para enviarlo a otro archivo. Pero no es solo eso. Observa que, a pesar de todo, el nombre del archivo es el mismo que se definió en la línea 41. Sin embargo, aquí indicamos que MetaTrader 5 dirija el flujo a otra SandBox.
¿Podemos hacer esto? Sí, mi querido lector. Observa que, independientemente de cómo el código deba o pueda implementarse, podemos dirigir el flujo, de una manera muy simple, a cualquier archivo al que tengamos acceso. siempre que podamos escribir en la SandBox indicada.
En la práctica, esto es mucho más interesante de lo que parece con solo mirar este código. Tanto es así que, cuando ejecutes este código 08, el resultado será la creación de dos archivos. Ambos pueden verse a continuación.

Imagen 04

Imagen 05
Observa que el contenido es diferente, así como el directorio, que está destacado en verde.
Consideraciones finales
En este artículo vimos que no existe una fórmula única para trabajar con sistemas de archivos. Empezamos a poner en práctica algo que a muchos puede parecerles complicado, pero, con el tiempo, estudio y dedicación, acabarás tomándole gusto y adquiriendo soltura. No existe una solución mágica. Lo que existe es la aplicación de diversos conceptos, de tal forma que, al final, podemos obtener un determinado resultado.
Así que, mi querido lector, procura estudiar y practicar lo que se vio aquí. Porque una comprensión correcta, unida a una práctica constante y a la búsqueda permanente de nuevas formas de hacer lo mismo, es lo que te dará el conocimiento adecuado. En el anexo tendrás los códigos para poder estudiar y practicar lo que se mostró aquí. Pero no uses los códigos solo tal como están en el anexo. Procura modificar los códigos, sobre todo este código 08, para entender mejor cómo las líneas 41 y 45 afectan el resultado general de la ejecución. En el futuro, cuando ya se hayan explicado las clases, verás que este código 08 tiene grandes posibilidades de mejora y de facilitar su uso, siempre que lo adaptes de la manera correcta. Pero, para hacerlo, primero necesitas entender cómo funciona este código estructural.
| Archivo MQ5 | Descripción |
|---|---|
| Code 01 | Demostración de acceso a archivos |
| Code 02 | Demostración de acceso a archivos |
| Code 03 | Demostración de acceso a archivos |
| Code 04 | Demostración de acceso a archivos |
| Code 05 | Demostración de acceso a archivos |
| Code 06 | Demostración de acceso a archivos |
| Code 07 | Demostración de acceso a archivos |
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/16211
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Utilizando redes neuronales en MetaTrader
Simulación de mercado: Position View (III)
Particularidades del trabajo con números del tipo double en MQL4
Operando con el Calendario Económico MQL5 (Parte 8): Optimización del backtesting basado en noticias mediante el filtrado inteligente de eventos y el registro selectivo
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso