| / | Статьи |
Cтатьи
Примеры
Групповые файловые операции
Авторизуйтесь или зарегистрируйтесь , чтобы добавить новую статью
|
Групповые файловые операции [ en ]ВведениеПрочитать или записать один файл не является проблемой. Даже если при этом придется использовать WinAPI, как это описано в статье Файловые операции через WinAPI. Но что делать, если мы не знаем точного имени файла, известно только его местонахождение в определенной папке и задано расширение? Можно вручную каждый раз вводить нужное имя в виде параметра, но если таких файлов будет пять, десять или более? Нужен способ групповой обработки однотипных файлов в указанной папке. Для этого хорошо подходят функции FindFirstFile(), FindNextFile() и FindClose(), входящие в библиотеку kernel32.dll. Функция FindFirstFile()Описание функции приводится в msdn по адресу http://msdn.microsoft.com/en-us/library/aa364418(VS.85).aspx. HANDLE WINAPI FindFirstFile( Из него следует, что данная функция возвращает указатель на найденный файл, удовлетворящий условиям поиска. Условие поиска задается в переменной lpFileName, которая содержит путь, по которому следует искать файл, и возможное имя этого файла. Удобство этой функции в том, что мы можем задать поиск по маске, например, найти файл по маске "C:\folder\*.txt". Функция вернет первый же файл, который находится в папке "C:\folder" и будет иметь расширение txt. Возвращаемый результат этой функции имеет тип int в MQL4. Для передачи входного параметра подойдет тип string, осталось только разобраться с тем, что передавать в эту функкцию вторым параметром и как этот параметр потом обрабатывать. Импорт этой функции будет выглядеть примерно так: #import "kernel32.dll" int FindFirstFileA(string path, .какой-то второй параметр); #import Здесь мы видим уже знакомую библиотеку kernel32.dll. Правда, имя функции записано не FindFirstFile(), а как FindFirstFileA(). Это объясняется тем, что многие функции в этой библиотеки имеют два варианта: для работы со строками в кодировке Unicode к имени функции добавляется буква "W" (FindFirstFileW), а для работы с кодировкой ANSI добавляется буква "A" (FindFirstFileA). Теперь нам необходимо разобраться только со вторым параметром функции, который описан как: lpFindFileData [out] - A pointer to the WIN32_FIND_DATA structure that receives information about a found file or directory. То есть, это указатель на некую структуру WIN32_FIND_DATA. В данном случае структура - это некая область в оперативной памяти компьютера, указатель(адрес) на которую и передается в функцию. Выделить память в MQL4 мы можем массивом данных, указатель задается спецификатором &, осталось только узнать размер необходимой памяти в байтах, указатель на которую мы передадим. Смотрим описание структуры. typedef struct _WIN32_FIND_DATA {
В MQL4 нет типов DWORD, TCHAR и FILETIME. Известно, что DWORD занимает 4 байта, как тип int в MQL4, TCHAR имеет внутренее представление в один байт. Чтобы подсчитать весь размер структуры WIN32_FIND_DATA в байтах, осталось только выяснить что такое FILETIME. typedef struct _FILETIME {
Оказывается, что FILETIME состоит из двух DWORD, а значит FILETIME - это 8 байт. Запишем все это в таблицу:
Теперь мы можем подсчитать размер структуры WIN32_FIND_DATA и представить визуально где и что в ней находится.
Итого размер структуры равен: 4 + 8 + 8 + 8 + 4 + 4 + 4 +4 + 260 +14 = 318 байт.
Как видно из рисунка, имя файла начинается с 45 по счету байта, предудыщие 44 байта содержат всякую вспомогательную информацию. Необходимо передать в функцию FindFirstFile() в качестве второго параметра по ссылке какую-то структуру средствами языка MQL4, которая имела бы размер 318 байт. Наиболее удобным будет использовать для этого массив типа int, который имел бы размер не менее требуемого. Делим 318 на 4 (внутреннее представление типа int - 4 байта), получаем 79.5, округляем до ближайшего верхнего целого - значит нам нужен массив с 80 элементами. Сам импорт функции будет выглядеть теперь так: #import "kernel32.dll" int FindFirstFileA(string path, int & answer[]); #import Здесь используется вариант функции с буквой A в конце имени FindFirstFileA() для ANSI - кодировки. Массив answer передается по ссылке и служит для заполнения структурой WIN32_FIND_DATA. Пример вызова: int win32_DATA[80]; int handle = FindFirstFileA(TerminalPath() + "\experts\*.mq4",win32_DATA); Функции FindNextFileA() и FindClose()Функция FindNextFileA() получает в качестве первого параметра handle файла, полученного предварительно функцией FindFirstFileA() или другим более ранним вызовом FindNextFileA(). Второй параметр точно такой же. Функция FindClose() просто закрывает поиск. Поэтому полный вид импорта данных функций будет выглядеть таким образом: #import "kernel32.dll" int FindFirstFileA(string path, int & answer[]); bool FindNextFileA(int handle, int & answer[]); bool FindClose(int handle); #import Осталось только научиться извлекать имя файла, записанного в массив answer[]. Получение имени файлаИмя файла содержится в массиве начиная с 45-го и заканчивая 304 байтом включительно. Тип int содержит 4 байта, поэтому каждый элемент массива содержит как бы 4 символа, если представлять весь массив наполненным символами. Поэтому, чтобы обратиться к первому символу в имение файла, нам необходимо пропустить 44/4 = 11 элементов массива answer[]. Имя файла находится в цепочке из 65(260/4=65) элементов массива, начиная с элемента answer[11] (индексация начинается с нуля) и заканчивая элементом answer[76]. Таким образом, мы можем получать из массива answer[] имя файла блоками по 4 символа в каждом. Число int представляет из себя последовательность из 32 бит, эти 32 бита представляют из себя 4 блока по 8 бит в каждом.
Самый младший байт находится справа, самый старший байт находится слева. Биты пронумерованы в порядке возрастания, то есть, биты с 1-по по 8-ой составляют из себя младший байт. Мы можем извлечь нужные нам байты с помощью битовых операций. Чтобы получить значение младшего байта, нам необходимо заполнить нулевыми значениями все биты с 9-го по 32. Делается это логической операцией И. int a = 234565; int b = a & 0x000000FF; Здесь 0x000000FF - это 32-битовое целое число, у которого все разряды начиная с 9-го имеют нулевое значение, а разряды с первого по восьмой имеют значение единица. Таким образом, полученное число b будет содержать только один (младший) байт от числа a. Полученный байт (код символа) мы превратим в односимвольную строку с помощью функции CharToStr(). Первый символ мы получили, как получить следующий? Очень просто, производим побитовый сдвиг вправо на 8 бит и второй бит становится на место младшего бита. Затем применяем уже знакомую операцию логического И. int a = 234565; int b = (a >>8) & 0x000000FF; Как вы уже догадались, третий по старшинству байт мы получим сдивгом на 16 бит, а самый старший сдвигом на 24 бита. Таким образом, из одного элемента массива типа int мы можем извлечь 4 символа. Вот как выглядит получение 4-х первых символов в имени файла из массива answer[]: string text=""; int pos = 11; int curr = answer[pos]; { text = text + CharToStr(curr & 0x000000FF) +CharToStr(curr >> 8 & 0x000000FF) +CharToStr(curr >> 16 & 0x000000FF) +CharToStr(curr >> 24 & 0x000000FF); } Print("text = ", text); Создадим отдельную функцию, которая возвращает текстовую строку из переданного массива с именем buffer. //+------------------------------------------------------------------+ //| считать текст из буфера | //+------------------------------------------------------------------+ string bufferToString(int buffer[]) { string text=""; int pos = 10; for (int i=0; i<64; i++) { pos++; int curr = buffer[pos]; text = text + CharToStr(curr & 0x000000FF) +CharToStr(curr >> 8 & 0x000000FF) +CharToStr(curr >> 16 & 0x000000FF) +CharToStr(curr >> 24 & 0x000000FF); } return (text); } Задача по получению имени файла из структуры решена. Получение списка всех экспертов с исходным кодом
|
![]() Файловые операции через WinAPI
Исполнительная среда MQL4 основана на концепции безопасной "песочницы": чтение и запись средствами языка разрешены только в определенных папках. Это защищает пользователя MetaTrader 4 от потенциальной опасности испортить важные данные на жестком диске компьютера. Но иногда все же бывает необходимость покинуть безопасную зону. Как это сделать легко и правильно - об этом статья. |
![]() Построение горизонтальных уровней пробития при помощи фракталов
В статье описывается создание индикатора, который отображает уровни поддержки/сопротивления на основе фракталов вверх и вниз. |
| Предыдущая | Следующая |
А что? ни у кого не было такого, что не читается первый файл в папке???
Код скопирован копи-паст, и единственное для удобства заменен принт на алерт.
этот:
int handle = FindFirstFileA(TerminalPath() + "\experts\*.mq4",win32_DATA); Print(bufferToString(win32_DATA)); ArrayInitialize(win32_DATA,0); while (FindNextFileA(handle,win32_DATA)) { Alert(bufferToString(win32_DATA)); ArrayInitialize(win32_DATA,0); }
ага... в принтах таки есть первый файл!
оно и правильно, его печаиает "первый" принт, а в алерте уже со второго до последнего.
*
блин! а как же сделать что-б за цикл алерт показал все файлы???
В совете заменить первый принт на алерт не нуждаюсь... ;)))
В этой строке опечатка: "Оказывается, что FILETIME состоит из двух DWORD, а значит DWORD - это 8 байт. Запишем все это в таблицу:"
Правильно: "Оказывается, что FILETIME состоит из двух DWORD, а значит FILETIME - это 8 байт. Запишем все это в таблицу:"
Хороший пример того, как средствами MQL4 передавать во внешнюю DLL указатель на структуру.
В этой строке опечатка: "Оказывается, что FILETIME состоит из двух DWORD, а значит DWORD - это 8 байт. Запишем все это в таблицу:"
Правильно: "Оказывается, что FILETIME состоит из двух DWORD, а значит FILETIME - это 8 байт. Запишем все это в таблицу:"