Будучи большим поклонником абстракций итераторов C ++ и их алгоритмов, мне бы очень хотелось, чтобы это был быстрый способ чтения файла (или любого другого входного потока) в std::string
(а затем распечатать содержимое):
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
int main()
{
std::string s(std::istreambuf_iterator<char>(std::ifstream("file")
>> std::skipws),
std::istreambuf_iterator<char>());
std::cout << "file='" << s << "'\n";
}
Это, конечно, быстро для моей собственной реализации IOStreams, но для ее быстрого выполнения требуется много хитрости.Прежде всего, он требует оптимизации алгоритмов для обработки сегментированных последовательностей : поток можно рассматривать как последовательность входных буферов.Я не знаю ни о какой реализации STL, последовательно выполняющей эту оптимизацию.Необычное использование std::skipws
просто для получения ссылки на только что созданный поток: std::istreambuf_iterator<char>
ожидает ссылку, с которой не будет связан временный файловый поток.
Поскольку это, вероятно, не самый быстрыйПодход, я был бы склонен использовать std::getline()
с определенным символом "новой строки", то есть на котором нет в файле:
std::string s;
// optionally reserve space although I wouldn't be too fuzzed about the
// reallocations because the reads probably dominate the performances
std::getline(std::ifstream("file") >> std::skipws, s, 0);
Это предполагает, что файл не содержит нулевой символ,Любой другой персонаж будет делать то же самое.К сожалению, std::getline()
принимает char_type
в качестве аргумента разграничения, а не int_type
, который член std::istream::getline()
принимает за разделитель: в этом случае вы можете использовать eof()
для символа, который никогда не встречается (char_type
, int_type
и eof()
относятся к соответствующему элементу char_traits<char>
).Версия участника, в свою очередь, не может быть использована, потому что вам нужно знать заранее, сколько символов в файле.
Кстати, я видел несколько попыток использовать поиск для определения размерафайл.Это не должно работать слишком хорошо.Проблема в том, что преобразование кода, выполненное в std::ifstream
(ну, на самом деле в std::filebuf
), может создать количество символов, отличное от количества байтов в файле.Следует признать, что это не тот случай, когда используется стандартная локаль C, и можно обнаружить, что она не выполняет никакого преобразования.В противном случае лучшим вариантом для потока было бы запустить файл и определить количество создаваемых символов.Я действительно думаю, что это то, что нужно сделать, когда преобразование кода может что-то интересное, хотя я не думаю, что это на самом деле сделано.Тем не менее, ни в одном из примеров явно не указана локаль C, например, std::locale::global(std::locale("C"));
.Даже при этом также необходимо открыть файл в режиме std::ios_base::binary
, так как в противном случае последовательности конца строки могут быть заменены одним символом при чтении.Следует признать, что это только сделает результат короче, а не длиннее.
Другие подходы, использующие извлечение из std::streambuf*
(т. Е. Те, которые включают rdbuf()
), требуют, чтобы результирующий контент был скопирован в какой-то момент.Учитывая, что файл на самом деле может быть очень большим, это может быть не вариант.Однако без копии это может быть самым быстрым подходом.Чтобы избежать копирования, можно было бы создать простой пользовательский потоковый буфер, который принимает ссылку на std::string
в качестве аргумента конструктора и непосредственно добавляет к этому std::string
:
#include <fstream>
#include <iostream>
#include <string>
class custombuf:
public std::streambuf
{
public:
custombuf(std::string& target): target_(target) {
this->setp(this->buffer_, this->buffer_ + bufsize - 1);
}
private:
std::string& target_;
enum { bufsize = 8192 };
char buffer_[bufsize];
int overflow(int c) {
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
*this->pptr() = traits_type::to_char_type(c);
this->pbump(1);
}
this->target_.append(this->pbase(), this->pptr() - this->pbase());
this->setp(this->buffer_, this->buffer_ + bufsize - 1);
return traits_type::not_eof(c);
}
int sync() { this->overflow(traits_type::eof()); return 0; }
};
int main()
{
std::string s;
custombuf sbuf(s);
if (std::ostream(&sbuf)
<< std::ifstream("readfile.cpp").rdbuf()
<< std::flush) {
std::cout << "file='" << s << "'\n";
}
else {
std::cout << "failed to read file\n";
}
}
По крайней мере снадлежащим образом выбранный буфер, я ожидаю, что версия будет довольно быстрой.Какая версия является самой быстрой, безусловно, будет зависеть от системы, используемой стандартной библиотеки C ++ и, возможно, ряда других факторов, например, вы хотите измерить производительность.