Начнём с вводных. Мы разрабатываем программу, которая будет сохранять свои данные в файл, и при этом…
- будет расширяться, и существенно (отпадают уровни и сохранения большинства игр: после пары патчей бросаем игру и пишем новую);
- тем не менее программа не рассчитывает на то, чтобы быть стандартом (отпадает LibreOffice). То есть по формату сохранения она должна быть совместима только с собой-старой и собой-новой;
- все её данные надо держать одномоментно в памяти; СУБД типа SQLite не даёт каких-то преимуществ (отпадают базы переписки в почте или мессенджере);
- но файл сохранения будет очень велик (отпадают программы фотопроявки вроде Lightroom, где документ — это всего лишь положения сотни-другой ползунков: мелочь по сравнению с 40-мегабайтным RAW);
- нет нужды в ручной корректировке файлов (отпадает пользовательский интерфейс типа «файл конфигурации», присущий, например, серверу Apache).
Таких программ на самом деле немало. Это AutoCAD, Photoshop, Microsoft Office (будем честными: даже пытаясь протащить его через ISO, «мелкомягкие» рассчитывали, что он будет совместим в первую очередь с самим собой).
И для простоты добавим ещё одно требование, которое отбросит все три этих программы, но довольно реалистичное (ему отвечают Windows 10 и куча программ помельче).
- программа разрабатывается по непрерывной схеме, так что нет денежных барьеров обновляться, а достаточно старые версии программы по факту неподдерживаемые.
Я долго искал программу, которая отвечает всем этим требованиям, и наконец нашёл: Inkscape. Несмотря на то, что её файлы основаны на стандарте SVG, половина всей информации лежит в расширениях SVG — пространствах имён «sodipodi» и «inkscape».
В первой части постараюсь обойтись без программирования, зато объясню парочку важных концепций.
Собственно, у нас есть такие возможные форматы для хранения информации.
Формат первый. XML
Из форматов, доступных из коробки, самый распиаренный — XML. Если правильно писать код разбора, часть задач по обратной совместимости решаются автоматически: старая программа пропустит все лишние тэги. Остаётся сделать, чтобы новая читала документы, созданные старой — и дело в шляпе.
[+] Много стандартного инструментария.
[+] Оптимальный выбор, если мы храним размеченный текст.
[±] Можно (хотя и тяжело) редактировать файл вручную.
[−] Разбор может занимать много времени — от 20 до 95% всего времени загрузки. Эту неоптимальность даже обозвали «налогом на угловые скобки». По опыту загрузки (не полной) XLSX собственными силами: раззиповка — 0,3 с, разбор XML — 1,1 с, остальное — 0,6 с.
[−] Программировать своими силами очень опасно, возможны незаметные ошибки.
[−] Зачастую избыточен.
[−] Если в программе есть огромные массивы из малюсеньких объектов (звуковые волны, картинки, матрицы чисел), их приходится сохранять не-XML’ными мерами. Посмотрите, например, как хранятся кривые в SVG:
<polygon id="Star-1" stroke="#979797" stroke-width="3" fill="#F8E81C"
points="99 154 40 185 51 119 4 73 69 64 99 3 128 64 194 73 147 119 158 185 " />
Будьте готовы к тому, что XML будет громадный, подчас больше, чем ваш документ занимает в памяти. Хотя не стоит так бояться больших файлов: обещаю, мы не будем грузить такую громадину в память.
Каким образом? Я знаю четыре подхода к чтению XML: на callback’ах (SAX), на потоках (StAX), на сопрограммах и DOM. А также два подхода к записи XML: прямая запись тэг за тэгом и DOM.
Чтение на callback’ах даёт сложный и путаный код, напоминающий конечный автомат. DOM расходует памяти в разы больше, чем структура предметной области. Сопрограммы — сложное дело и есть не везде. Так что наш выбор — чтение на потоках, запись тэг за тэгом. В обоих случаях расходуется пренебрежимо мало памяти, так что автоматически выполняется моё обещание не грузить громадину.
На языках, где мы управляем памятью вручную (Си), у потокового парсера есть ещё одна полезная черта. Управление памятью действует как унитазный бачок — потихоньку заполняется, мгновенно сливается — потому в самых быстрых из них действует собственное управление памятью. К сожалению, не получается добиться столь впечатляющей скорости, как в DOM (прежде чем выйти из функции getThing, надо «подобрать концы»). Так что у меня лично получилось (на Си++) сделать потоковый парсер в 2,5 раза медленнее, чем у рекордсмена PugiXML. Но это уже замечательный результат: научился грузить огромный XLSX втрое быстрее, чем Excel.
Формат второй. XML-лайты
«XML-лайтами» я называю языки наподобие XML, в которых единицы смысла — это строки текстового файла (иногда также агрегаты из нескольких строк). Наиболее известный — YAML.
name: Mark McGwire
accomplishment: >
Mark set a major league
home run record in 1998.
stats: |
65 Home Runs
0.278 Batting Average
Много лет назад, не накопав хорошего XML-инструментария для Delphi, за полдня я придумал XML-лайт с тупым названием Multilevel. Правда, тогда по неопытности работал только с DOM-разбором.
{ MULTILEVEL
Format=McJoy
Noise=10
{ AXES
{ AXIS
Index=2
Name=Тормоз
Type=Analog
Neutral=65535
Threshold=25
}
{ AXIS
Index=3
Type=Analog
Neutral=32895
Threshold=25
}
}
}
[+] Достаточно быстры.
[+] Редактируются вручную.
[±] Думаю, можно подобрать подходящий формат для огромных массивов из малюсеньких объектов.
[±] Легко запрограммировать своими силами.
[−] Даже если формат известный, вроде YAML, велика вероятность, что найдутся только DOM-подобные механизмы.
[−] Малопригодны для размеченного текста (и то придётся специально продумывать подходящий формат).
Формат третий. Двоичные XML-подобные коды
XML — формат текстовый. Но ничего не стоит перевести его в двоичный вид. Вот наобум придуманный XML-подобный двоичный формат (порядок байтов для наглядности взят Motorola):
89 AB CD EF ; заголовок
00 01 ; блок с кодом 1
00 00 00 03 ; длина блока (=3)
AA BB CC ; данные
80 02 ; каталог с кодом 2 (бит 80 00 означает «подкаталог», поля длины нет)
00 03 ; блок с кодом 3
00 00 00 08 ; длина блока (=8)
AA BB CC DD EE FF 00 11 ; данные
00 00 ; конец подкаталога
На XML бы это выглядело так.
<data>
<block opcode="1">AA BB CC</block>
<dir opcode="2">
<block opcode="3">AA BB CC DD EE FF 00 11</block>
</dir>
</data>
Я не открываю Америку, формат BIFF из Microsoft Office существует лет тридцать. Компания облажалась в двух местах: 1) Формат не иерархический, что сильно мешает дробить блоки; 2) блок ограничен 64 килобайтами, что решается вторым, третьим и т.д. блоком CONTINUE.
Недавно для целей медиаконтейнера Matroška сделали язык EBML — тот же XML, но двоичный. Конкретно с ним не знаком.
[+] Крайне быстры.
[+] Тэги, скорее всего, будут достаточно коротки, чтобы размеченный текст отнял даже меньше места в файле, чем в памяти.
[±] Легко запрограммировать своими силами.
[±] Огромные массивы из малюсеньких объектов можно сохранять как есть, в двоичном виде.
[−] Ручному редактированию не подлежат, для отладки потребуется программа-дампер.
Прочие форматы (и их недостатки)
Эти форматы лучше даже не рассматривать.
Текстовый или двоичный формат без расширяемой иерархической структуры нечего и придумывать. Раз мы хотим расширяемость — программа скоро вылезет из него, как из детской кроватки.
Любые сериализации через рефлексию (встроенные в Java, Delphi, C#). Годятся только для задач, где большая совместимость не нужна (уровни и сохранения игр, файлы настроек). Если вам удастся задействовать рефлексию и сэкономить строки — вы молодец, но программа не должна полагаться только на неё.
UPD. Чем плоха рефлексия? Костылями при серьёзных изменениях структуры файла.
SQLite продвигает свой формат БД как формат документа. По-моему, из-за сложностей программирования с ним имеет смысл работать именно как с СУБД.
JSON несколько проще в программировании, чем XML, но всё равно сложен. Плюс затруднено потоковое считывание: JSON различает объекты и массивы, и синтаксис JS говорит, что от перестановки атрибутов результат не меняется.
{ // объект
"address": { // объект
"streetAddress": "Московское ш., 101, кв.101",
"city": "Ленинград",
"postalCode": 101101
},
"phoneNumbers": [ // массив
"812 123-1234",
"916 123-4567"
],
"firstName": "Иван", // хотелось бы их иметь в начале
"lastName": "Иванов"
}
Хотя JSON подсказывает нам одну важную идею. А именно…
Идея 1. Или последовательность, или коллекция
Нашу жизнь очень сильно упростят два важных требования.
Любой тэг должен быть или последовательностью тэгов/блоков, или коллекцией тэгов/блоков. Объясню, что это такое.
- Последовательность — это когда каждый сын повторяется не более одного раза и знает своё место. При этом возможны необязательные или взаимоисключающие.
- В терминах регулярных выражений — что угодно без дублей и операции «повторить». Например,
AB [C] (D | [E]F) G
. - Неизвестные тэги, замеченные в последовательности, пропускаются.
- Пример: тэг
<html>
будет последовательностью из<head>
и<body>
.
- В терминах регулярных выражений — что угодно без дублей и операции «повторить». Например,
- Коллекция — это повтор тэгов из какого-то ограниченного набора, с предельно простыми правилами взаимодействия друг с другом. Что-то вроде
(A|B|C)*
.
- Неизвестные тэги, замеченные в коллекции, пропускаются или вызывают ошибку по желанию программиста.
- Пример: тэг
<body>
будет коллекцией из (почти) всех возможных тэгов HTML. Тэг<tr>
содержит только<td>
и<th>
.
Примечание. Я понимаю, что в HTML оба этих тэга, td и th, можно использовать как непарные, браузер разберётся. Но давайте будем считать, что имеем дело с XHTML и эти тэги парные.
А вот совершенно не подходит под нашу модель тэг table
. В официальной документации он описан как [caption], [title], [summary], ( col* | colgroup* ), (( [thead], [tfoot], tbody+ ) | ( tr+ ))
— не похоже ни на коллекцию, ни на последовательность. Я понимаю разработчиков: HTML-то пишется в первую очередь руками. Но в машиночитаемом формате лучше так не делать.
Делая коллекцию дочкой коллекции, надо чётко осознавать риски. Тэг table
в одном из вариантов будет коллекцией тэгов tr
. А он, в свою очередь, коллекция из td
и th
. Если вдруг, случись что, в tr
появится какой-нибудь тэг tx
, с большой вероятностью оборвётся совместимость.
Как бы мы переписали нашу несчастную таблицу под машинное сохранение?
table :== [caption] [title] [summary] [cols|colgroups] [thead] [tfoot] (tbodies|tbody)
cols :== col+
colgroups :== colgroup+
tbodies :== tbody+
thead, tfoot, tbody :== tr+
tr :== (td|th)+
Но при этом мы чётко понимаем, что внутри коллекции tbody находится коллекция tr. Но таблица — она двухмерная, и крайне мала вероятность, что внутри tr вставят что-то не являющееся клеткой таблицы — так что закрываем на это глаза.
А вот в нашем простом иерархически-двоичном формате так нельзя. Причина в том, что у тэга XML есть атрибуты, куда, если что, можно закинуть немножко информации. А у каталога двоичного формата нет ничего — потому в этот самый tr
не впишешь никаких новых данных: ни стиля, ни соответствия электронного документа и бумажного источника… Потому приходится полностью исключать коллекции в коллекциях.
thead, tfoot, tbody :== trs
trs :== tr+
tr :== tds
tds :== (td|th)+
Выносите отдельные сущности, особенно необязательные, в тэг-контейнер. Заголовок к таблице можно описать двумя способами:
<table caption="xxx" />
<table><caption>xxx</caption></table>
Второй лучше — больше места для расширения. Может, вы когда-нибудь станете оформлять части заголовка жирным и курсивом. А может, будут два заголовка (например, в начале таблицы и в продолжении, если её разорвало на две страницы). Кто знает?
Впрочем, <table class="myTable" />
вполне себе катит: ссылка на стиль — свойство, заголовок, висящий где-то на мониторе — сущность.
Идея 2. Extend, upgrade, break. Или расширить, модернизировать, порвать
Какую бы мы ни придумали конструкцию — когда-нибудь мы из неё вырастем. Пусть у нас редактор простейших уровней, например, для Super Mario. Изначально там был один «мир» (скажем, лес). Делаем так, чтобы были два, три и больше миров. Как это будет выглядеть в XML?
БЫЛО:
<tileset />
СТАЛО:
<tilesets>
<tileset />
<tileset />
<tileset />
</tilesets>
Новую программу можно научить открывать старый формат. Но старая программа не откроет нового. И тогда я предлагаю трёхстадийный «мягкий переход». Сколько длится каждая из стадий — зависит от того, насколько сложен формат, как вы заставляете пользователей обновлять программу и как вы готовы тестировать её на проектах старых версий.
(название намеренно сделано похожим на Microsoft’овское Embrace/Extend/Extinguish, да и суть та же).
Расширить: программа пишет старый формат, если в игре один мир, и в новый, если много.
Примерно через полгода-год происходит фаза Модернизировать: программа начинает писать только новый формат, всё ещё читая оба.
И наконец — может, через год-полтора, может, когда руки программиста снова дойдут до этого места и потребуется очередное расширить — даём приказ порвать: перестаём даже читать старый формат.
Идея 3. Работая с текстовыми файлами, используйте нейтральную локаль
Разрешите процитировать IT Happens:
«Странно», — подумал я, но девочка из ПФР оставила ман в сто страниц А4. Решив начать по-хорошему, я сел и стал его вдумчиво курить. Где-то на 40-й странице я наткнулся на указание сменить стандартный разделитель целой и дробной частей в винде на запятую, иначе программа не будет работать. Открыл, смотрю — ага, всё верно. Сменил на точку — программа ФОМС заработала, но перестала работать программа ПФР, выдавая всю ту же ошибку сохранения данных. Теперь пожилая женщина-кадровик обучена переключать разделитель, а я проникся небывалой нежностью к отечественным программистам.
Идея 4. Как синхронизировать версии программы у сотрудников, работающих над одним документом
Обычно это важно для внутренних утилит. Часто бывает, что несколько человек работают над одним документом разными версиями программы, и не все вовремя обновляют, отсюда мелкие потери данных. Предлагаю такой тэг.
<program name="MyProgram" savedWith="1.1.1.42" fullyCompatibleWith="1.1.1.40" partlyCompatibleWith="1.1.1.25" />
Цифру partlyCompatibleWith поднимаем до текущей, когда делаем extend или break в чём-то важном. Можно её устанавливать умно в зависимости от того, сохраняли мы в старом формате или нет. Цифру fullyCompatibleWith поднимаем, когда добавляем любой тэг.
Если файл загружен программой версии меньше 25, вывести: «Настоятельно рекомендуем обновить программу. Есть вероятность, что важные данные потеряны».
Если файл загружен программой версии меньше 40, вывести: «Рекомендуем обновить программу. Есть вероятность, что какие-то данные потеряны.»
Если файл загружен какой-то очень новой программой, чей partlyCompatibleWith больше 42, тоже можно что-то вывести.
Если библиотека разбора под вашим контролем, можно поступить и так: если хоть один тэг был прочитан кодом загрузки не полностью, вывести предупреждение. Но делается это, повторяю, на уровне библиотеки разбора.
Также в новых версиях программы можно хранить чёрный список версий, которые часто падают или портят файлы — если замечено, что savedWith в чёрном списке, тоже что-то вывести.
Комментарии (54)
trir
13.01.2020 03:53Mercury13 Автор
13.01.2020 03:53Первое — я не очень понял, но похоже на XML-лайт. Второе — сериализация через рефлексию, и, как я уже сказал, сэкономил работы рефлексией — молодец. Но, надеюсь, они позволили в сложных случаях забираться вручную в структуру тэга.
Dotarev
13.01.2020 03:53Второе — ключевая фраза «Protocol Buffers — структура данных, которая затем компилируется в классы».
Mercury13 Автор
13.01.2020 03:53Всё равно это разновидность рефлексии при компиляции. А что, разве только при выполнении она бывает? Главный недостаток рефлексии — при сложных изменениях структуры будут костыли.
hssergey
13.01.2020 03:53Но что, мало прог, где реально всё держим в памяти?
Для небольших программ, например, программ редактирования документов, вполне оправдано держать все в памяти. Но попробуйте, например, в LibreOffice открыть документ на десятки мегабайт — уже почувствуете боль. А если объем данных заранее неизвестен и может быть очень большим — тут нужны иные подходы.
Во-первых, в принципе работа с СУБД сложна, по опыту.
Достаточно будет выучить самые базовые вещи SQL, этого уже хватит для работы. Более сложные конструкции могут и не понадобиться.
Во-вторых, бывают и очень нелюбимы программистами изменения структуры файла — и на XML-то это противное дело, а на БД тем более.
Вот тут не согласен. В случае СУБД вообще нет никакой нужды лезть в структуру файла. Например, добавились новые колонки в таблицу. Один раз делаешь ALTER TABLE при миграции данных на новый формат (да, версионность нужна, куда без нее. Но даже версию можно хранить в БД в отдельной таблицу). Те места в коде, где эти колонки не используются, можно вообще не трогать, там все будет работать по-прежнему. Аналогично с добавлением новых таблиц. То есть ты в принципе не касаешься конкретной физической структуры файла, а работаешь непосредственно с данными.Mercury13 Автор
13.01.2020 03:53Тут, получается, ALTER по любому чиху, даже если добавляем какой-то параметр.
Сложность работы с СУБД не столько в сложности SQL, сколько в промежуточном слое между программной логикой и обёрткой над СУБД, который генерирует гарантированно корректный SQL и инкапсулирует стандартную логику (то ли ORM, то ли горбушка попроще).atd
13.01.2020 03:53Если атрибутов много и часто меняется их набор, то в базе хранят их в виде obj_id + attr_key + attr_value, тогда alter не нужен.
re ORM: если полноценный орм тащить не хочется, то между ним и ручным написанием запросов есть ещё куча готовых промежуточных решений.
doctorw
13.01.2020 03:53А мешает ли что-либо, для часто изменяющегося списка параметров, завести отдельную таблицу вроде Parameters, в которой делать insert/delete по необходимости?
Dotarev
13.01.2020 03:53Deleted
Mercury13 Автор
13.01.2020 03:53Всё равно это разновидность рефлексии при компиляции. А что, разве только при выполнении она бывает?
doctorw
13.01.2020 03:53habr.com/en/post/343078/#comment_21122902
В процессе компиляции разве есть экземпляры классов?
И что плохого в так называемом «compile time reflection»?Mercury13 Автор
13.01.2020 03:53В страшных костылях для обеспечения совместимости, как на этапе чтения, так и на этапе записи.
Akon32
13.01.2020 03:53А вы не пробовали ini файлы? В Delphi их поддержка есть с незапамятных времён. А с некоторых пор даже json поддерживается стандартными классами.
В чём плюс ini по сравнению с sqlite — сразу есть вменяемый (типа CRUD) API, и скорость, как ни странно, в разы выше. Минусы — нет транзакционности из коробки, неэффективно хранение бинарных данных.Mercury13 Автор
13.01.2020 03:53Ну, например, программа фотопроявки под названием DxO Photolab, известная своим высокотехнологичным шумодавом, работает на INI-файлах. Но сколько там данных у программы фотопроявки, повторяю… Самый большой файл, который нашёл у себя, около 50K. А для больших объёмов INI плохо пригодны.
OldFisher
13.01.2020 03:53Кажется, автор попал в плен XML-дискурса. Ему подавай или XML, или XML-лайт, или бинарный XML. А ведь он изначально проектировался из соображений человекочитаемости (ну да, не получилось, но тем не менее).
Если требования человекочитаемости нет, то намного лучше делать свой формат, разумеется, бинарный, не придерживаясь идеологии иных форматов, которые делали с совершенно другими целями и исходя из других соображений. Например, вместо хранения ключей (названий параметров) в строковом виде, можно использовать числовые идентификаторы, а лучше хэши. Это упрощает формат и экономит место. Расширяемость формата обеспечивается попросту тем, что парсер игнорирует незнакомые ключи.
Когда не ограничиваешь себя изначально чужими парадигмами, можно создать формат, превосходно подходящий под свои конкретные задачи, «срезая углы» там, где это допустимо и акцентируя то, что требуется. Важно не стремиться чересчур сильно его обобщить.leotsarev
13.01.2020 03:53Просто игнорировать незнакомые ключи может привести к отображению мусора для просмотрщиков и к испорченному файлу для редакторов. Нужны флаги как в PNG: public/private, safe to ignore, safe to copy.
OldFisher
13.01.2020 03:53Это всё из-за того произошло, что я в свою очередь тоже попал в плен своей парадигмы, в которой мне не нужно редактировать и сохранять файл с незнакомыми тегами. Что тоже в общем-то подтверждает главную мысль.
MonkAlex
13.01.2020 03:53Таки у PNG это следствие того, что есть контент и его метаданные. Настройки немного про другое. Но в целом — да, есть шанс что настройки зависят друг от друга и просто игнорировать незнакомые может быть ломающим поведением.
Mercury13 Автор
13.01.2020 03:53Я попал в плен этого дискурса, потому что выяснилось самое ценное преимущество XML — древовидность.
OldFisher
13.01.2020 03:53Все пудели — собаки, но не все собаки — пудели. То, что в XML есть древовидность, вовсе не означает, что для древовидного формата нужен именно XML или что-то ему родственное.
KvanTTT
13.01.2020 03:53Если требования человекочитаемости нет, то намного лучше делать свой формат, разумеется, бинарный
Зачем свой? Куча бинарных форматов тоже существует, не надо изобретать велосипед. Например, MessagePack, в котором поддерживаются расширения, древовидные структуры. Он быстрый и компактный.
OldFisher
13.01.2020 03:53Тут по обстоятельствам. Иногда готовый формат превосходно подойдёт, а иногда лучше всё-таки создать свой, хорошо приспособленный к конкретным требованиям.
APXEOLOG
13.01.2020 03:53JSON несколько проще в программировании, чем XML, но всё равно сложен. Плюс затруднено потоковое считывание: JSON различает объекты и массивы, и синтаксис JS говорит, что от перестановки атрибутов результат не меняется.
Попробуйте YAML
TargetSan
13.01.2020 03:53Автор написал про YAML. И он кстати значительно сложнее.
Сравните JSON grammar и YAML grammar. Формат описания разный, но общее представление о разнице в сложности, думаю, даёт.
MonkAlex
13.01.2020 03:53В своё время использовал XML как простой вариант хранения настроек.
Когда XML конфиг вырос до мегабайт, начал тормозить запуск приложения, я переехал в БД. Самый бюджетный вариант — SQLite. Запуск снова выполняется быстро, конвертировать БД намного проще (т.к. есть куча готовых вариантов в интернетах), дополнительно на уровне БД теперь есть ограничения — нельзя как в текстовые конфиги забить невалидные данные, даже ссылочная целостность уже дает много плюсов.
Из вопросов к автору — что с сохрананием старых настроек в новый файл? Ну т.е. есть у меня приложение версии 10, есть приложение версии 20, и есть конфиг версии 15. Сделать чисто запуск версии 10 на конфиге версии 15 не так сложно, а вот чтобы ещё и работало сохранение настроек без даунгрейда до 10 версии — сложная работа, стоит ли, какие есть хорошие приемы? У меня чаще получается, что 10 перетирает конфиг на 10 версию, 20 обновляет до 20 (потому что он то знает как мигрировать).
ПС: json читабельнее xml, не понял минусов json-а в статье.
leotsarev
13.01.2020 03:53Ещё важное преимущество SQlite — файлы вообще говоря сложная штука. Нужно делать сложные трюки вроде копирования файла, записи изменений в временную копию, при сохранении нужно атомарно удалить старый файл и переименовать временный.
Тем временем в SQLite можно просто сделать BEGIN TRAN, изменения делать сразу в БД, а при нажатии кнопки сохранить делать COMMIT.
Если семантика реляционки не подходит, можно сделать либо Entity-Attribute-Value, либо каждый объект хранить в JSON / другом формате в строковом поле в БД, либо в бинарном в protobuf. Даже просто ради написанной за вас транзакционности я бы текстовые файлы не использовал.
Текстовый формат нужен в случае, если файл смотрит/пишет человек.
galaxy
13.01.2020 03:53Расширяемый и компактный формат? ASN.1?
MonkAlex
13.01.2020 03:53Ещё бы человекочитаемым был этот ASN.1 =)
Инструментов почти нет, довольно неудобная, имхо, штука.strib
13.01.2020 03:53ASN.1 XER — вполне читаем, прозрачно конвертируется в BER. Как нет инструментов? Куча стандартных либ. При том написанных для телекома, т.е. работающих, раз мы тут пишем всякое. Написал нотацию и вперед. Wireshark, громадное количество тулов.
MonkAlex
13.01.2020 03:53Либы есть, но в блокноте не открыть, и вот визуально быстро глянуть — честно бесплатных тулов не видел хороших. Wireshark возможно ваш проф инструмент, я его видел давно и издали, поверю на слово.
Я в курсе что за формат, я в курсе как он работает, я смогу написать парсер даже сам, если вдруг что. Я больше к тому, что не сильно популярен он, в отличие от кучи других форматов, для которых найдется пяток бесплатных и удобных тулов для всего-всего.
vintage
13.01.2020 03:53Не могу не поделиться своим разбором форматов данных: https://youtu.be/vBqJWQzPB3Y?list=PLXyFFhv8ucKSC96WOd7Ju2HmEWTA3jPa5&t=5652
MonkAlex
13.01.2020 03:53В видео все так же вопросы заданные в статье актуальны. Формат вы предлагаете, но всё ещё нет ничего готового, нужно описать весь мир самому. Да и библиотек всё ещё нет.
В такой ситуации, JSON всё ещё заметно лучше.
Mercury13 Автор
13.01.2020 03:53Вот такой придумали XML-лайт. Верно, причина та же.
JSON придуман, кстати, по другой причине — если источнику данных доверяешь, на JS в две строчки разбирается.
Biga
13.01.2020 03:53Вместо SQLite можно взять NoSQL базу (ключ-значение), например LMDB или LevelDB. С ними не надо писать SQL запросы. Да и SQLite при желании можно превратить в key-value базу.
У базы данных преимущество в том, что вам не нужно парсить и заново сериализовывать всю сохранку, если требуется поменять одно единственное поле.
pankraty
13.01.2020 03:53Не освещен вопрос возможности / удобства использования в системах контроля версий — тут SQLite и прочие бинарные форматы жестко проигрывают человекочитаемым.
OldFisher
13.01.2020 03:53Тут вопрос поставлен с ног на голову. СКВ предназначены как раз для человекочитаемых форматов. Если данные не предназначены для редактирования вручную, нет особого смысла ни делать формат человекочитаемым, ни контролировать версии через СКВ.
pankraty
13.01.2020 03:53Ни в коей мере с этим не спорю. Это просто еще один фактор, который следует учитывать, решая, какой формат выбирать для сохранения данных. И лично я, в ходе размышлений, склонялся к SQLite, но невозможность эффективной работы в СКВ делает этот формат неприменимым в целом ряде случаев.
hssergey
А если данных будут десятки гигабайт? Или предполагается запуск на мощных серверах с сотнями гигов ОЗУ? В памяти надо держать то, что необходимо в данный момент.
Отпадание баз переписки — это проблема конкретных мессенжеров, кривости рук их разработчиков (вы про Skype?). Сама СУБД тут ни в чем не виновата…
Не очень понятно, что в этом плохого. Библиотеки для SQLite есть для многих языков программирования. Расширяемость — вообще не проблема — добавляй новые столбцы в таблицу или создавай новые таблицы. Иерархическая структура данных — в виде реляционной БД. Файл целиком грузить в память не нужно. Вручную разбирать его тоже не нужно, запросами можно получать данные, необходимые именно в этом конкретном месте. Аналогично с записью. Формат файла документирован, достаточно стороних инструментов для его просмотра и редактирования, если будет такая необходимость.
Mercury13 Автор
Совершенно верно, вы сказали тот случай, когда СУБД более чем оправдана: данных много, и нет нужды держать все в памяти. Потому и сказал: отпадают базы переписки. Но что, мало прог, где реально всё держим в памяти?
Во-первых, в принципе работа с СУБД сложна, по опыту. Во-вторых, бывают и очень нелюбимы программистами изменения структуры файла — и на XML-то это противное дело, а на БД тем более.
GokenTanmay
Прелесть БД — бэкап из коробки, множественный доступ, согласованность, целостность.
Сарказм?Если программа пишется в одно лицо, и только одно приложение будет шариться по файликам — то да, россыпь файлов ограниченна только фантазиями разработчика.
Но данная лафа кончается, когда к данным необходимо ходить из нескольких модулей одновременно и появляется требование иметь хоть сколько нибудь не противоречивый бекап.
TargetSan
Исходя из п.4, вам оочень стоит рассмотреть SQLite как формат, убирающий большинство проблем с потерями. Поскольку при чтении-записи старые версии программы смогут работать со знакомыми им данными, не убивая старые при перезаписи части информации.
GRaAL
Вы, по-моему, что-то не так поняли. Пункты, на которые вы отвечаете, содержатся в разделе «Мы разрабатываем программу, которая...», т.е. это некоторые выставленные предусловия, для которых автор приводит решение. Одно из рассматриваемых предусловий — «все данные надо держать в памяти».
Автор не говорит что «всегда надо данные держать в памяти, а SQLite не дает преимуществ», автор говорит «рассмотрим случай, когда данные надо держать в памяти, в этом случае SQLite не даст нам преимуществ».
Mercury13 Автор
Отпадают — имеется в виду «не рассматриваем», «это не наш вопрос».