Чтение непосредственно из std :: istream в std :: string - PullRequest
13 голосов
/ 29 ноября 2009

Есть ли способ прочитать известное количество байтов непосредственно в std :: string без создания временного буфера для этого?

например, в настоящее время я могу сделать это

boost::uint16_t len;
is.read((char*)&len, 2);
char *tmpStr = new char[len];
is.read(tmpStr, len);
std::string str(tmpStr, len);
delete[] tmpStr;

Ответы [ 6 ]

11 голосов
/ 29 ноября 2009

std::string имеет функцию resize, которую вы можете использовать, или конструктор, который будет делать то же самое:

boost::uint16_t len;
is.read((char*)&len, 2);

std::string str(len, '\0');
is.read(&str[0], len);

Это не проверено, и я не знаю, должны ли строки иметь непрерывное хранилище.

6 голосов
/ 28 февраля 2011

Вы можете использовать комбинацию copy_n и insert_iterator

void test_1816319()
{
    static char const* fname = "test_1816319.bin";
    std::ofstream ofs(fname, std::ios::binary);
    ofs.write("\x2\x0", 2);
    ofs.write("ab", 2);
    ofs.close();

    std::ifstream ifs(fname, std::ios::binary);
    std::string s;
    size_t n = 0;
    ifs.read((char*)&n, 2);
    std::istream_iterator<char> isi(ifs), isiend;
    std::copy_n(isi, n, std::insert_iterator<std::string>(s, s.begin()));
    ifs.close();
    _unlink(fname);

    std::cout << s << std::endl;
}

без копирования, без хаков, без возможности переполнения, без неопределенного поведения.

2 голосов
/ 30 ноября 2009

Я бы использовал вектор в качестве буфера.

boost::uint16_t len;
is.read((char*)&len, 2); // Note if this file was saved from a different architecture 
                         // then endianness of these two bytes may be reversed.

std::vector buffer(len);  // uninitialized.
is.read(&buffer[0], len);

std::string  str(buffer.begin(),buffer.end());

Хотя вам, вероятно, не удастся использовать строку в качестве буфера (как описано в GMan). Стандарт не гарантирует, что члены строки находятся в последовательных расположениях (поэтому проверьте текущую реализацию и добавьте большой комментарий, который необходимо проверить при портировании на другой компилятор / платформу).

2 голосов
/ 29 ноября 2009

Вы можете использовать что-то вроде getline:

#include <iostream>
#include <string>
using namespace std;

int main () {
  string str;
  getline (cin,str,' ');
}
0 голосов
/ 01 февраля 2013

Простой способ будет:

std::istream& data
const size_t dataSize(static_cast<size_t>(data.rdbuf()->in_avail()));
std::string content;
content.reserve( dataSize);
data.read(&content[0], dataSize);
0 голосов
/ 29 ноября 2009

Вы просто оптимизируете длину кода или пытаетесь сохранить себе копию здесь? Что не так с временным буфером?

Я бы сказал, что вы на самом деле обходите защиту строки, пытаясь писать напрямую, делайте это так. Если вы беспокоитесь о производительности копии в std :: string, поскольку вы определили, что это каким-то образом влияет на производительность вашего приложения, я бы работал непосредственно с символом *.

РЕДАКТИРОВАТЬ: Делать больше ищет ... инициализация std :: string из char * без копирования

Во втором ответе довольно четко сказано, что вы не можете достичь того, чего хотите достичь (т. Е. Заполнить std :: string без итерации над символом * для копирования.)

Взгляните на свою подпрограмму загрузки (возможно, опубликуете ее здесь?) И сведите к минимуму распределение: new и delete, безусловно, не бесплатны, так что вы можете хотя бы сэкономить время, если вам не нужно постоянно заново создавать буфер , Я всегда нахожу полезным стереть его, установив в memset'у буфер равным 0 или обнуляя завершив первый индекс массива на каждой итерации, но вы можете быстро удалить этот код в интересах производительности, если вы уверены в своем алгоритме.

...