Хранение пользовательских типов данных. Часть 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. Этого достаточно
Сначала сформируем список этих переменных со значениями, которые нравятся разработчику:
("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 не очень разумно. Поэтому пишем отдельную функцию, создающую каталог любого уровня вложенности:
;|
* Гарантированное создание каталога.
* Параметры вызова:
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” файла:
(strcat
(_lispru-dir-create (strcat (vl-string-right-trim "\\" (getenv "APPDATA")) "\\LispRu\\Datas"))
"\\sysvar.dat"
) ;_ end of strcat
) ;_ end of setq
Для “ini”-файла:
(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-файлы. Проще всего прямо в указанном файле хранить нечто типа
("blipmode" . 0)
("filedia" . 1)
("cmddia" . 1)
("attdia" . 1)
("attreq" . 1)
("insbase" 0.0 0.0 0.0)
Потом открывать файл и выполнять оттуда последовательное чтение данных.
Напишем функцию установки системных переменных. Сделаем ее универсальной. То есть она должна “сбрасывать” значения в режим “по умолчанию” и тут же устанавливать соответствующие системные переменные, а также должна уметь читать записанные ранее (и, возможно, отличающиеся от “стандартных”) значения и устанавливать уже их. Есть несколько вариантов решения.
Первое, “в лоб”:
;|
* Установка системных переменных *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*:
;|
* Чтение установленных в текущем файле значений системных переменных
* из *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 | Функция сохранения текущих значений системных переменных |
Материалы для проектирования, работы и разработки (и не только в AutoCAD)
Комментарии
Есть 1 комментарий к “Хранение пользовательских типов данных. Часть 3.1. Хранение в файлах.”Трэкбэки
Узнайте, что другие говорят про эту заметку...[...] разговор, начатый здесь и продолженный в части 3.1. Хранение в файлах списков, которые потом читаются и [...]