fk0, легенда (05.12.2019 02:42, просмотров: 1380) ответил scorpion на Приспичило хранить текстовые и двоичные данные в одном файле. Форматов метафайлов много. А кто какие пользует и почему ?
Вопрос в том, что первично. Если бинарные данные в текстовом файле (т.е. другие программы будут его осознанно использовать как текстовый файл, например, он будет открываться в редакторе) -- закодировать в base64 (и отдельно закодировать длину ещё, ибо base64 длина не кодируется, зато есть padding). Если просто нужно хранить вперемешку и текстовые данные и двоичные, но текстовый формат сам по себе не первичен: текст в каком-то смысле тоже "бинарные данные", например, строки фиксированной длины, нуль-терминированные, с указателем длины в начале (паскалевские, микрософтовские...)
Другой вопрос, как отделить зерна от плевел различные записи внутри файла друг от друга. Т.е. подразумевается, что файл содержит структурированные данные и вопрос -- какая структура. Ответ может быть различен, в зависимости от того и какая структура, и какой контент (текст или бинарные данные) первичен.
Под структурой подразумевается, что записаны могут быть: фиксированное количество различных полей данных (т.е. одна структура, условно говоря), массив структур (который, к слову, можно "транспонировать" в массивы однотипных записей разных типов), наконец это может быть дерево, хранящее в узлах вложенные структуры (не фиксированное количество, заранее не известны типы узлов).
Вообще нужно отдельно сказать, что если планируется хранить какие-либо данные, то возможно лучше их хранить не в той форме, как они представляются в памяти компьютера, или как их проще сериализовать, а перевести перед сериализацией данные в нормальную форму (см. определение нормальных форм в Wikipedia). С такими данными проще будет потом работать, пропадёт избыточность, такие данные легко загрузить в SQL. Например, если данные представляют собой последовательность структур с определёнными полями, то очень даже разумно будет пройтись по списку структур несколько раз и каждый раз сериализовать только одно поле в несколько разных таблиц, каждая из которых хранит только один тип значения, а таблицы связать между собой неким уникальным ключём (можно сгенерировать последовательные номера). В принципе, таким методом можно сериализовать любые "сложные" структуры в плоские таблицы вида ключ-значение (которые могут быть обычными csv файлами к тому же, например). Например, некоторые БД (Metakit в частности) хранят данные в таком виде, когда они разделены по полям и поля одного типа хранятся рядом.
Универсальным способом закодировать что угодно в бинарной форме является ASN.1. Обычно работа с ASN.1 подразумевает формальное определение "грамматики" и генерацию из формального определения исходных текстов программ (де)сериализации. Тут риск ошибок из-за ручного кодирования сводится к минимуму, что важно, если структуры достаточно сложные. К тому же бинарное представление достаточно компактно.
В простых случаях можно просто записать последовательность структур... проблема с бинарными данными, что у разных компьютеров разное представление о размещении бинарных данных в памяти, о ширине полей, выравнивании, порядке байт. ASN.1 решает эти проблемы. Кроме ASN.1 есть другие способы кросс-платформенной сериализации, например, XDR (RFC4506). Кроме кросс-платформенности важен факт возможности расширения протокола новыми типами данных (которые не могут быть прочитаны более старым парсером). Обычно (внутри того же ASN.1) используется TLV (type-length-value) запись, когда прямо в данных кодируется и тип данных и длина, чтоб парсер не имеющий представление о таком типе мог его копировать или пропускать без анализа. При записи структур как есть неплохо хотя бы предусмотреть в самой структуре поле "длины структуры", чтоб в будущем можно было расширить протокол путём удлинения структуры. И как сказано выше, данные всегда можно развернуть в набор массивов записей одинаковых типов (вначале все первые члены всех структур, потом вторые...) Тогда расширение структуры превращается в добавление в конец файла нескольких таблиц с новыми типами, для новых добавленных полей, которые старый парсер может по крайней мере легко игнорировать.
Для текстового файла аналогичным способом является наверное XML, позволяющих хранить в текстовом файле деревья. Которыми могут быть сложные рекурсивные списки объектов, например. Альтернативой может быть JSON. Не совсем полноценной альтернативой, XML позволяет и трансформацию данных, и валидацию, и адресацию узлов из дерева с помощь XPath...
Если требуется сохранить достаточно простые структуры, то может быть и нет смысла лезть в дебри и можно использовать свой простой формат текстового файла наподобии CSV, или значения разделённые пробелами (их легко обратно считывать через scanf). Или список ключ-значение, по два слова в каждой строчке.
Может быть, имеет смысл сгенерировать программу на каком-либо интерпретируемом ЯВУ, интерпретатором которого располагает тот, кто будет файл парсить. И он просто воспримет записи как программу. Пример такого подхода -- язык Postscript, например (и PDF в том числе). Некоторые ЯВУ, в частности Tcl, позволяют легко строить на своей базе специальные ограниченные domain-specific языки (исполняется в "безопасном" интерпретаторе, чтоб лишнего чего не наделал).
[ZX]