я использую SerializeToString
на моих объектах Protobuf, а затем сохранить строку в БД.
Однако иногда у меня есть массив таких объектов. Я хотел бы сохранить весь сериализованный массив, и для этого мне нужна строка-разделитель между сериализованными строками.
Согласно документация Я видел, что строка является просто байтовым массивом, и поэтому мне ничего не обещали относительно его содержимого.
Какой будет лучший подход здесь?
Я не знаю длину массива, потому что объекты могут быть добавлены к нему по ходу, и я хочу, чтобы он сохранялся в БД на протяжении всего процесса.
Предположим, ваш протобуф message
выглядит так:
message Object
{
... = 1;
... = 2;
... = 3;
}
Затем в том же файле ввести еще 1 message
которая является коллекцией этих Object
s.
message Objects
{
repeated Object array = 1;
}
Следовательно, когда у вас много элементов, вы можете просто использовать Objects
и использовать SerializeAsString()
на Objects
сам. Это сэкономит ваши усилия по сериализации личности Object
отдельно и положить свой собственный разделитель ручной работы. Вы можете сериализовать все Object
с использованием одного экземпляра Objects
,
При таком подходе вы делегировать все разбор & сериализацию работы также в библиотеку Protobuf. Я использую это в своем проекте, и это работает как шарм.
Кроме того, разумное использование Objects
также позволит избежать создания дополнительных копий Object
, Вы можете добавить элементы к этому и получить доступ, используя индексацию. repeated
поля protobufs совместимы с C ++ 11, поэтому вы можете использовать его с итераторами или с расширенными for
петля также.
Важно отметить, что при сохранении выходных данных Objects::SerializeAsString()
в файл, вы должны сначала ввести длину этого string
сопровождается фактической сериализованной строкой. При чтении вы можете сначала прочитать длину, а затем общее количество байтов. Для простоты использования я продлил std::fstream
и перегружены следующие методы:
struct fstreamEncoded : std::fstream
{
// other methods
void // returns `void` to avoid multiple `<<` in a single line
operator<< (const string& content)
{ // below casting would avoid recursive calling of this method
// adding `length() + 1` to include the protobuf's last character as well
static_cast<std::fstream&>(*this) << (content.length() + 1) << "\n" << content << std::endl;
}
string
getline ()
{
char length_[20] = {};
std::istream::getline(length_, sizeof(length_) - 1);
if(*length_ == 0)
return "";
const size_t length = std::atol(length_); // length of encoded input
string content(length, 0); // resize the `string`
read(&content[0], length); // member of `class istream`
return content;
}
}
Выше просто иллюстрация. Вы можете следовать в соответствии с вашими потребностями проекта.
Если вы не можете гарантировать, что ваш разделитель будет уникальным для содержимого сериализованных данных, тогда я добавлю элемент размера в начале каждого объекта, указывающий, какова длина следующей строки.