Буферы протокола Google - сохранение сообщений в файл
Я использую буфер протокола google для сериализации данных о рынке акций (т.е. временные метки, ставки, поля запроса).
Я могу сохранить одно сообщение в файл и десериализировать его без проблем.
Как хранить несколько сообщений в одном файле? Не знаю, как я могу отделить сообщения. Мне нужно иметь возможность добавлять новые сообщения в файл на лету.
Ответы
Ответ 1
Я бы рекомендовал использовать методы writeDelimitedTo(OutputStream)
и parseDelimitedFrom(InputStream)
на объектах Message
. writeDelimitedTo
записывает длину сообщения до самого сообщения; parseDelimitedFrom
затем использует эту длину для чтения только одного сообщения и не дальше. Это позволяет записывать несколько сообщений на один OutputStream
, чтобы затем анализироваться отдельно. Для получения дополнительной информации см. https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/MessageLite#writeDelimitedTo(java.io.OutputStream)
Ответ 2
Из документов:
http://code.google.com/apis/protocolbuffers/docs/techniques.html#streaming
Потоковая передача нескольких сообщений
Если вы хотите записать несколько сообщений в один файл или поток, зависит от вас, чтобы отслеживать, где заканчивается одно сообщение, и следующее начинается. Формат протокола буфера протокола не является самограничным, поэтому синтаксические анализаторы протокола не могут определить, где заканчивается сообщение своя. Самый простой способ решить эту проблему - записать размер каждое сообщение, прежде чем вы сами напишите сообщение. Когда вы читаете сообщения, вы читаете размер, затем читаете байты в отдельный буфер, затем проанализируйте этот буфер. (Если вы хотите избежать копирование байтов в отдельный буфер, проверьте CodedInputStream class (как на С++, так и на Java), которые могут быть ограничены чтением определенное количество байтов.)
Ответ 3
Protobuf не включает терминатор на самую удаленную запись, поэтому вам нужно сделать это самостоятельно. Самый простой подход - префикс данных с длиной следующей записи. Лично я склонен использовать подход к написанию строкового заголовка (для произвольного номера поля), тогда длина как "varint" - это означает, что весь документ сам по себе является действительным protobuf и может быть использован как объект с "повторным" элементом, однако, только маркер фиксированной длины (обычно 32-разрядный малоподобный) будет делать то же самое. При любом таком хранилище он является дополнительным, как вам нужно.
Ответ 4
Если вы ищете решение на С++, Kenton Varda отправил исправление к protobuf в августе 2015 года, добавив поддержку вызовов writeDelimitedTo() и readDelimitedFrom(), которые будет сериализовать/десериализовать последовательность прото-сообщений в/из файла таким образом, который совместим с Java-версией этих вызовов. К сожалению, этот патч еще не утвержден, поэтому, если вам нужна функциональность, вам нужно будет объединить его самостоятельно.
Другим вариантом является то, что у Google есть открытый файл для чтения/записи файла protobuf через другие проекты. Например, библиотека or-tools содержит классы RecordReader и RecordWriter, которые сериализуют/десериализуют прото-поток в файл.
Если вы хотите, чтобы автономные версии этих классов почти не имели внешних зависимостей, у меня есть вилка or-tools, которая содержит только эти классы. См.: https://github.com/moof2k/recordio
Чтение и запись с этими классами просты:
File* file = File::Open("proto.log", "w");
RecordWriter writer(file);
writer.WriteProtocolMessage(msg1);
writer.WriteProtocolMessage(msg2);
...
writer.Close();
Ответ 5
Более простой способ - base64 кодировать каждое сообщение и сохранять его как запись на строку.