Хранение вектора символов в Redis, содержащего NUL

Я хочу сохранить изображение JPEG в Redis как одну пару ключ-значение. Из OpenCV я получаю std::vector<unsigned char> jpeg от imencode()

Теперь я конвертирую этот вектор в std::string и SET с помощью Hiredis. Проблема в том, что вектор jpeg содержит NUL символов (ANSII == 0), а функция Hiredis SET получает value.c_str(). .c_str() усекает строку после первого вхождения NUL, поэтому в БД сохраняется только эта подстрока.

Мой вопрос: как я могу SET и GET std::vector<unsigned char> (содержащий NUL) с Hiredis? (Минимизация времени выполнения имеет решающее значение.)

Вот мой код:

// Create vector of uchars, = From CV [Disregard inefficiency here]

std::vector<unsigned char> jpeg;
jpeg.push_back( 'a' );
jpeg.push_back( 'b' );
jpeg.push_back( (unsigned char) 0 );
jpeg.push_back( 'c' );
jpeg.push_back( 'd' );

// Convert to string

std::string word = "";
for (int i=0; i<jpeg.size(); ++i)
{
    word.push_back(jpeg[i]);
}

std::cout << "word = " << word << std::endl;
std::cout << "word.c_str() = " << word.c_str() << std::endl;

// connect redis

std::string hostname = "127.0.0.1";
int port = 6379;
timeval timeout = { 1, 500000 }; // 1.5 seconds
redisContext* context = redisConnectWithTimeout(hostname.c_str(), port, timeout);

// set redis

std::string key = "jpeg";
redisReply* reply = (redisReply *)redisCommand(context, "SET %s %s", key.c_str(), word.c_str() );
freeReplyObject( (void*) reply);

// get redis

reply = (redisReply *)redisCommand(context, "GET %s", key.c_str() );
std::string value = reply->str;
freeReplyObject((void*) reply);
std::cout << "returned value = " << value << std::endl;

// Convert back to vector of uchars (this should be the same as the original jpeg)  [Disregard inefficiency here]

std::vector<unsigned char> jpeg_returned;
for (int i=0; i<value.size(); ++i)
{
    jpeg_returned.push_back(value[i]);
    // std::cout << "value[i] = " << value[i] << std::endl;
}

person user2926577    schedule 15.03.2017    source источник
comment
Хотя std::string может хранить произвольные данные, вы действительно не можете использовать строковые функции при работе с двоичными данными (что и представляет собой изображение JPEG). Разве в Redis нет какого-то двоичного типа blob?   -  person Some programmer dude    schedule 15.03.2017
comment
На самом деле HiRedis lib имеет двоичную поддержку, и вам не нужно преобразовывать std::vector в std::string перед его сохранением. Проверьте это: stackoverflow.com/questions/26799074/   -  person JustRufus    schedule 15.03.2017
comment
.c_str() обрезает строку после первого вхождения NUL. Это неверно. Это не c_str(), который усекает строку, это функции, которым вы даете c_str(), которые останавливаются на первом NULL. См. документацию по c_str(). Например, ваше использование cout не будет работать таким образом, так как вам нужно будет использовать cout.write(word.c_str(), word.size()) для правильного вывода всей строки.   -  person PaulMcKenzie    schedule 15.03.2017
comment
Да, jpeg является двоичным, но opencv возвращает vec‹char›, который просто умоляет сохранить его в Redis в виде строки. Единственная проблема на самом деле - это символ NUL. Я видел ссылку раньше, автор не рекомендует этот метод. Также было бы неплохо, если бы я мог прочитать этот jpg из другого приложения Python.   -  person user2926577    schedule 15.03.2017
comment
Сэр, можете ли вы поделиться своим кодом о сохранении изображения Opencv в Redis с использованием C++ и его чтении с помощью Python? Это мне очень поможет, спасибо!   -  person ToughMind    schedule 05.12.2019
comment
Сэр, можете ли вы поделиться своим кодом о сохранении изображения Opencv в Redis с использованием C++ и его чтении с помощью Python? Это мне очень поможет, спасибо!   -  person ToughMind    schedule 05.12.2019


Ответы (1)


Прежде чем показать код, хочу еще раз предупредить, что хранение бинарных данных без надлежащей сериализации может быть проблематичным. По крайней мере, убедитесь, что все ваши серверы имеют одинаковый порядок байтов и имеют одинаковый размер (int).

std::vector<char> v{'A', 'B', '\0', 'C', 'D'};

std::string key = "jpeg";
redisReply* reply = static_cast<redisReply *>( redisCommand(context, "SET %s %b", key.c_str(), v.data(), v.size() ) );
freeReplyObject( reply );

И чтение из python.

>>> import redis
>>> r = redis.StrictRedis(host='localhost', port=6379, db=0)
>>> r.get("jpeg")
'AB\x00CD'
person JustRufus    schedule 15.03.2017
comment
Благодарю вас! Я реализовал функцию получения на основе вашего другого сообщения: redisReply* reply = (redisReply *) redisCommand(context, "GET %s", a_key.c_str() ); unsigned char *val = (unsigned char *) malloc( reply->len ); memcpy( val, reply->str, reply->len); std::vector<unsigned char> jped_returned(val, val + reply->len); free( val ); freeReplyObject(reply); Является ли это наиболее эффективным способом? - person user2926577; 15.03.2017
comment
@ user2926577 Думаю, да. почему вы выделяете массив c-style, а затем копируете в std::vector. Вы должны иметь возможность создавать std::vector‹unsigned char› jped_returned непосредственно из answer-›str и answer-›len. - person JustRufus; 15.03.2017
comment
Я следил за вашим комментарием // Here, it is safer to make a copy to be sure memory is properly aligned, я на самом деле не уверен, почему это рекомендуется здесь. Я создал указатель на массив, здесь не должно быть глубоких копий. Не могли бы вы опубликовать код С++ для двоичного GET, как бы вы его оптимизировали? Спасибо - person user2926577; 15.03.2017
comment
@ user2926577 вы можете сделать что-то вроде этого: reply = static_cast<redisReply *>(redisCommand(context, "GET %s", key.c_str() ) ); std::vector<unsigned char> read_vector( reply->str, reply->str+reply->len ); - person JustRufus; 15.03.2017