Прочитать весь файл ASCII в C ++ std :: string [duplicate]

473

Мне нужно прочитать целый файл в памяти и поместить его в С++ std::string.

Если бы я прочитал его в char[], ответ был бы очень простым:

std::ifstream t;
int length;
t.open("file.txt");      // open input file
t.seekg(0, std::ios::end);    // go to the end
length = t.tellg();           // report location (this is the length)
t.seekg(0, std::ios::beg);    // go back to the beginning
buffer = new char[length];    // allocate memory for a buffer of appropriate dimension
t.read(buffer, length);       // read the whole file into the buffer
t.close();                    // close file handle

// ... Do stuff with buffer here ...

Теперь я хочу сделать то же самое, но используя std::string вместо char[]. Я хочу избежать циклов, т.е. Я не хочу:

std::ifstream t;
t.open("file.txt");
std::string buffer;
std::string line;
while(t){
std::getline(t, line);
// ... Append line to buffer and go on
}
t.close()

Любые идеи?

  • 1
    Всегда будет задействован цикл, но он может быть неявным как часть стандартной библиотеки. Это приемлемо? Почему вы пытаетесь избежать петель?
  • 2
    Я считаю, что автор знал, что чтение байтов связано с циклом. Он просто хотел простой эквивалент глотка в стиле Perl. Это включало написание небольшого кода.
Показать ещё 5 комментариев
Теги:
string
caching
file-io
standard-library

9 ответов

431
Лучший ответ

Обновление:. Оказывается, этот метод, хорошо соблюдая идиомы STL, на самом деле неожиданно неэффективен! Не делайте этого с большими файлами. (См.: http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html)

Вы можете сделать итератор streambuf из файла и инициализировать строку с ним:

#include <string>
#include <fstream>
#include <streambuf>

std::ifstream t("file.txt");
std::string str((std::istreambuf_iterator<char>(t)),
                 std::istreambuf_iterator<char>());

Не уверен, откуда вы получаете синтаксис t.open("file.txt", "r"). Насколько я знаю, это не метод, который имеет std::ifstream. Похоже, вы смутили его с помощью C fopen.

Изменить: Также обратите внимание на дополнительные круглые скобки вокруг первого аргумента на конструктор строк. Это важно. Они предотвращают проблему, известную как " наиболее неприятный синтаксический анализ", который в этом случае фактически не даст вам ошибку компиляции, как это обычно бывает, но даст вам интересные (читай: неверные) результаты.

Следуя указаниям KeithB в комментариях, вот способ сделать это, который выделяет всю память спереди (вместо того, чтобы полагаться на автоматическое перераспределение строки):

#include <string>
#include <fstream>
#include <streambuf>

std::ifstream t("file.txt");
std::string str;

t.seekg(0, std::ios::end);   
str.reserve(t.tellg());
t.seekg(0, std::ios::beg);

str.assign((std::istreambuf_iterator<char>(t)),
            std::istreambuf_iterator<char>());
  • 4
    open - это определенно метод ifstream, однако 2-й параметр неверен. cplusplus.com/reference/iostream/ifstream/open
  • 1
    Правильно. Я говорил, что если у ifstream нет метода с open(const char*, const char*) подписью open(const char*, const char*)
Показать ещё 18 комментариев
620

Есть несколько возможностей. Мне нравится использовать строковый поток как промежуточный:

std::ifstream t("file.txt");
std::stringstream buffer;
buffer << t.rdbuf();

Теперь содержимое "file.txt" доступно в строке как buffer.str().

Другая возможность (хотя мне это тоже не нравится) гораздо больше напоминает ваш оригинал:

std::ifstream t("file.txt");
t.seekg(0, std::ios::end);
size_t size = t.tellg();
std::string buffer(size, ' ');
t.seekg(0);
t.read(&buffer[0], size); 

Официально это не требуется для работы по стандарту С++ 98 или 03 (строка не требуется для хранения данных смежно), но на самом деле она работает со всеми известными реализациями, а С++ 11 и более поздние версии требуют непрерывного хранения, поэтому он гарантирует работу с ними.

Что касается того, почему мне тоже не нравится последнее: во-первых, потому что это больше и труднее читать. Во-вторых, потому что это требует, чтобы вы инициализировали содержимое строки данными, которые вам не нужны, затем сразу же напишите эти данные (да, время инициализации обычно тривиально по сравнению с чтением, поэтому, вероятно, это не имеет значения, но для меня это все еще кажется неправильным). В-третьих, в текстовом файле позиция X в файле не обязательно означает, что вы будете читать X-символы для достижения этой точки - не нужно учитывать такие вещи, как переводы на конец строки. В реальных системах, которые выполняют такие переводы (например, Windows), переведенная форма короче, чем в файле (т.е. "\ R\n" в файле становится "\n" в переведенной строке), так что все, что вы сделали зарезервировано немного лишнего пространства, которое вы никогда не используете. Опять же, на самом деле не вызывает серьезной проблемы, но в любом случае кажется немного неправильным.

  • 29
    Трехсторонний лайнер работает как шарм!
  • 76
    Это должно было быть отмечено как ответ.
Показать ещё 25 комментариев
47

Я думаю, лучший способ - использовать строковый поток. просто и быстро!!!

ifstream inFile;
inFile.open(inFileName);//open the input file

stringstream strStream;
strStream << inFile.rdbuf();//read the file
string str = strStream.str();//str holds the content of the file

cout << str << endl;//you can do anything with the string!!!
  • 2
    Просто и быстро, верно! insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html
  • 3
    Не забудьте закрыть поток потом ...
Показать ещё 3 комментария
9

Вы не можете найти это в любой книге или на сайте, но я узнал, что он работает очень хорошо:

ifstream ifs ("filename.txt");
string s;
getline (ifs, s, (char) ifs.eof());
  • 5
    eof к (char) немного хитроумно, предполагая некоторую актуальность и универсальность, которая иллюзорна. Для некоторых возможных значений eof() и знакового char он даст результаты, определенные реализацией. Непосредственное использование, например, char(0) / '\0' было бы более надежным и честным показателем того, что происходит.
  • 2
    @TonyD. Хороший вопрос о преобразовании eof () в char. Я полагаю, для старых наборов символов ascii, передача любого отрицательного значения (msb, установленного в 1) будет работать. Но передача \ 0 (или отрицательное значение) не будет работать для широких или многобайтовых входных файлов.
Показать ещё 1 комментарий
5

Попробуйте один из этих двух методов:

string get_file_string(){
    std::ifstream ifs("path_to_file");
    return string((std::istreambuf_iterator<char>(ifs)),
                  (std::istreambuf_iterator<char>()));
}

string get_file_string2(){
    ifstream inFile;
    inFile.open("path_to_file");//open the input file

    stringstream strStream;
    strStream << inFile.rdbuf();//read the file
    return strStream.str();//str holds the content of the file
}
2

Я выяснил другой способ, который работает с большинством istreams, включая std:: cin!

std::string readFile()
{
stringstream str;
ifstream stream("Hello_World.txt");
if(stream.is_open())
{
    while(stream.peek() != EOF)
    {
        str << (char) stream.get();
    }
    stream.close();
    return str.str();
}
}
1

Если вы используете glibmm, вы можете попробовать Glib:: file_get_contents.

#include <iostream>
#include <glibmm.h>

int main() {
    auto filename = "my-file.txt";
    try {
        std::string contents = Glib::file_get_contents(filename);
        std::cout << "File data:\n" << contents << std::endl;
    catch (const Glib::FileError& e) {
        std::cout << "Oops, an error occurred:\n" << e.what() << std::endl;
    }

    return 0;
}
  • 0
    Imho: Хотя это работает, предоставление «glib» решения, которое не является независимым от платформы эквивалентом сундука Пандоры, может сильно запутать, даже больше, если есть простое CPP-стандартное решение для него.
1

Я мог бы сделать это вот так:

void readfile(const std::string &filepath,std::string &buffer){
    std::ifstream fin(filepath.c_str());
    getline(fin, buffer, char(-1));
    fin.close();
}

Если это что-то, на что можно не согласиться, пожалуйста, сообщите мне, почему

  • 4
    char (-1), вероятно, не является переносимым способом обозначения EOF. Кроме того, реализации getline () не обязаны поддерживать "недопустимый" псевдосимвол EOF как символ разделителя, я думаю.
-5

Я не думаю, что вы можете сделать это без явного или неявного цикла, не прочитав сначала массив char (или какой-либо другой контейнер) и десять построив строку. Если вам не нужны другие возможности строки, это можно сделать с помощью vector<char> так же, как вы в настоящее время используете char *.

  • 14
    -1 Не правда ... Смотри выше
Сообщество Overcoder
Наверх
Меню