От начального до среднего уровня: FileSave и FileLoad
Введение
В предыдущей статье От начального до среднего уровня: Sandbox и MetaTrader в максимально простой и практичной форме было разъяснено понятие "песочница" и его влияние на рабочий процесс в MetaTrader 5 при использовании функций и процедур библиотеки MQL5 для создания, обработки и чтения файлов в целом.
Этот материал был предназначен лишь для объяснения того, что такое песочница, а не для демонстрации оптимальных методов работы с файлами. Это обусловлено тем, что существуют гораздо более эффективные способы реализации этого средствами чистого MQL5, без необходимости задействования каких-либо функций операционной системы.
Предполагая, что читатель ознакомился с концепцией песочницы и её влиянием на рабочий процесс, можно перейти к рассмотрению взаимодействия с файлами для достижения иных результатов. Следует отметить, что в рамках данного материала не рассматривается разработка какого-либо конкретного приложения. Для демонстрации реализации определенных задач будут использоваться исключительно некоторые функции и процедуры библиотеки MQL5. Тем не менее, для понимания определенных нюансов, которые могут вызвать вопросы, необходимо самостоятельно практиковать изученный материал. Таким образом, по сложившейся традиции, давайте перейдем к новой теме для рассмотрения базовых вопросов.
Единой универсальной формулы не существует
В предыдущей статье была реализована схема чтения и записи для довольно простой задачи, и на тот момент код можно было оставить в исходном виде. Однако на практике в реальных условиях реализация задач далеко не всегда соответствует примеру, который мы тогда показали. Это обусловлено возможностью возникновения определенной проблемы. Для восстановления контекста давайте ещё раз посмотрим, в чём именно состоит эта проблема. Для этого мы воспользуемся одним из кодов, которые рассматривали в предыдущей статье. Его можно увидеть ниже:
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. //+------------------------------------------------------------------+
Код 01
При выполнении данного кода 01 будет создан файл, содержимое которого представлено на следующем изображении.

Изображение 01
На данном изображении 01 показан необычный символ — в данном случае это нулевой символ (NUL). Он используется для определения границы строки и в контексте приложения, использующего такой тип представления строк, не представляет собой проблему. Однако в файле, предназначенном для хранения чистого текста, наличие такого символа не имеет смысла и зачастую представляет собой проблему в зависимости от назначения или целевого использования текстового файла.
Существуют различные способы устранения подобной проблемы. Некоторые из них являются более прямыми, в то время как другие носят косвенный характер и требуют выполнения дополнительных шагов для корректной реализации. Более того, существует еще один нюанс, связанный с данным кодом 01. Хотя строка 11 способна создавать файл и записывать в него данные напрямую максимально простым и непосредственным образом, такой способ решения задачи создает другую проблему, которую подробно рассмотрим в ближайшее время, а именно произвольный доступ к данным файла.
Хотя подобное утверждение может показаться необычным, на практике файлы на диске не функционируют как системы последовательного чтения, как это происходит, например, с файлами на магнитных лентах. Там при необходимости чтения файла или определенных данных приходится считывать значительную часть магнитной ленты, что в некоторые моменты существенно усложняет процесс чтения и записи. Однако при работе с файлами, сохраненными на диске, возможно использование как последовательного, так и произвольного доступа, что позволяет выполнять чтение или запись любого фрагмента файла в любой момент времени.
Однако, несмотря на такую возможность, FileSave и FileLoad не являются подходящими функциями для произвольного доступа, поскольку они считывают или записывают файл полным блоком и обычно загружают или сохраняют на диск весь файл целиком. Для небольших файлов это не представляет никакой проблемы, и, если содержимое файла можно игнорировать ради его перезаписи, в приложениях можно без каких-либо затруднений использовать FileSave и FileLoad, так как они представляют собой очень практичный и безопасный способ чтения и записи файлов.
Однако, если задача заключается в изменении файла с сохранением части его содержимого в неизменном виде, использование FileSave и FileLoad может оказаться не лучшим решением, поскольку в этом случае требуется держать весь файл в памяти. Это может быть совершенно нецелесообразно, поскольку зачастую в памяти необходимо удерживать лишь небольшой фрагмент, с которым ведется непосредственная работа. Остальная часть может оставаться на диске и загружаться по мере необходимости.
По этой причине не существует единой универсальной формулы или универсального и окончательного способа реализации задач. Каждый случай уникален, и принимаемое решение нужно выбирать исходя из требований конкретного случая, чтобы работа пользователя с вашими приложениями в MetaTrader 5 оставалась удобной и практичной.
Для более простого и наглядного понимания вышесказанного следует создать несколько простых примеров, позволяющих продемонстрировать цель утверждения о необходимости индивидуального проектирования каждой реализации, так как у многих может отсутствовать четкое представление о том, насколько сильно определенные решения могут повлиять на способ разработки приложения.
Начнем с попытки добавления новой информации в файл, который уже сохранен на диске. В данном случае обрабатываемый файл будет точно таким же, как и файл, создаваемый кодом 01. Однако в этот самый код 01 будут внесены некоторые изменения для обеспечения возможности такой обработки файла. Напоминаем: цель заключается в добавлении новой информации в уже сохраненный файл. Итак, теоретически можно было бы рассмотреть решение, аналогичное представленному ниже.
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. //+------------------------------------------------------------------+
Код 02
При выполнении кода 02 ожидается, что файл Hello.txt будет содержать как данные с изображения 01, так и новую информацию. Данные, которые должны были оказаться в файле, приведены в строках 06 и 07 этого кода 02. Прошу заметить, что это очень просто для понимания. Однако при выполнении данного скрипта в MetaTrader 5 результат оказывается не совсем таким, как ожидалось Его можно увидеть ниже:

Изображение 02
И тут возникает главный вопрос — и вместе с ним не менее важное сомнение. Что могло произойти, чтобы получить результат, показанный на изображении 02? Поскольку изначально такой результат не является ожидаемым, так как и содержимое строки 06, и содержимое строки 07 должны присутствовать в конечном файле. Однако отображается только сообщение из строки 07.
Иными словами, по какой-то причине содержимое строки 06 игнорируется и записывается только содержимое строки 07. Таким образом, возникает вопрос о том, что происходит с содержимым строки 06. Это одна из наиболее сложных проблем для понимания без содействия специалиста, способного подробно разъяснить происходящие процессы. Однако на практике это достаточно просто. При выполнении строки 06 функция, расположенная в строке 15, осуществляет запись данных в файл. Непосредственно после этого, при выполнении строки 07, происходит перезапись данного файла и внесение в него нового содержимого. Несмотря на простоту данного процесса, без сторонних разъяснений бывает трудно обнаружить причину, по которой код не записывает определенные данные. Это создает ложное впечатление о наличии серьезной ошибки в коде, хотя в действительности проблема заключается в другом.
Итак, как можно решить данную проблему? Иными словами, как можно было бы сохранить содержимое как строки 06, так и строки 07 в одном файле? Итак, уважаемый читатель, как и указано в заголовке данной темы: ЕДИНОЙ ФОРМУЛЫ НЕ СУЩЕСТВУЕТ. Существуют различные варианты решения данной задачи, каждый из которых имеет свои преимущества и недостатки. Здесь мы покажем только один пример для наглядности. Но вы можете придумать и другие варианты и попытаться их реализовать. Попробуйте сделать это, взяв за основу функцию FileSave. Это объясняется наличием других, гораздо более простых решений, при условии, что они хорошо спланированы.
Давайте посмотрим, какое решение можно было бы предложить в первую очередь. Его можно увидеть ниже:
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. //+------------------------------------------------------------------+
Код 03
Выполнив код 03, мы получим результат, очень похожий на этот:

Изображение 03
Что ж, текущий результат полностью соответствует ожидаемому. Иными словами, в файл добавляются как содержимое строки 06, так и содержимое строки 07. Но не только это. Можно заметить, что теперь указанный символ NULL больше не добавляется. Как удалось решить две проблемы одновременно?
Кроме того, при более внимательном анализе становится очевидно, что процедура SaveInfo, расположенная в строке 10 кода 03, практически представляет собой код для создания файлов логов. Иными словами, без особых усилий и затрат можно создать систему, которая будет записывать в файл то, что делает ваш код. Таким образом, можно детально изучить и проанализировать то, что происходит во время выполнения. Интересно, не правда ли? Простое в реализации решение с весьма интересными результатами и возможностями.
Давайте теперь посмотрим, что здесь произошло, чтобы стало возможным исправить код 02 и сделать код 03 гораздо более подходящим и, главное, способным сохранять как содержимое строки 06, так и строки 07.
Для этого можно заметить, что была добавлена константа. Это позволяет использовать одно и то же обозначение имени файла, поскольку в строке 15 мы будем его читать, а в строке 18 — записывать. Но зачем это делать? Итак, уважаемый читатель, причина заключается в том, что, как уже показали в коде 02, функция FileSave осуществляет запись содержимого буфера за один проход. Иными словами, если буфер, которым в данном случае является массив info, содержит определенную информацию, то эта информация перезапишет исходные данные в файле. Однако, когда мы читаем файл, мы загружаем его содержимое в буфер. Сразу после этого, используя строку 17, мы добавляем новую информацию к той, которая уже существовала в исходном буфере файла. Это как если бы мы буквально создавали файл в памяти, а затем сохраняли его на диск.
В рассматриваемом нами примере данный вид операций выполняется очень быстро. Однако по мере увеличения размера файла это становится проблемой, поскольку выполнение операций чтения и записи подобным образом снижает эффективность приложения с точки зрения использования системных ресурсов. В конечном итоге это влияет на время выполнения, поскольку чем больше данных в файле, тем дольше длится чтение и запись данных на диск. Однако для таких простых случаев, как рассматриваемый здесь, данный вариант решения вполне может быть применен.
Хорошо, но что произойдет, если мы запустим приложение снова? На первый взгляд, всё работает без проблем. В этом случае строки 06 и 07 будут записываться снова и снова, добавляя всё больше и больше строк с той же информацией. Это будет продолжаться до тех пор, пока мы не удалим исходный файл, после чего весь процесс начнётся заново. Иными словами, у нас появилась новая задача. Это можно сделать с помощью кода двумя основными способами. Хотя существуют и другие, здесь мы сосредоточимся только на двух.
Первое решение
Первое решение этой проблемы — удалить файл и начать перезаписывать его по мере работы приложения. Для этого потребуется заменить код 03 решением, аналогичным представленному ниже.
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. //+------------------------------------------------------------------+
Код 04
Отлично, код 04 частично решает проблему, существовавшую в коде 03. Однако важно подчеркнуть, что данное решение, несмотря на свою работоспособность, является в некоторой степени опасным. Это связано с тем, что в случае, если программист забудет указать корректную песочницу, это может привести к возникновению серьезных проблем. Причина в том, что строка 08 кода 04 действительно удалит указанный файл. Однако, если операция чтения в строке 17 и операция записи в строке 20 не совпадают со строкой 08 в отношении используемой песочницы, мы можем удалить файл, который не совсем соответствует тому, что мы хотели.
Поскольку код 04 будет приложен, можно изменить его, чтобы попытаться понять, какие проблемы могут возникнуть при использовании разных песочниц. Однако для решения данной проблемы можно добавить определение для синхронизации всех аспектов реализации и предотвратить, таким образом, возникновение проблем, связанных с использованием различных песочниц в разные моменты времени. Это можно сделать, как показано ниже.
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. //+------------------------------------------------------------------+
Код 05
Прошу заметить, что здесь, в коде 05, мы решаем проблему синхронизации очень практичным способом, просто потому что в строке 05 мы определяем, какой код должен быть скомпилирован. Это позволяет избежать различных проблем, возникающих при попытке доступа к файлу одним способом в одном месте и другим способом в другом.
Хорошо, похоже, мы внесли полезные изменения в код. Но если вы изучали и пытались применять на практике то, что показано в этих статьях, вы наверняка подумали: разве нельзя было бы поместить вызов FileDelete внутрь процедуры SaveInfo? Это позволило бы исключить необходимость использования реализации, представленной в коде 05, при одновременном сохранении определенной синхронизации самой песочницы, так как все функции библиотеки будут объединены, что обеспечит возможность модификации кода с большей легкостью и оперативностью.
Да, уважаемый читатель, это можно сделать. Хотя это и не совсем традиционный способ, он работает. Исходя из того, что было показано до этого момента, можно сделать это с помощью статической переменной. Более безопасным способом было бы использование классов, но, поскольку мы ещё не рассматривали эту концепцию, мы не будем показывать, как реализовать решение с помощью класса. Но можно показать решение, используя статическую переменную. Это делается с помощью данного кода:
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. //+------------------------------------------------------------------+
Код 06
Итак, теперь слушайте внимательно. Хотя код 06 очень похож на код 04, его поведение существенно отличается. Именно это связано со статической переменной, объявленной в строке 15. Но тот факт, что все операции, которые необходимо выполнить с файлом, сосредоточены в одном месте, а именно в процедуре SaveInfo, это значительно упрощает поиск и изменение данных в соответствии с нашими нуждами. Таким образом, нам не придётся искать в коде места, которые могут конфликтовать с операциями, предусмотренными для этого файла.
Хорошо, думаю, теперь понятно, как мы можем работать с системой, удалив исходный файл. Однако мы можем сделать это немного по-другому и всё равно получить тот же результат, что и при внесенных и показанных в этой теме изменениях. Для иллюстрации этого перейдем к новой теме, в которой мы представим второй вид решения.
Второе решение
Как уже упоминалось ранее, существует множество способов решения данной проблемы. Однако здесь мы рассмотрим только два способа, чтобы не зацикливаться на одном решении. Размышлять о способах решения проблем — это задача каждого хорошего программиста. Давайте теперь посмотрим, каким будет второй способ решения проблемы. Вы будете удивлены уровнем его простоты. Честно говоря, решение должно быть чем-то средним между кодом 03 и кодом 06. Однако, чтобы всё стало понятно, я бы хотел, чтобы вы рассматривали данное решение как вариант кода 03 после внесения простых изменений. Эти изменения можно увидеть ниже:
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. //+------------------------------------------------------------------+
Код 07
Теперь посмотрите на код 07 и ответьте честно. Вы понимаете, что там происходит? Прошу заметить, что сам код очень похож на смесь кода 03 и кода 06. Однако, в отличие от кода 06, где мы использовали функцию FileDelete, здесь, в коде 07, мы используем саму систему записи для очистки файла, обеспечивая при этом получение новой информации по мере её поступления. Но как нам удалось реализовать это?
Что ж, мой уважаемый читатель, если у вас остались какие-либо сомнения относительно того, как это стало возможным, пожалуй, вам следует изучить первые статьи данной серии, в которых мы объяснили, как работать со статическими переменными. Хотя многие считают статические переменные сложной темой, понимание их крайне важно для простого и беспроблемного решения различных задач, что делает код достаточно переносимым между различными приложениями, но с одной целью.
О статических переменных я рассказывал в статье От начального до среднего уровня: Переменные (II) . Поскольку код 07 очень прост, и вы понимаете основные принципы его использования, не будем тратить время на объяснение его функционирования. Вместо этого мы рассмотрим другое решение, очень похожее, но с довольно интересным результатом, который заслуживает объяснения.
Во всех рассмотренных нами фрагментах кода вы наверное заметили, что мы всегда были привязаны к одному файлу, который задавался в каком-то месте кода. Можно даже представить, как здорово было бы сохранять данные в разных файлах, не внося при этом абсолютно никаких изменений в код.
В большинстве случаев можно просто передать имя сохраняемого файла в качестве аргумента процедуре SaveInfo. Это был бы один из способов решения проблемы, но в то же время он породил бы и другие типы проблем, которые могут быть более или менее сложными в решении, в зависимости от типа деятельности, которую ожидает и требует наше приложение. Однако существует относительно элегантный способ решения данной проблемы, который позволяет сохранять данные в разных файлах без особых усилий.
Этот подход предполагает использование структурного программирования. Мы уже обсуждали это в небольшом блоке статей в рамках данной же серии. Поскольку эта тема уже затрагивалась в нескольких статьях, в данном случае мы не будем давать ссылку. Потому что для правильного понимания того, как на самом деле работает структурированное программирование, вам наверняка потребуется изучить эту тему с самого начала. Однако было бы несправедливо по отношению к тем, кто следил за статьями и изучал представленную информацию, не рассказать, как решить проблему, связанную со способом выбора имени файла для сохранения.
Итак, для тех, кто уже знаком с основами структурного программирования, давайте посмотрим, как бы выглядел код, чтобы получить гораздо лучшее решение, чем то, что мы видели до настоящего момента, но которое также требует меньшего количества обращений к диску, как в коде 07. Решение представлено ниже:
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. //+------------------------------------------------------------------+
Код 08
Код 08 очень интересен и даже довольно занимателен, поскольку в нем одновременно применяются различные концепции. Это делается для того, чтобы у нас была очень конкретная задача. Поскольку у многих, даже у тех, кто пытался изучить статьи, в которых мы объясняли, как работает структурное программирование, могут возникнуть сомнения относительно того, как работает данный код в некоторых моментах, мы дадим краткое объяснение некоторым интересным аспектам. Однако следует отметить, что значительная часть кода во многом напоминает код 07. Но здесь мы делаем нечто более сложное и, следовательно, гораздо лучшее, по крайней мере, на мой взгляд.
Обратите внимание, что в строке 04 мы объявили структуру. Это послужит основой для того, что мы собираемся сделать. Внутри структуры у нас есть несколько закрытых полей и две процедуры. Процедура в строке 12 предназначена для указания имени файла, с которым мы будем работать в любой момент времени. Кроме того, это нам позволяет менять используемую нами песочницу. Итак, теперь начинается немного сложная часть этого первого этапа.
Поскольку мы работаем со структурным кодом, а не с классом, мы не можем выполнять определенные действия. Но можем использовать другие приемы. В этом случае, когда компилятор создает механизм для выделения области памяти переменным, объявленным между строками 07 и 09, он также выполняет предварительную инициализацию значений. Очень важно знать, какое начальное значение будет иметь эта область памяти. Строки обычно инициализируются значением null. Это гарантирует возможность провести тест, который показан в строке 23.
Цель данного теста — убедиться в наличии указанного файла. Иными словами, если мы не указали, какой файл использовать, данная проверка пройдет успешно, и процедура SaveInfo немедленно завершит работу. Остальная часть кода очень проста, поскольку она была рассмотрена в других частях данной статьи.
Однако давайте перейдем к процедуре OnStart, поскольку здесь требуется некоторое пояснение, чтобы вы могли должным образом изучить код 08.
Прошу заметить, что в строке 39 мы создали переменную для использования структуры, определенной в строке 04. Теперь, при выполнении строки 41, мы укажем, какой файл будем использовать с этого момента. Обратите на это внимание, уважаемый читатель. Без этой строки 41 последующие вызовы не имели бы никакого практического эффекта.
Но интересный момент можно заметить именно на строке 45. На данном этапе мы изменим поток информации, который ранее был направлен в файл, определенный в строке 41, чтобы отправить его в другой файл. Но дело не только в этом. Прошу заметить, что, несмотря ни что, имя файла совпадает с именем, указанным в строке 41. Однако здесь мы указываем, что MetaTrader 5 должен перенаправить поток данных в другую песочницу.
Разве мы можем это сделать? Да, такое возможно, уважаемый читатель. Обратите внимание, что независимо от того, как должен или может быть реализован код, мы можем легко направить поток данных в любой доступный нам файл, при условии, что у нас есть возможность записи в указанную песочницу.
На практике это гораздо интереснее, чем кажется, судя по данному коду. Настолько, что при выполнении кода 08 в результате будут созданы два файла, которые можно увидеть ниже.

Изображение 04

Изображение 05
Прошу заметить, что содержимое отличается, как и каталог, который выделен зеленым цветом.
Заключительные идеи
В этой статье мы убедились, что не существует единой формулы для работы с файловыми системами. Мы начали применять на практике то, что многим может показаться сложным, но со временем, благодаря обучению и целеустремленности, вам это понравится, и со временем вы войдете во вкус и почувствуете уверенность. Волшебного решения не существует. Суть заключается в применении различных концепций таким образом, чтобы в конечном итоге получить определенный результат.
Итак, уважаемый читатель, обязательно изучите и примените на практике то, что вы здесь увидели. Потому что правильное понимание в сочетании с постоянной практикой и непрерывным поиском новых способов выполнения того же самого — вот что даст вам необходимые знания. В приложении можно найти коды для изучения и отработки представленной здесь информации. Но не используйте коды в том виде, в котором они приведены в приложении. Попробуйте изменить код, особенно код 08, чтобы лучше понять, как строки 41 и 45 влияют на общий результат выполнения. В будущем, после того как будут объяснены классы, мы увидим, что код 08 имеет большой потенциал для улучшения и упрощения использования, при условии правильной его адаптации. Но для этого сначала нужно понять, как работает этот структурированный код.
| Файл MQ5 | Описание |
|---|---|
| Code 01 | Демонстрация доступа к файлам |
| Code 02 | Демонстрация доступа к файлам |
| Code 03 | Демонстрация доступа к файлам |
| Code 04 | Демонстрация доступа к файлам |
| Code 05 | Демонстрация доступа к файлам |
| Code 06 | Демонстрация доступа к файлам |
| Code 07 | Демонстрация доступа к файлам |
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/16211
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Нейросети в трейдинге: Когнитивная инерция в анализе финансовых рынков (CogDriver)
Рыночные секреты Ларри Уильямса (Часть 5): Автоматизация стратегии волатильного пробоя на MQL5
Преодоление проблем доступности в торговых инструментах на MQL5 (Часть II): Включению голосовых функций в советнике с помощью Python-движка синтеза речи
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования