2009-11-29 3 views
12

Есть ли все равно, чтобы прочитать известное количество байтов непосредственно в std :: string, не создавая временного буфера для этого?Чтение непосредственно из std :: istream в 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; 
+0

Вы думали об использовании '' вектор вместо 'string'? Если ваши данные более «сырые», чем «строковые», это может сработать лучше для вас, и есть меньше путаницы в отношении прямого доступа. (Векторы должны храниться смежно, поэтому используйте '& v [0]'.) – 2009-11-29 19:14:16

+0

По большей части это строковые данные, просто встроенные в большие двоичные файлы.Также я хочу только изменить подпрограммы загрузки, а не 1000 строк кода, которые затем используют загруженные данные, которые потребуются для изменения из std :: string. –

+0

Затем я проверил вашу конкретную реализацию строки, а затем воспользуюсь ответом GMan, убедитесь, что вы проверили поток после 'is.read'. – 2009-11-29 19:21:43

ответ

11

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

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

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

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

+0

Строки определены как векторы. То же соприкосновение. – bmargulies

+4

Они не определены как векторы, но 21.3.4/1 подразумевает непрерывное хранение. Однако есть сообщения о путанице и дефектах в этом конкретном разделе, и я не уверен, каков нынешний консенсус, и насколько он переносим в зависимости от этой интерпретации. – 2009-11-29 19:17:57

+2

@Roger. Я не согласен с тем, что 21.3.4/1 подразумевает непрерывное хранение. Именно наличие c_str() и data() подразумевает это, но только потому, что для эффективной реализации потребуется непрерывное хранилище для их реализации. Я считаю, что следующая версия стандарта также устранит ситуацию. –

0

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

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

EDIT:. Делать больше глядя ... initializing std::string from char* without copy

Во втором ответе, он заявил, довольно решительно, что вы не можете добиться того, что вы хотите достичь (то есть заполнить зЬй :: строку без итерация над символом char * для копирования.)

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

+0

Производительность std :: string в порядке, проблема заключается в загрузке данных из них из двоичного файла, который в настоящее время занимает недопустимо длительный период времени. Профилирование показало, что 70% времени загрузки - это строки для чтения, а только 30% - это другие двоичные данные или небольшие бит обработки, поэтому ускорение чтения строк кажется очевидным решением для ускорения всего этого с большим отрывом. Поэтому я ни в коем случае не хочу заменять std :: string в остальной части программы, что означало бы изменение 1000 строк, а не просто изменение строковой загрузки. –

+0

Насколько велика проблема alloc, dealloc char * на каждой итерации? Что делать, если вы просто сохранили char * достаточного размера (проверяя каждую итерацию, очевидно) вокруг и только что создали новые строки из этого единственного символа char *? – antik

2

Вы могли бы использовать что-то вроде GetLine:

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

int main() { 
    string str; 
    getline (cin,str,' '); 
} 
+1

Это хорошее предложение для других проблем, но не для этого: неформатированный ввод определенного количества байтов. – 2009-11-29 19:09:25

+0

Это не отвечает на вопрос, так как он не считывает определенное количество байтов. Даже если это так, getline должен смотреть на каждый байт, который он читает для разделителя, который является дорогостоящим и ненужным, когда указано количество байтов. Этот ответ следует удалить. – xaxxon

2

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

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). Стандарт не гарантирует, что члены строк находятся в последовательных местах (так что проверьте текущую реализацию и поставьте большой комментарий, который необходимо проверить при переносе на другой компилятор/платформу).

+0

«Стандарт не гарантирует, что члены строк находятся в последовательных местах» <== он, по-видимому, был с '11 – xaxxon

+0

@xaxxon: True. Но в приведенном выше коде не требуется строка для хранения элементов в последовательных местах. Теперь, если вы ссылаетесь на вектор (и только что упомянутую строку случайно), этот код делает это предположение. Но, как вы отметили после C++ 11, это было гарантировано. Кроме того, до того, как в 2011 году был обновлен стандарт C++, был проведен обзор всех основных реализаций (около 2007 года), и все они реализовали векторы как непрерывные блоки (что упростило обновление стандарта). –

5

Вы можете использовать комбинацию 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; 
} 

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

+0

Если вы делаете то, что, как я думаю, вы делаете, тогда прочитайте эту [ссылку] (http://www.boost.org/doc/libs/1_46_0/libs/serialization/doc/index.html) и код что с этим связано. –

+0

Не в этом случае, но является ли 'copy_n' безопасным, если конец файла или возникшая ошибка? – Liviu

+0

Я построил код, используя ваш метод: [обзор кода] (http://codereview.stackexchange.com/questions/38148/updating-a-file-through-c-streams). Благодаря! – Liviu

0

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

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);