Хранение пользовательских типов данных. Часть 3.1. Хранение в файлах.
Как и было обещано в предыдущей части, начинаем разбор методов хранения данных. Уже с примерами
Сначала рассмотрим "файловый" вариант. Для хранения простых данных можно использовать обычные текстовые файлы, откуда все по мере надобности и читать. Если данные можно "сгруппировать", и глубина вложения - всего один уровень, то уже в игру вступают ini-файлы. Если же данные сложноструктурированы, то xml в помощь и масса головной боли по их обработке.
Проблема файлового хранения состоит в месте хранения этих файлов. Предполагаем, что файлы должны меняться, то есть пользователь с правами User гарантированно должен иметь к ним полный доступ. Хранить их в %temp% нереально - это чуть ли не первый каталог, который чистят при проблемах работы AutoCAD. В "Мои документы"? Неконтролируемо появляющиеся файлы в личном каталоге - прекрасный повод для паники Для хранения таких файлов придется создавать каталог в %UserProfile% (например, c:\documents and settings\user\Application Data\), и внутри него уже структурировать все хранимые файлы. Я говорю о данных, имеющих отношение только к конкретному пользователю!
В реестре данные хранить тоже можно и, как правило, нужно. Ветка HKEY_CURRENT_USER, как правило, полностью доступна для текущего пользователя. Создается свой подключ в Software и творим там чего хотим
Каждый из методов имеет свои плюсы и минусы. Основной плюс файлового подхода - как правило, все видно и видно сразу. И это идеальное средство, если данные должны храниться на сервере. Основной плюс реестра - скорость доступа. Ну и дополнительно - файлы, как правило, пользователи уровня "обезьяна с гранатой" рано или поздно находят, а вот в реестр лезут уже совсем отмороженные товарищи
И вот еще... Данные могут быть "общими" для всех профилей AutoCAD - как имеющимися, так и вновь создаваемыми. А могут быть и "индивидуальными". Через файлы подобное решить проблематично, а вот реестровый подход такое позволяет.
Для примера рассмотрим следующую достаточно простую задачу: есть список системных переменных, которые надо устанавливать в любом файле в определенные значения. Для упрощения задачи возьмем, пожалуй, elevation, blipmode, filedia, cmddia, attdia, attreq, insbase. Этого достаточно
Сначала сформируем список этих переменных со значениями, которые нравятся разработчику:
1 2 3 4 5 6 7 8 9 | (setq *lispru-sysvar-list* '(("elevation" . 0.0) ("blipmode" . 0) ("filedia" . 1) ("cmddia" . 1) ("attdia" . 1) ("attreq" . 1) ("insbase" 0.0 0.0 0.0) ) ) ;_ end of setq |
Код здесь.
Теперь начинается веселье Сначала разберем файловый подход. Первым на очереди "простые файлы", не-ini. С них и начнем.
Да вот беда - прежде чем начинать писать код, необходимо все же определиться с местом хранения файлов. В %AppData% создадим свой каталог LispRu, в котором будет еще один подкаталог - Datas, и уже в нем будем хранить все данные.
%AppData% получить просто - (getenv "APPDATA"), а дальше? Создать "одним движением" каталог вида %AppData%\LispRu\Datas средствами LISP невозможно: vl-mkdir не умеет создавать многоуровневые каталоги, а привлекать ради одной-единственной задачи ObjectARX или .NET не очень разумно. Поэтому пишем отдельную функцию, создающую каталог любого уровня вложенности:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | (defun _lispru-dir-create (path) ;| * Гарантированное создание каталога. * Параметры вызова: path создаваемый каталог |; (setq path (vl-string-right-trim "\" path)) (cond ((vl-file-directory-p path) path) (t (vl-mkdir (strcat (_lispru-dir-create (vl-filename-directory path)) "\" (vl-filename-base path) (cond ((vl-filename-extension path)) (t "") ) ;_ end of cond ) ;_ end of strcat ) ;_ end of vl-mkdir (if (vl-file-directory-p path) path ) ;_ end of if ) ) ;_ end of cond ) ;_ end of defun |
Теперь уже можно создавать глобальную (или "суперглобальную" ;)) переменную файла с настройками:
Для "не-ini" файла:
1 2 3 4 5 6 | (setq *lispru-sysvar-datfile* (strcat (_lispru-dir-create (strcat (vl-string-right-trim "\" (getenv "APPDATA")) "\\LispRu\\Datas")) "\\sysvar.dat" ) ;_ end of strcat ) ;_ end of setq |
Для "ini"-файла:
1 2 3 4 5 6 | (setq *lispru-sysvar-inifile* (strcat (_lispru-dir-create (strcat (vl-string-right-trim "\" (getenv "APPDATA")) "\\LispRu\\Datas")) "\\sysvar.ini" ) ;_ end of strcat ) ;_ end of setq |
"xml"-файлы рассматривать не будем, там слишком сложный механизм работы с ними. Если есть желание - у себя на блоге я разбирал эти моменты.
Дальше надо подключить фантазию и подумать: допустим, файл dwg открывается или создается. Значит, эти системные переменные должны получить устанавливаемые значения. А для этого данные должны быть прочитаны. А если их еще не записывали? А потом вдруг пользователь понимает, что текущие значения хранимых переменных его более чем устраивают и надо их сохранить? А если надо восстановить те, которые устанавливал разработчик?
Вот и получается, что необходимы функции:
- Сохранение списка системных переменных с их значениями "по умолчанию" в .dat или .ini файл. При этом функция должна сразу создавать .dat / .ini файл
- Чтение и установка прочитанных значений системных переменных
- Сохранение текущих значений в dat / ini файл
Приступим?
Итак, сначала dat-файлы. Проще всего прямо в указанном файле хранить нечто типа
1 2 3 4 5 6 7 | ("elevation" . 0.0) ("blipmode" . 0) ("filedia" . 1) ("cmddia" . 1) ("attdia" . 1) ("attreq" . 1) ("insbase" 0.0 0.0 0.0) |
Потом открывать файл и выполнять оттуда последовательное чтение данных.
Напишем функцию установки системных переменных. Сделаем ее универсальной. То есть она должна "сбрасывать" значения в режим "по умолчанию" и тут же устанавливать соответствующие системные переменные, а также должна уметь читать записанные ранее (и, возможно, отличающиеся от "стандартных") значения и устанавливать уже их. Есть несколько вариантов решения.
Первое, "в лоб":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | (defun _lispru-sysvar-dat-set (default / handle str) ;| * Установка системных переменных *lispru-sysvar-list* в их значения "по * умолчанию". Установленные значения записываются в файл *lispru-sysvar-datfile* * Параметры вызова default устанавливать значения "по умолчанию" (t) или читать из файла *lispru-sysvar-datfile* и устанавливать записанные |; (if default (progn (setq handle (open *lispru-sysvar-datfile* "w")) (foreach item *lispru-sysvar-list* (vl-catch-all-apply (function (lambda () (setvar (car item) (cdr item)) ) ;_ end of lambda ) ;_ end of function ) ;_ end of vl-catch-all-apply (write-line (strcat "("" (car item) "" " (if (listp (cdr item)) "" ". " ) ;_ end of if (vl-string-trim "()" (vl-princ-to-string (cdr item))) ")" ) ;_ end of strcat handle ) ;_ end of write-line ) ;_ end of foreach (close handle) ) ;_ end of progn (if (not (findfile *lispru-sysvar-datfile*)) (_lispru-sysvar-set t) (progn (setq handle (open *lispru-sysvar-datfile* "r")) (while (setq str (read-line handle)) (vl-catch-all-apply (function (lambda () (setq str (read str)) (setvar (car str) (cdr str)) ) ;_ end of lambda ) ;_ end of function ) ;_ end of vl-catch-all-apply ) ;_ end of while (close handle) ) ;_ end of progn ) ;_ end of if ) ;_ end of if ) ;_ end of defun |
Код здесь.
Если убрать постоянные проверки на ошибки (вызовы vl-catch-*), то все станет достаточно просто. Оставлю эту задачку, неохота ее разбирать
По идее можно сделать дополнительно информационное окошко о том, какие значения не удалось установить, но я предполагаю, что эти файлы все же не будут правиться вручную
Можно разработать другой вариант той же функции, работающий по другому алгоритму, без рекурсии: сначала анализируется параметр default, и, если он t, то выполняется запись в файл *lispru-sysvar-datfile*, потом файл открывается на чтение и уже независимо ни от чего оттуда считываются пары переменная - значение и устанавливаются системные переменные.
Осталось написать функцию чтения имеющихся значений и записи их в *lispru-sysvar-datfile*:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | (defun _lispru-sysvar-dat-save (/ handle) ;| * Чтение установленных в текущем файле значений системных переменных * из *lispru-sysvar-list* и запись их в *lispru-sysvar-datfile* |; (setq handle (open *lispru-sysvar-datfile* "w")) (foreach item *lispru-sysvar-list* (write-line (strcat "("" (car item) "" " (if (listp (cdr item)) "" ". " ) ;_ end of if (vl-string-trim "()" (vl-princ-to-string (cdr item))) ")" ) ;_ end of strcat handle ) ;_ end of write-line ) ;_ end of foreach (close handle) ) ;_ end of defun |
Код здесь.
Теперь, комбинируя эти функции, можно написать лисп, который при старте нового файла сразу будет устанавливать эти системные переменные в значения "по умолчанию".
Небольшое резюме... Используя такой метод - прямого сохранения списков в файл - можно решать весьма серьезные задачи. Но какова информативность таких файлов? Если что-то "упало", и для восстановления потребуется такие файлы править вручную, то это будет ад. Такие файлы никак не задокументируешь, изменение алгоритма работы превращается в пытку. На каждый "чих" придется делать отдельный файл, не забывая его "закрывать".
В общем, лично я не в восторге от такого бардака. Изыскивая пути "ухода", я сначала нашел ini-файлы, потом реестр, и, в последнюю очередь - xml-файлы.
Ффуууууххх, еле дописал. Теперь точно надолго замолчу
---
Исходные коды, упомянутые на странице:
Имя файла | Выполняемые действия |
lispru-sysvar-list.lsp | Создание *lispru-susvar-list* |
_lispru-dir-create.lsp | Функция практически гарантированного создания каталога |
lispru-sysvar-datfile.lsp | Создание *lispru-sysvar-datfile* |
lispru-sysvar-inifile.lsp | Создание *lispru-sysvar-inifile* |
_lispru-sysvar-dat-set.lsp | Функция установки системных переменных |
_lispru-sysvar-dat-save.lsp | Функция сохранения текущих значений системных переменных |
Комментарии
Есть 1 комментарий к “Хранение пользовательских типов данных. Часть 3.1. Хранение в файлах.”Трэкбэки
Узнайте, что другие говорят про эту заметку...[...] разговор, начатый здесь и продолженный в части 3.1. Хранение в файлах списков, которые потом читаются и [...]