Перейти к содержимому
Ktlo

Хранение настроек программы 1.0

Рекомендуемые сообщения

Представьте, что вы написали программу, и вам нужно, чтобы некоторые настройки этой программы сохранялись на диск и считывались, да так, чтобы сам файл настроек был читаем, содержал комментарии, легко редактируем с помощью текстовых редакторов и сохранял целостность самого файла после перезаписи (не путал строки). Всё выше перечисленное я добавил в эту небольшую библиотеку, и честно сказать результатом доволен.  :D
 
Скачать можно коммандой с pastebin:
pastebin get qDRfwGX3 /libs/settings.lua

 
Функции

 

open(path: string):object or nil, string — основная функция для загрузки настроек из файла. Принимает значение пути до файла, как строку и возвращает объект настроек. Если файл не удаётся открыть, то возвращает nil и причину неудачи.

 

convert(path: string, table: table or nil):object — эта функция конвертирует обычную таблицу Lua в объект настроек. Первый аргумент нужен для последующего сохранения по этому пути второй необязатильный, для тех случаев, когда вы создаёте новый чистый объект настроек.

 

decode(string: string, line: number):true, string, value, string — нужна для сугубо конкретных случаев, принимает строку типа " key =    365n #comment" и переводит в значения Lua. Сначала возвращает значение успеха, затем ключ, значение, либо при неудаче строку с ошибкой и nil. Четвёртое же возвращаемое значение комментарий строки если, есть. В данном примере вернёт true"key", 365, "comment".

code(key: string, value: value, comment: string or nil, hex: boolean):true, string or nil, string — делает обратное действие функции decode(). Аргумент hex влияет на то, в какую систему счисления будут кодироваться числа. Если true то в шестнадцатиричную, иначе в десятичную.

 

 

Формат файла

 

В итоге вы можете получить файл с настройками, который может хранить любые значения Lua кроме таблиц (массивы хранить может), функций, потоков и userdata разумеется. При записи, вы можете увидеть нечто подобное:

# Комментарий первой строки здесь можно чего-нибудь пояснить

string = "#1 строка \n #2 \"строка\"" #Такой комментарий спокойно работает и не конфликтует со знаком # в строке
  ч и с л о=FFFx #Число в шестнадцатеричном формате, не целое.
string  =   'строка' #В данном случае одинаковый ключ работать будет, если вы будете обращаться к строке.
число = 23.842
число 2 = 23.842n
boolean = FaLsE #false
boolean_ = true #true
boolean2 = 1b #true
hex = 0x12Acd

#Ниже можно увидеть массив
array = {"строка", tRue, false, {34, {}, abcdefx}} #Массив в массиве, да, да так можно)))
ничего = #агась, это работает)))

А теперь поподробнее о каждом типе данных (жирным шрифтом выделены отличительные признаки):

  • 34.028n, 2342.058 — десятичные числа, они не могут иметь дробную часть больше 3-ёх знаков, т.к. зачем?
  • f30acdx — шестнадцатеричное число 15928.031. Оно также может хранить дробную часть, из-за чего его просто так расшифровать не выйдет.
  • "строка \n\t\b#\"'" — обычная строка Lua, загружается с помощью функции load(), символ # не конфликтует с комментарием строки.
  • 0b и 1b, true, false — тип данных boolean. true и false можно записывать, не учитывая регистр.
  • {"36", 1b; {}, 34.028} — массив, хранит все типы данных, и отделяет их знаками , и ;.
  • 0xabСdef — обычное шестнадцатеричное число. При сохранении в таком формате не записывается, но спокойно считывается, регистр цифр не учитывается.
  • — ничего, то-есть nil, (ключ = ) - означает nil.

Также у любой строки может быть комментарий, а может и не быть. С помощью него вы можете отделять разные части настроек. После перезаписи комментарии сохраняются.

#комментарий строки

 

 

Методы

 

object:save(hex: nil or boolean) — сохраняет настройки в файл. Аргумент hex влияет на то, в какую систему счисления будет сохранятся число.

 

object:totable():table — конвертирует настройки в таблицу.

 

object:setLine(key: string or nil, value: value, line: nil or number) — первый аргумент - ключ, второй - значение ключа. Их можно не указывать, если хотите сделать пустую строку. Третий аргумент - номер строки, если не указывается, создаётся новая строка.

 

object:getLine(line: number):string, value, string — возвращает ключ, если есть, значение, если есть и комментарий, если есть.

 

object:set(key: string, value: value) — устанавливает значение ключу. Ключ должен быть указан обязательно. Если не находит такой ключ, создаёт новый на новой строке.

 

object:get(key: string):value — возвращает значение ключа.

 

object:setComment(line: number, comment: nil or value) — устанавливает комментарий для строки. Если комментарий не указан, просто удаляет его.

 

object:getComment(line: number):string or nil — возвращает комментарий, если есть.

 

 

Свойства

 

object.path: string — путь до файла настроек.

 

object.n: number — количество линий. Вы не можете изменить значение этого свойства. Если вы всё-таки это сделали примените где нибудь это выражение (#object) и значение восстановиться.

 

object.keys: table — здесь хранятся ключи.

 

object.valuestable — здесь хранятся значения.

 

object.commentstable — здесь хранятся комментарии.

 

 

Операторы (взаимодействие)

 

#object — переназначен на пересчёт количества строк. Отсекает все пустые строки в конце.

 

pairs(object):function — переназначен на возвращение итератора, который проходит по всем существующем ключам и их значениям, также по комментариям, если присутствуют.

 

ipairs(object):function — переназначен на возвращение итератора, который проходит по всем строкам и периодически возвращает номер строки, ключ, значение, комментарий.

 

Индексирование — удобная альтернатива методам object:set() и object:get(), следует понимать, что индексировать некоторые поля не получится, т.к. они заняты методами и свойствами, но вы всегда можете использовать методы, прописанные выше.

 

tostring(object):string — возвращает строку по типу: "settings: (путь_до_файла)"

 

 

Пример использования

 

Напишем программу с некоторыми параметрами в файле:

Программа:

 

-- Для начала подключим библиотеку
local settings = dofile("settings.lua") --[[Разумеется, если вы запихнули
	библиотеку в папку libs, можно использовать другую комманду:
local settings = require("settings")	
]]

-- Далее попытаемся открыть этот файл по пути:
local conf, err = settings.open("conf.cfg") --[[Функция вернёт
	объект настроек (тип таблица) или nil и ошибку.
	Также можно использовать:
local conf = assert(settings.open("conf.cfg"))
	для выдачи ошибки в программе, в случае недоступности
	файла.
]]

-- Создадим ветвление
if conf then
	print("Настройки успешно загружены")
else
	print("Ошибка! "..err)
	print("Создание нового файла настроек...")
	conf = settings.convert("conf.cfg") --[[Конвертор таблицы Lua в объект настроек, принимает
		путь до файла и таблицу для конвертирования, без указания таблицы создаёт новую.
		Пример конвертации:
	conf = settings.convert("conf.cfg", {
		key = 2;
		key2 = { 1, " ", true }
	})
		Минус этого способа, в том, что ключи в файле будут записаны случайно.
		Мы избежим этого построчно записывая файл.
	]]
	conf:setComment(1, "Это файл настроек :)") -- Установим в первую строку комментарий. Все аргументы важны в данном случае.
	conf:set("format", "png") --[[Устанавливаем ключу "format" значение "png",
		т. к. ключа "format" ещё не существует, он появится на новой строке.
		Есть альтернативные методы заполнения файла:
	conf.format = "png"
		Этот способ плох тем, что некоторые поля зарезервированы для методов
		и свойств, в остальном идентичен методу conf:set().
	]]
	conf:setLine("позиция", { 3, 6 }) --[[Устанавливаем новую линию с ключом "позиция"
		и массивом. Можно указать номер строки для установки отдельных строк.
	]]
	conf:setComment(conf.n, "позиция картинки") -- Просто комментарий для последней строки
	conf["быстрая загрузка"] = nil --[[Вы работаете не с обычной таблицей, поэтому
		такая форма записи установит ключу "быстрая загрузка" значение nil.
	]]
	conf:save() -- Сохраним наш файл
	--[[
	conf:save(true)
		Аргумент true говорит о том, что числа будут переделаны
		в особый шестнадцатиричный формат (достаточно нечитаемый).
	]]
end

-- Что вы можете сделать с файлом:
local conf_table = conf:totable() --[[Конвертирует объект в новую
	таблицу, возможно вам будет удобнее работать с ней
]]

local pos = conf["позиция"] -- Получаем массив с позицией

for n_line, key, value, comment in ipairs(conf) do
	print(n_line, key, value, comment) -- Проходим по всем строкам файла настроек и выводим их.
end
-- Все остальные фичи вы сможете найти в разделах темы 

 

 

 

Файл настроек, созданный программой выше:

 

#Это файл настроек 
format = "png"
позиция = {3, 6}  #позиция картинки
быстрая загрузка =  

Скопировал, как есть, ничего не менял.

 

 

 

 

Старые версии (вдруг кому надо...)

 

 

Библиотека ещё будет дорабатываться, в соответствии с вашими пожеланиями. Надеюсь оцените  :).

Изменено пользователем Ktlo

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Лучше бы сделал json парсер.

На самом деле и такое планирую, в основном для записи команд minecraft'а, этот способ нужен для построчного считывания, для того, чтобы не хранить весь текст файла сразу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Лучше бы сделал json парсер.

Один из тех моментов, когда хочется иметь дизлайки на форуме :(

И, к слову, парсер конфигов типа OC: https://github.com/OpenPrograms/EvaKnievel-Programs/tree/master/applicationconf

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Немного обновил библиотеку.

Изменения

  1. Устранил баг неправильной расшифровки массива.
  2. Добавил прямое индексирование для удобства.
  3. После некоторых раздумий понял, что всякие 0b и 34n будут не удобны пользователям, и переделал теперь можно использовать привычные false и 34.
  4. Добавил расшифровку обычного шестнадцатеричного числа (0x1234f56 например). Думаю, это будет удобнее для указания цветов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Фактически то же самое, что и эти два варианта реализации

 

Не, я так не думаю. В моём способе реализации сохранятся целостность строк (строки не путаются местами), существуют комментарии, и типы данных. А так же маленькая фича, которую я забыл упомянуть: строка 

key = "string" #comment

 идентично расшифровывается, как и  

   key="string"   #comment

. Всё это по-моему очень важно для наглядного способа заполнения файла настроек пользователем.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

хочется иметь дизлайки на форуме

Они есть, но оно переключается типа лайк/не лайк (и тогда количество плюсов и минусов),а есть просто кнопка нравится 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Они есть, но оно переключается типа лайк/не лайк (и тогда количество плюсов и минусов),а есть просто кнопка нравится 

Да знаю я. И даже знаю, почему система репутации только в плюс идёт здесь. Потому, что написать нормально и удобно не могут разрабы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Обновление 0.3


  • Исправлен метод object:set(), а то он после обновления 0.2 вообще перестал работать.
  • Добавлен особый вывод для стандартной функции tostring().
  • Скоро добавлю пример пользования библиотекой.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Обновление 0.4


  • Исправлена автоматическая корректировка строк у методов object:set() и object:setLine().
  • Добавил пример использования библиотеки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Обновление 1.0

  • Во-первых версия 1.0! Думаю пора, т. к. нечего тут уже добавлять, только исправлять, если есть баги (надеюсь они кончились).
  • Добавлена защита от дурака. Теперь нельзя так:
    object.path = {} -- Передаём параметру path значение таблицы (должна быть строка).
    --Такое действие выдаст вам ошибку.
    
    object.n = 6 -- Значение присваивать этому параметру нельзя.
    --Выдаст ошибку, object.n теперь только читаемое
    
  • Исправлен небольшой баг, связанный с чтением массива. Теперь, если при пользовательском вводе допущена ошибка в одном из элементов массива, то массив всё равно прочитается, а ошибочный элемент будет заменён строкой с ошибкой, с номером элемента. Пример ошибки: "#5: couldn't read unregistered type".

 

Теперь думаю можно выкладывать в репозиторий...

Изменено пользователем Ktlo

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

Ktlo  а  при помощи этой библиотеки сохранить в файле  многомерный массив элементами которого тоже являются массивы а потом прочитать его обратно из файла в тот же массив можно?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ktlo  а  при помощи этой библиотеки сохранить в файле  многомерный массив элементами которого тоже являются массивы а потом прочитать его обратно из файла в тот же массив можно?

Да, можно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...