Как преобразовать из строкового потока в беззнаковый вектор символов и наоборот?

У меня есть std::stringstream ss;, содержащий двоичные данные, которые я хочу поместить в std::vector<unsigned char> my_vector;. Затем я хочу взять my_vector и использовать его для создания нового std::stringstream new_ss, идентичного ss.

Я попытался сделать что-то похожее на ответы на этот вопрос:

Есть ли более эффективный способ установить std::vector из потока?

и этот вопрос:

C++: вектор в поток строк

в итоге:

std::copy(std::istream_iterator<unsigned char>(ss),
          std::istream_iterator<unsigned char>(),
          std::back_inserter(my_vector));

тогда:

std::copy(my_vector.begin(), 
          my_vector.end(),
          std::ostream_iterator<unsigned char>(new_ss));

но ss и new_ss не одно и то же. Я подозревал, что проблема может быть связана с игнорированием пробелов, поэтому вместо этого я попытался создать вектор следующим образом:

    std::string const& s = ss.str();
    my_vector.reserve(s.size());
    my_vector.assign(s.begin(), s.end());

но это не решило проблему.

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


person nellapizza    schedule 15.12.2018    source источник
comment
Вы использовали std::ios::binary для создания строковых потоков?   -  person eerorika    schedule 16.12.2018
comment
Было бы полезно иметь минимальный воспроизводимый пример и знать, чем отличаются строки... (не то же самое не супер точно)   -  person Marc Glisse    schedule 16.12.2018
comment
@stringstreams берутся из хлопьев::BinaryInputArchive iarchive(ss) и являются результатом сериализации объекта.   -  person nellapizza    schedule 16.12.2018


Ответы (3)


Я подозреваю, что это будет довольно эффективный метод:

std::string const test_data = "hello world";

std::stringstream ss(test_data);

// discover size of data in stringstream
auto bof = ss.tellg();
ss.seekg(0, std::ios::end);
auto stream_size = std::size_t(ss.tellg() - bof);
ss.seekg(0, std::ios::beg);

// make your vector long enough
std::vector<unsigned char> v(stream_size);

// read directly in
ss.read((char*)v.data(), std::streamsize(v.size()));

// Now put it into a new stream object
std::stringstream new_ss;

// direct write again
new_ss.write((char const*)v.data(), std::streamsize(v.size()));

// check the result is the same as the original data
assert(new_ss.str() == test_data);

Их можно было бы обернуть в функции, чтобы сделать их проще:

std::vector<unsigned char> to_vector(std::stringstream& ss)
{
    // discover size of data in stream
    ss.seekg(0, std::ios::beg);
    auto bof = ss.tellg();
    ss.seekg(0, std::ios::end);
    auto stream_size = std::size_t(ss.tellg() - bof);
    ss.seekg(0, std::ios::beg);

    // make your vector long enough
    std::vector<unsigned char> v(stream_size);

    // read directly in
    ss.read((char*)v.data(), std::streamsize(v.size()));

    return v;
}

std::stringstream to_stream(std::vector<unsigned char> const& v)
{
    std::stringstream ss;
    ss.write((char const*)v.data(), std::streamsize(v.size()));
    return ss;
}

int main()
{
    std::string const test_data = "hello world";

    std::stringstream ss(test_data);

    auto v = to_vector(ss);

    auto new_ss = to_stream(v);

    assert(new_ss.str() == test_data);
}
person Galik    schedule 15.12.2018

Использование итератора std::istream_iterator<T> или std::ostream_iterator<T> обязательно не будет работать (в случае ввода) и просто излишне медленно (в случае вывода). Проблема в том, что std::istream_iterator<T> пропускает начальные пробелы, и оба итератора проходят через форматированный интерфейс, который выполняет различные проверки, которые на самом деле не помогают.

Вы можете использовать std::istreambuf_iterator<char> или std::ostreambuf_iterator<char>. Однако я думаю, что преобразование через строковый интерфейс более эффективно.

person Dietmar Kühl    schedule 15.12.2018

std::vector имеет конструктор перегрузки, который принимает пару входных итераторов, определяющих последовательность содержимого нового вектора.

std::stringstream ss;

// Fill in your stringstream, then:

std::vector<unsigned char> my_vector{
    std::istreambuf_iterator<char>{ss},
    std::istreambuf_iterator<char>{}
};
person Sam Varshavchik    schedule 15.12.2018