Использование istringstream для обработки блока памяти переменной длины - PullRequest
2 голосов
/ 19 февраля 2010

Я пытаюсь использовать istringstream для восстановления закодированного wstring из некоторой памяти. Память раскладывается следующим образом:

  1. 1 байт, чтобы указать начало кодирования wstring. Произвольно это «!».
  2. n байтов для хранения длины символа в текстовом формате, например 0x31, 0x32, 0x33 будет "123", то есть строка из 123 символов
  3. 1 байт-разделитель (пробел)
  4. n байтов, которые представляют собой wchars, составляющие строку, где wchar_t - 2 байта каждый.

Например, последовательность байтов:

21 36 20 66 00 6f 00 6f 00

- это "! 6 f.o.o." (используя точки для обозначения символа 0)

Все, что у меня есть, это указатель char * (назовем его pData) на начало блока памяти с этими закодированными данными. Каков «лучший» способ использования данных для восстановления wstring («foo»), а также для перемещения указателя на следующий байт после конца закодированных данных?

Я играл с istringstream, чтобы позволить мне использовать префиксный байт, длину строки и разделитель. После этого я могу рассчитать, сколько байтов нужно прочитать, и использовать функцию потока read() для вставки в wstring с соответствующим изменением размера. Проблема в том, как мне сначала получить эту память в поток istring? I мог бы сначала попытаться создать строку, а затем передать ее в поток istring, например,

std::string s((const char*)pData);

но это не работает, потому что строка усекается в первом нулевом байте. Или я мог бы использовать другой конструктор строки, чтобы явно указать, сколько байтов использовать:

std::string s((const char*)pData, len);

, который работает, но только если я заранее знаю, что такое len. Это сложно, учитывая, что данные имеют переменную длину.

Это похоже на действительно решаемую проблему. Означает ли мой статус новобранца со строками и потоками, что я упускаю простое решение? Или я лаю не то дерево с помощью всего строкового подхода?

Ответы [ 4 ]

0 голосов
/ 19 февраля 2010

Соблазнительно (ab) использовать (устаревший, но, тем не менее, стандартный) std :: istrstream здесь:

// Maximum size to read is 
// 1 for the exclamation mark
// Digits for the character count (digits10() + 1)
// 1 for the space
const std::streamsize max_size = 3 + std::numeric_limits<std::size_t>::digits10;

std::istrstream s(buf, max_size);

if (std::istream::traits_type::to_char_type(s.get()) != '!'){
    throw "missing exclamation";
}

std::size_t size;
s >> size;

if (std::istream::traits_type::to_char_type(s.get()) != ' '){
    throw "missing space";
}

std::wstring(reinterpret_cast<wchar_t*>(s.rdbuf()->str()), size/sizeof(wchar_t));
0 голосов
/ 19 февраля 2010

Можно ли изменить способ кодирования длины и сделать его фиксированным размером?

unsigned long size = 6; // known string length<br> char* buffer = new char[1 + sizeof(unsigned long) + 1 + size];<br> buffer[0] = '!';<br> memcpy(buffer+1, &size, sizeof(unsigned long)); </p> <p>

В буфере

должны храниться индикатор запуска (1 байт), фактический размер (размер строки без знака), разделитель (1 байт) и сам текст (size).
Таким образом, вы можете легко получить размер «довольно», затем установить указатель так, чтобы он находился за пределами издержек, а затем использовать переменную len в конструкторе строк.
unsigned long len;<br> memcpy(&len, pData+1, sizeof(unsigned long)); // +1 to avoid the start indicator<br> // len now contains 6<br> char* actualData = pData + 1 + sizeof(unsigned long) + 1;<br> std::string s(actualData, len);

Это низкий уровень и подвержен ошибкам :) (например, если вы читаете что-то, что не закодировано так, как вы ожидаете, len может стать довольно большим), но вы избегаете динамического чтения длины строки .

0 голосов
/ 19 февраля 2010

Кажется, что-то в этом порядке должно работать:

std::wstring make_string(char const *input) { 
    if (*input != '!')
       return "";
    char length = *++input;
    return std::wstring(++input, length);
}

Сложная часть имеет дело с переменной длиной размера.Без указания длины трудно угадать, когда прекратить обрабатывать данные как указание длины строки.

Что касается перемещения указателя, если вы собираетесь сделать это внутри функции, вы 'Вам нужно будет передать ссылку на указатель, но в противном случае достаточно просто добавить найденный вами размер к полученному указателю.

0 голосов
/ 19 февраля 2010

Попробуйте установить строковый поток rdbuf :

char* buffer = something;
std::stringbuf *pbuf;
std::stringstream ss;

std::pbuf=ss.rdbuf();
std::pbuf->sputn(buffer, bufferlength);
// use your ss

Edit: я вижу, что это решение будет иметь аналогичную проблему с вашей строки (char *, len) ситуации Можете ли вы рассказать нам больше о вашем объекте буфера? Если вы не знаете длину и она не заканчивается нулем, с ней будет очень трудно справиться.

...