Как предварительно выделить память для объекта std :: string? - PullRequest
22 голосов
/ 22 июля 2010

Мне нужно скопировать файл в строку. Мне нужно каким-то образом предварительно выделить память для этого строкового объекта и способ непосредственного чтения содержимого файла в память этой строки?

Ответы [ 6 ]

28 голосов
/ 22 июля 2010

std::string имеет метод .reserve для предварительного распределения.

std::string s;
s.reserve(1048576); // reserve 1 MB
read_file_into(s);
14 голосов
/ 22 июля 2010

Это не столько сам по себе ответ, сколько вид комментария / резюме / сравнения нескольких других ответов (а также быстрая демонстрация того, почему я рекомендовал стиль кода @Johannes - Литб дает в своем ответе). Поскольку @sbi опубликовал альтернативу, которая выглядела довольно неплохо, и (особенно) избегал дополнительной копии, связанной с чтением в поток строк, а затем с помощью члена .str() для получения строки, я решил написать быстрое сравнение двух:

[Редактировать: я добавил третий тестовый пример с использованием кода на основе @Tyler McHenry istreambuf_iterator и добавил строку для вывода длины каждой прочитанной строки, чтобы оптимизатор не оптимизировал чтение, потому что результат никогда не использовался.]

[Edit2: И теперь был добавлен код от Мартина Йорка ...]

#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <iterator>
#include <time.h>

int main() {
    std::ostringstream os;
    std::ifstream file("equivs2.txt");

    clock_t start1 = clock();
    os << file.rdbuf();
    std::string s = os.str();
    clock_t stop1 = clock();

    std::cout << "\ns.length() = " << s.length();

    std::string s2;

    clock_t start2 = clock();
    file.seekg( 0, std::ios_base::end );
    const std::streampos pos = file.tellg();
    file.seekg(0, std::ios_base::beg);

    if( pos!=std::streampos(-1) )
        s2.reserve(static_cast<std::string::size_type>(pos));
    s2.assign(std::istream_iterator<char>(file), std::istream_iterator<char>());
    clock_t stop2 = clock();

    std::cout << "\ns2.length = " << s2.length();

    file.clear();

    std::string s3;

    clock_t start3 = clock();   
    file.seekg(0, std::ios::end);   
    s3.reserve(file.tellg());
    file.seekg(0, std::ios::beg);

    s3.assign((std::istreambuf_iterator<char>(file)),
            std::istreambuf_iterator<char>());
    clock_t stop3 = clock();

    std::cout << "\ns3.length = " << s3.length();

    // New Test
    std::string s4;

    clock_t start4 = clock();
    file.seekg(0, std::ios::end);
    s4.resize(file.tellg());
    file.seekg(0, std::ios::beg);

    file.read(&s4[0], s4.length());
    clock_t stop4 = clock();

    std::cout << "\ns4.length = " << s3.length();

    std::cout << "\nTime using rdbuf: " << stop1 - start1;
    std::cout << "\nTime using istream_iterator: " << stop2- start2;
    std::cout << "\nTime using istreambuf_iterator: " << stop3 - start3;
    std::cout << "\nTime using read: " << stop4 - start4;
    return 0;
}

Теперь впечатляющая часть - результаты. Сначала с VC ++ (на случай, если кому-то все равно, код Мартина достаточно быстрый, я увеличил размер файла, чтобы получить для него значимое время):

s.length () = 7669436
s2.length = 6390688
s3.length = 7669436
s4.length = 7669436
Время использования rdbuf: 184
Время использования istream_iterator: 1332
Время использования istreambuf_iterator: 249
Время использования прочитано: 48

Затем с помощью gcc (cygwin):

s.length () = 8278035
s2.length = 6390689
s3.length = 8278035
s4.length = 8278035
Время использования rdbuf: 62
Время использования istream_iterator: 2199
Время использования istreambuf_iterator: 156
Время с использованием чтения: 16

[конец редактирования - выводы остаются, хотя победитель изменился - код Мартина явно самый быстрый. ]

Результаты вполне соответствуют тому, что является самым быстрым и самым медленным. Единственное несоответствие заключается в том, насколько намного быстрее или медленнее одного другого. Хотя места размещения одинаковы, различия в скорости намного больше с gcc, чем с VC ++.

6 голосов
/ 22 июля 2010

Это должно быть все, что вам нужно:

ostringstream os;
ifstream file("name.txt");
os << file.rdbuf();

string s = os.str();

Это читает символы из file и вставляет их в поток строк.После этого он получает строку, созданную за кулисами.Обратите внимание, что я попал в следующую ловушку: Использование оператора извлечения пропустит начальный пробел.Вы должны использовать оператор вставки, как описано выше, или использовать манипулятор noskipws:

// Beware, skips initial whitespace!
file >> os.rdbuf();

// This does not skip it
file >> noskipws >> os.rdbuf(); 

Эти функции описаны как чтение потока символ за символом (хотя, не уверен, какие оптимизации здесь возможны)Я не рассчитал их, чтобы определить их скорость.

5 голосов
/ 22 июля 2010

Ради интереса, вот еще один способ сделать это:

// Beware, brain-compiled code ahead!

std::ifstream ifs( /* ... */ );
if( !ifs.good() ) return; // whatever

std::string str;

ifs.seekg( 0, std::ios_base::end );
const std::streampos pos = ifs.tellg();
ifs.seekg( 0, std::ios_base::beg );
if( pos!=std::streampos(-1) ) // can get stream size? 
  str.reserve(static_cast<std::string::size_type>(pos));

str.assign( std::istream_iterator<char>(ifs)
          , std::istream_iterator<char>() );

Надеюсь, я не слишком взорвался

2 голосов
/ 22 июля 2010
1 голос
/ 22 июля 2010

Похоже, что вы спрашиваете, как сделать операцию типа CString :: GetBuffer, ReleaseBuffer с std :: string.

Я не знаю ни одного способа сделать это напрямую, проще всего было бы просто создать необработанный буфер в стиле C, прочитать в буфер, а затем скопировать буфер в std :: string, используя assignили что угодно.Конечно, вам придется беспокоиться о проблемах переполнения буфера и т. Д. Также я бы использовал std :: autoptr для управления необработанным указателем буфера, для использования освобождения при исключении и т. Д. Это немного проще, чем использование stringstream и т. Д.пример, если необходимо.

Девин Эллингсон

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...