Как мне прочитать весь файл в std :: string в C ++?

Как мне прочитать файл в std::string, т. Е. Прочитать весь файл сразу?

Текстовый или двоичный режим должен быть указан вызывающим. Решение должно быть совместимым со стандартами, портативным и эффективным. Он не должен без необходимости копировать данные строки и должен избегать перераспределения памяти при чтении строки.

Один из способов сделать это - установить размер файла, изменить размер std::string и fread() на const_cast<char*>()'ed data() std::string. Для этого требуется, чтобы данные std::string были непрерывными, что не требуется стандартом, но, похоже, так обстоит дело для всех известных реализаций. Что еще хуже, если файл читается в текстовом режиме, размер std::string может не совпадать с размером файла.

Полностью правильные, совместимые со стандартами и переносимые решения могут быть созданы с использованием rdbuf() std::ifstream в std::ostringstream, а оттуда в std::string. Однако это может скопировать строковые данные и / или без необходимости перераспределить память.

  • Достаточно ли умны все соответствующие реализации стандартных библиотек, чтобы избежать ненужных накладных расходов?
  • Есть другой способ сделать это?
  • Я пропустил какую-то скрытую функцию Boost, которая уже обеспечивает желаемую функциональность?


void slurp(std::string& data, bool is_binary)

person Community    schedule 22.09.2008    source источник
comment
Обратите внимание, что у вас все еще есть недоопределенные параметры. Например, какова кодировка символов в файле? Будете ли вы пытаться автоматически определять (что работает только в некоторых конкретных случаях)? Будете ли вы уважать, например, Заголовки XML сообщают вам кодировку файла? Также нет таких вещей, как текстовый режим или двоичный режим - вы думаете о FTP?   -  person Jason Cohen    schedule 22.09.2008
comment
Текстовый и двоичный режимы - это специальные хаки для MSDOS и Windows, которые пытаются обойти тот факт, что новые строки представлены двумя символами в Windows (CR / LF). В текстовом режиме они обрабатываются как один символ ('\ n').   -  person Ferruccio    schedule 22.09.2008
comment
Обычно такие вещи лечатся подпрограммами, которые разбивают строки на строки, а не подпрограммами, считывающими данные из файлов. То есть в каждой среде, в которой я программировал, есть какие-то функции readAsLines () или breakIntoLines (), которые разбираются в таких вещах.   -  person Jason Cohen    schedule 22.09.2008
comment
Хотя это не (совсем) точная копия, это тесно связано с: как предварительно выделить память для объекта std :: string? (который, в отличие от приведенного выше утверждения Конрада, включал код для этого, читая файл прямо в место назначения, без дополнительной копии).   -  person Jerry Coffin    schedule 21.09.2012
comment
прилегающий не требуется стандартом - да, окольным путем. Как только вы используете op [] в строке, она должна быть объединена в непрерывный записываемый буфер, поэтому запись в & str [0] гарантированно безопасна, если сначала вы .resize () достаточно большой. А в C ++ 11 строка просто всегда непрерывна.   -  person Tino Didriksen    schedule 19.07.2013
comment
Связанная ссылка: Как читать файл в C ++? - тесты и обсуждение различных подходов. И да, rdbuf (тот, что в принятом ответе) не самый быстрый, read.   -  person legends2k    schedule 27.11.2014
comment
Все эти решения приведут к неправильному формату строк, если ваша кодировка / интерпратация файла неверна. У меня была действительно странная проблема при сериализации файла JSON в строку, пока я вручную не преобразовал его в UTF-8; Независимо от того, какое решение я пробовал, я получал только первого персонажа! Просто нужно остерегаться! :)   -  person kayleeFrye_onDeck    schedule 01.11.2018


Ответы (15)


Один из способов - сбросить буфер потока в отдельный поток памяти, а затем преобразовать его в std::string:

std::string slurp(std::ifstream& in) {
    std::ostringstream sstr;
    sstr << in.rdbuf();
    return sstr.str();
}

Это красиво лаконично. Однако, как указано в вопросе, это выполняет дублирующую копию, и, к сожалению, принципиально нет способа удалить эту копию.

К сожалению, единственное реальное решение, позволяющее избежать дублирования копий, - это выполнять чтение вручную в цикле. Поскольку в C ++ теперь гарантированы непрерывные строки, можно написать следующее (≥C ++ 14):

auto read_file(std::string_view path) -> std::string {
    constexpr auto read_size = std::size_t{4096};
    auto stream = std::ifstream{path.data()};
    stream.exceptions(std::ios_base::badbit);

    auto out = std::string{};
    auto buf = std::string(read_size, '\0');
    while (stream.read(& buf[0], read_size)) {
        out.append(buf, 0, stream.gcount());
    }
    out.append(buf, 0, stream.gcount());
    return out;
}
person Konrad Rudolph    schedule 22.09.2008
comment
Какой смысл делать это одним лайнером? Я всегда выбираю разборчивый код. Как самопровозглашенный энтузиаст VB.Net (IIRC), я думаю, вы должны понять это мнение? - person sehe; 21.09.2012
comment
@sehe: Я ожидал, что любой наполовину компетентный программист на C ++ легко поймет эту однострочную формулировку. Это довольно скучно по сравнению с другими существующими вещами. - person DevSolar; 21.09.2012
comment
@DevSolar Что ж, более разборчивая версия на ~ 30% короче, без приведения и в остальном эквивалентна. Поэтому мой вопрос остается в силе: какой смысл делать его одинарным? - person sehe; 21.09.2012
comment
@sehe Я никогда не говорил, что буду использовать oneliner в реальном коде. Это было больше для того, чтобы показать, что это можно сделать одним выражением. - person Konrad Rudolph; 24.09.2012
comment
@KonradRudolph Wokay. Рад это знать. Я просто немного сбит с толку, что вы тогда об этом упомянули. В любом случае, я поддержу ваш другой ответ (??!), А затем за уловку :) - person sehe; 24.09.2012
comment
Я знаю, что это очень давно, но я только что провел профилирование нескольких методов и обнаружил, что получение размера файла и вызов in.read в буфер, предварительно выделенный для правильного размера, намного быстрее, чем это. Около 10x. Я использую VS2012 и тестирую файл размером 100 МБ. - person David; 14.05.2013
comment
@Dave Минимально быстрее - возможно. 10x? Это намекает на дефект в реализации стандартной библиотеки. - person Konrad Rudolph; 14.05.2013
comment
Просто хотел добавить, что тому, кто изучает C ++, это с первого взгляда сложно понять. - person Rahul Iyer; 13.10.2014
comment
@John Вот почему вы положили его на нормальное функционирование. Большинство нетривиальных кодов трудно понять новичкам, если бы это было аргументом против использования такого кода, мы бы никогда не справились с работой. - person Konrad Rudolph; 13.10.2014
comment
Примечание: этот метод считывает файл в буфер строкового потока, а затем копирует весь этот буфер в string. Т.е. требует вдвое больше памяти, чем некоторые другие варианты. (Нет возможности переместить буфер). Для большого файла это было бы значительным штрафом, возможно, даже вызвав сбой выделения. - person M.M; 06.02.2016
comment
@ M.M Хороший момент, я не знаю, как это так долго оставалось незамеченным. - person Konrad Rudolph; 06.02.2016
comment
@sehe Как бы то ни было, я очень ценю лаконичность. Я не хочу вводить новую функцию только ради того, что в моей текущей программе является второстепенным функционалом, предполагающим чтение одной строки из файла для неважной цели. Просто требование добавить функцию для этого заставит меня даже не читать строку. В моем случае наличие одной строки кода позволяет одной строке кода не выделяться, поэтому я с радостью делаю это таким образом! - person Dan Nissenbaum; 12.02.2016
comment
@DanNissenbaum Вы что-то путаете. Краткость действительно важна в программировании, но правильный способ добиться этого - разложить проблему на части и инкапсулировать их в независимые единицы (функции, классы и т. Д.). Добавление функций не умаляет краткости; наоборот. - person Konrad Rudolph; 12.02.2016
comment
@KonradRudolph, я тебя слышу. С годами я отошел от добавления функций и классов для одноразового использования, потому что само их присутствие придает вес их важности. Приятно иметь возможность взглянуть на код и увидеть простой небольшой набор функций и классов, представляющих основные функции. Я начал использовать «правило трех» - если короткий блок кода используется только один или даже два раза, преимущество отсутствия функции может перевесить преимущество инкапсуляции. Лишь к тому времени, когда он дойдет до третьего использования, я иногда буду склонен инкапсулировать его. Этот "прихлебывающий файл" подходит. - person Dan Nissenbaum; 12.02.2016
comment
@DanNissenbaum, поэтому были введены лямбды :) - person Ruslan; 29.06.2016
comment
Я думаю, что это решение работает, только если вы хотите читать файл в двоичном режиме. Если вы хотите читать его в текстовом режиме, istream_iterator - самый чистый способ. Это верно? - person Maxpm; 22.05.2017
comment
Этот способ медленный (потому что std :: stringstream медленный). - person Galik; 16.07.2018
comment
@Galik Медленно по сравнению с чем? Чтение в поток строк происходит очень быстро. Проблема в том, что строковые данные нельзя вывести из потока, их нужно скопировать. - person Konrad Rudolph; 16.07.2018
comment
Почему не dynamic_cast вместо static_cast? Разве мы не просто опускаемся? - person Ayxan Haqverdili; 03.03.2019
comment
@Ayxan Использование dynamic_cast действительно имеет смысл только в том случае, если вы не знаете, будет ли приведение успешным, и проверить возвращаемое значение (или уловить потенциал bad_cast). Однако мы знаем, что актерский состав здесь преуспел, поэтому нет необходимости хеджировать наши ставки. В идеале мы должны использовать приведение, которое только выполняет понижающее приведение и в то же время утверждает, что приведение будет успешным. Увы, в C ++ такого приведения не существует. - person Konrad Rudolph; 03.03.2019
comment
Будет ли этот метод вызывать перераспределение памяти много раз? - person coin cheung; 11.03.2020
comment
это решение короткое, но запутанное. rdbuf() возвращает filebuf*. Как указатель на rdbuf заставляет stringstream читать содержимое файла? Я бы предпочел более подробный, но более понятный код, чем это волшебство. - person anton_rh; 27.04.2020
comment
@anton_rh Это не волшебство, но для этого нужно знать, как работают соответствующие участники, что задокументировано. Кажется, вам не хватает перегрузки (9) на этой странице: en.cppreference .com / w / cpp / io / basic_ostream / operator_ltlt - person Konrad Rudolph; 27.04.2020

Самый короткий вариант: Live On Coliru

std::string str(std::istreambuf_iterator<char>{ifs}, {});

Требуется заголовок <iterator>.

Были сообщения, что этот метод медленнее, чем предварительное выделение строки и использование std::istream::read. Однако в современном компиляторе с включенной оптимизацией этого больше не происходит, хотя относительная производительность различных методов, по-видимому, сильно зависит от компилятора.

person Konrad Rudolph    schedule 22.09.2008
comment
Не могли бы вы подробно рассказать об этом ответе. Насколько это эффективно, читает ли он файл по символу за раз, во всяком случае, для предварительного выделения оперативной памяти? - person Martin Beckett; 22.09.2008
comment
@ M.M Как я прочитал это сравнение, этот метод медленнее, чем чистый C ++ метод чтения в предварительно выделенный буфер. - person Konrad Rudolph; 06.02.2016
comment
Вы правы, это тот случай, когда заголовок находится под образцом кода, а не над ним :) - person M.M; 06.02.2016
comment
Будет ли этот метод вызывать перераспределение памяти много раз? - person coin cheung; 11.03.2020
comment
@coincheung К сожалению, да. Если вы хотите избежать выделения памяти, вам необходимо вручную буферизовать чтение. Потоки ввода-вывода C ++ - это довольно дерьмо. - person Konrad Rudolph; 16.03.2020
comment
@KonradRudolph Спасибо, я заметил, что есть еще один способ: stringstream ss; ifs >> ss.rdbuf(); str = ss.str();, этот метод также вызовет много перераспределений памяти, пожалуйста? - person coin cheung; 16.03.2020
comment
@coincheung Это должно избегать повторных распределений, но на практике это глупо. «Канонический» способ чтения всего файла в C ++ 17 - gist.github.com/klmr/ 849cbb0c6e872dff0fdcc54787a66103. К сожалению, очень многословно. - person Konrad Rudolph; 16.03.2020

См. этот ответ на аналогичной вопрос.

Для вашего удобства я перепубликую решение CTT:

string readFile2(const string &fileName)
{
    ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);

    ifstream::pos_type fileSize = ifs.tellg();
    ifs.seekg(0, ios::beg);

    vector<char> bytes(fileSize);
    ifs.read(bytes.data(), fileSize);

    return string(bytes.data(), fileSize);
}

Это решение привело к увеличению времени выполнения примерно на 20%, чем другие ответы, представленные здесь, если взять в среднем 100 прогонов по тексту Моби Дика (1,3M). Неплохо для портативного решения на C ++, хотелось бы увидеть результаты mmap'ing файла;)

person paxos1977    schedule 08.02.2009
comment
связанные: сравнение производительности различных методов по времени: Чтение всего файла сразу на C ++ - person jfs; 03.12.2014
comment
До сегодняшнего дня я ни разу не видел, чтобы tellg () сообщал о результатах, отличных от размера файла. На поиск источника ошибки у меня ушло несколько часов. Пожалуйста, не используйте tellg () для получения размера файла. stackoverflow.com / questions / 22984956 / - person Puzomor Croatia; 27.12.2016
comment
не следует ли звонить ifs.seekg(0, ios::end) перед tellg? сразу после открытия файла указатель чтения находится в начале, поэтому tellg возвращает ноль - person Andriy Tylychko; 09.02.2017
comment
также вам необходимо проверить наличие пустых файлов, так как вы будете разыменовывать nullptr на &bytes[0] - person Andriy Tylychko; 09.02.2017
comment
хорошо, я пропустил ios::ate, поэтому думаю, что версия с явным перемещением в конец была бы более читабельной - person Andriy Tylychko; 09.02.2017
comment
Обратите внимание, что это решение работает только в двоичном режиме; тогда как OP запросил решение как для двоичного, так и для текстового режима. - person M.M; 12.06.2018
comment
Поскольку строки C ++ 11 гарантированно имеют непрерывное хранилище, вы можете напрямую использовать строку вместо вектора и, таким образом, пропустить вектор для копирования строки. - person syam; 18.12.2018
comment
Это решение не переносимо, поскольку результат .tellg() не гарантирует возврата размера файла. (а на практике некоторые системы этого не делают). - person spectras; 25.02.2021
comment
@spectras можете ли вы указать на систему или реализацию C ++, которая, как известно, не возвращает смещение в байтах? - person paxos1977; 15.03.2021
comment
@ paxos1977 ›определять, в каких системах ваша программа определена как правильная, зависит от вас. Как есть, он полагается на гарантии, которые не предоставляются C ++, и поэтому неверен. Если он работает с известным набором реализаций, которые действительно предоставляют такие гарантии (как в: задокументированы как гарантии, а не просто, что сегодня это выглядит нормально в той версии, которая у меня есть), тогда сделайте это явным, иначе это вводит в заблуждение. - person spectras; 16.03.2021
comment
@spectras Я согласен, что стандарт не гарантирует, что это переносимо для всех реализаций b / c, это зависит от конкретного поведения реализации ... однако он сломан только на какой-то неизвестной, безымянной, теоретической реализации C ++, которая использует токены вместо байтового смещения для tellg (). Вы не можете назвать реализацию, в которой это не сработало бы, и я тоже, поэтому я думаю, что это достаточно переносимо. - person paxos1977; 18.03.2021
comment
Прекрасный аргумент для создания хрупких кодовых баз, которые неожиданно ломаются, потому что любое поведение, которое я однажды наблюдал, было достаточно переносимым. Пока кто-то его не изменил. Не то чтобы у нас история повторялась снова и снова. - Надлежащее проектирование осуществляется с помощью гарантий, а не исследования того, что кажется работающим сейчас, и надежды на лучшее. Таким образом: этот код является только надежной инженерной реализацией, где его предположения гарантированы. [примечание: я не говорил о том, сработает это сегодня или нет, это не имеет значения] - person spectras; 18.03.2021
comment
… В остальном это не лучше, чем использование после удаления или висячая ссылка, но она никогда не сбивалась ни в одном месте, где я ее запускал, так что это достаточно переносимо. - person spectras; 18.03.2021
comment
@spectras Я бы не стал помещать поведение, определяемое реализацией, в тот же класс ошибки, что и использование после свободной или висячей ссылки. Использование после бесплатных и оборванных ссылок всегда везде ломается. Вы преувеличиваете. - person paxos1977; 19.03.2021
comment
@spectras Надлежащее проектирование осуществляется на основе гарантий, а не проверки того, что кажется работающим сейчас, и надежды на лучшее, о чем говорят, как студент, который никогда не писал и не поддерживал настоящую производственную базу кода. В реальном мире вы всегда точно знаете, на какие платформы вы нацеливаетесь и с какими компиляторами. Если поведение, определенное реализацией, дает вам лучшую производительность, вы делаете это и отмечаете проблему в журнале фиксации и комментариях в коде. Если производительность не имеет значения, вы бы реализовали использование наиболее читаемого кода, а не самого быстрого. - person paxos1977; 19.03.2021
comment
… Или говоря как опытный профессионал, который видел так много, что никогда не должно происходить ошибок, работая над долгоживущими кодовыми базами, они узнали, что этого никогда не должно происходить, мы никогда не будем нацеливаться на другую платформу , компилятор никогда не получит новую версию не являются частью реальной производственной кодовой базы для компании, которая переживает свой первоначальный запуск продукта. Поэтому, когда вы в конечном итоге полагаетесь на них, самый минимум - это четко пометить это и провести для этого модульный тест. - person spectras; 19.03.2021

Если у вас C ++ 17 (std :: filesystem), есть также этот способ (который получает размер файла через std::filesystem::file_size вместо seekg и tellg):

#include <filesystem>
#include <fstream>
#include <string>

namespace fs = std::filesystem;

std::string readFile(fs::path path)
{
    // Open the stream to 'lock' the file.
    std::ifstream f(path, std::ios::in | std::ios::binary);

    // Obtain the size of the file.
    const auto sz = fs::file_size(path);

    // Create a buffer.
    std::string result(sz, '\0');

    // Read the whole file into the buffer.
    f.read(result.data(), sz);

    return result;
}

Примечание: вам может потребоваться использовать <experimental/filesystem> и std::experimental::filesystem, если ваша стандартная библиотека еще не полностью поддерживает C ++ 17. Вам также может потребоваться заменить result.data() на &result[0], если он не поддерживает non-const std :: данные basic_string.

person Gabriel Majeri    schedule 01.12.2016
comment
Это может вызвать неопределенное поведение; открытие файла в текстовом режиме дает другой поток, чем файл на диске в некоторых операционных системах. - person M.M; 12.06.2018
comment
Первоначально разработан как boost::filesystem, поэтому вы также можете использовать boost, если у вас нет C ++ 17 - person Gerhard Burger; 29.09.2018
comment
Открытие файла с помощью одного API и получение его размера с помощью другого, похоже, требует несогласованности и условий гонки. - person Arthur Tacca; 24.10.2018

Использовать

#include <iostream>
#include <sstream>
#include <fstream>

int main()
{
  std::ifstream input("file.txt");
  std::stringstream sstr;

  while(input >> sstr.rdbuf());

  std::cout << sstr.str() << std::endl;
}

или что-то очень близкое. У меня нет открытой ссылки на stdlib, чтобы перепроверить себя.

Да, я понимаю, что написал функцию slurp не так, как просили.

person Ben Collins    schedule 22.09.2008
comment
Выглядит красиво, но не компилируется. Изменения для компиляции сводят его к другим ответам на этой странице. ideone.com/EyhfWm - person JDiMatteo; 29.06.2015
comment
Почему цикл while? - person Zitrax; 19.10.2017
comment
Согласовано. Когда operator>> читает в std::basic_streambuf, он потребляет (то, что осталось) входной поток, поэтому цикл не нужен. - person Remy Lebeau; 30.12.2018

У меня недостаточно репутации, чтобы напрямую комментировать ответы с помощью tellg().

Имейте в виду, что tellg() может вернуть -1 в случае ошибки. Если вы передаете результат tellg() в качестве параметра распределения, вы должны сначала проверить результат.

Пример проблемы:

...
std::streamsize size = file.tellg();
std::vector<char> buffer(size);
...

В приведенном выше примере, если tellg() обнаруживает ошибку, он вернет -1. Неявное преобразование между подписанным (т.е. результатом tellg()) и беззнаковым (т.е. аргументом конструктора vector<char>) приведет к тому, что ваш вектор ошибочно выделит очень большое количество байтов. (Вероятно, 4294967295 байт или 4 ГБ.)

Изменение ответа paxos1977 для учета вышеуказанного:

string readFile2(const string &fileName)
{
    ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);

    ifstream::pos_type fileSize = ifs.tellg();
    if (fileSize < 0)                             <--- ADDED
        return std::string();                     <--- ADDED

    ifs.seekg(0, ios::beg);

    vector<char> bytes(fileSize);
    ifs.read(&bytes[0], fileSize);

    return string(&bytes[0], fileSize);
}
person Rick Ramstetter    schedule 24.03.2017
comment
Более того, tellg() возвращает не размер, а токен. Многие системы используют байтовое смещение в качестве токена, но это не гарантируется, а некоторые системы этого не делают. Посмотрите пример в этом ответе. - person spectras; 25.02.2021

Это решение добавляет проверку ошибок к методу, основанному на rdbuf ().

std::string file_to_string(const std::string& file_name)
{
    std::ifstream file_stream{file_name};

    if (file_stream.fail())
    {
        // Error opening file.
    }

    std::ostringstream str_stream{};
    file_stream >> str_stream.rdbuf();  // NOT str_stream << file_stream.rdbuf()

    if (file_stream.fail() && !file_stream.eof())
    {
        // Error reading file.
    }

    return str_stream.str();
}

Я добавляю этот ответ, потому что добавление проверки ошибок к исходному методу не так тривиально, как вы ожидали. В исходном методе используется оператор вставки строкового потока (str_stream << file_stream.rdbuf()). Проблема в том, что это устанавливает бит отказа строкового потока, когда символы не вставлены. Это может быть из-за ошибки или из-за того, что файл пуст. Если вы проверяете наличие сбоев, проверяя бит сбоя, вы столкнетесь с ложным срабатыванием при чтении пустого файла. Как вы устраняете легитимную ошибку при вставке любых символов и «отказ» при вставке каких-либо символов из-за того, что файл пуст?

Вы можете подумать о явной проверке пустого файла, но это больше кода и связанной с ним проверки ошибок.

Проверка условия сбоя str_stream.fail() && !str_stream.eof() не работает, потому что операция вставки не устанавливает eofbit (ни в ostringstream, ни в ifstream).

Итак, решение - изменить операцию. Вместо использования оператора вставки ostringstream (‹<) используйте оператор извлечения ifstream (>>), который устанавливает eofbit. Затем проверьте условие отказа file_stream.fail() && !file_stream.eof().

Важно отметить, что когда file_stream >> str_stream.rdbuf() обнаруживает законный сбой, он никогда не должен устанавливать eofbit (в соответствии с моим пониманием спецификации). Это означает, что вышеуказанной проверки достаточно для обнаружения законных сбоев.

person tgnottingham    schedule 26.03.2017

Что-то вроде этого не должно быть так уж плохо:

void slurp(std::string& data, const std::string& filename, bool is_binary)
{
    std::ios_base::openmode openmode = ios::ate | ios::in;
    if (is_binary)
        openmode |= ios::binary;
    ifstream file(filename.c_str(), openmode);
    data.clear();
    data.reserve(file.tellg());
    file.seekg(0, ios::beg);
    data.append(istreambuf_iterator<char>(file.rdbuf()), 
                istreambuf_iterator<char>());
}

Преимущество здесь в том, что мы сначала делаем резерв, поэтому нам не придется увеличивать строку по мере чтения. Недостатком является то, что мы делаем это char за char. Более умная версия могла бы захватить весь буфер чтения и затем вызвать недополнение.

person Matt Price    schedule 22.09.2008
comment
Вы должны проверить версию этого кода, которая использует std :: vector для начального чтения, а не строку. Намного намного быстрее. - person paxos1977; 08.02.2009

Вот версия, использующая новую библиотеку файловой системы с достаточно надежной проверкой ошибок:

#include <cstdint>
#include <exception>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>

namespace fs = std::filesystem;

std::string loadFile(const char *const name);
std::string loadFile(const std::string &name);

std::string loadFile(const char *const name) {
  fs::path filepath(fs::absolute(fs::path(name)));

  std::uintmax_t fsize;

  if (fs::exists(filepath)) {
    fsize = fs::file_size(filepath);
  } else {
    throw(std::invalid_argument("File not found: " + filepath.string()));
  }

  std::ifstream infile;
  infile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
  try {
    infile.open(filepath.c_str(), std::ios::in | std::ifstream::binary);
  } catch (...) {
    std::throw_with_nested(std::runtime_error("Can't open input file " + filepath.string()));
  }

  std::string fileStr;

  try {
    fileStr.resize(fsize);
  } catch (...) {
    std::stringstream err;
    err << "Can't resize to " << fsize << " bytes";
    std::throw_with_nested(std::runtime_error(err.str()));
  }

  infile.read(fileStr.data(), fsize);
  infile.close();

  return fileStr;
}

std::string loadFile(const std::string &name) { return loadFile(name.c_str()); };
person David G    schedule 06.11.2019
comment
infile.open также может принимать std::string без преобразования с .c_str() - person Matt Eding; 02.01.2020
comment
filepath не std::string, это std::filesystem::path. Оказывается, std::ifstream::open тоже может принять один из них. - person David G; 10.01.2020
comment
@DavidG, std::filesystem::path неявно конвертируется в std::string - person Jeffrey Cash; 07.02.2020
comment
Согласно cppreference.com, функция-член ::open на std::ifstream, которая принимает std::filesystem::path, работает так, как если бы метод ::c_str() был вызван на пути. Базовый ::value_type путей - это char в соответствии с POSIX. - person David G; 18.02.2020

Поскольку это похоже на широко используемую утилиту, мой подход заключался бы в поиске и предпочтении уже доступных библиотек решениям, сделанным вручную, особенно если библиотеки boost уже связаны (флаги компоновщика -lboost_system -lboost_filesystem) в вашем проекте. Здесь (и более старые версии Boost) boost предоставляет утилита load_string_file:

#include <iostream>
#include <string>
#include <boost/filesystem/string_file.hpp>

int main() {
    std::string result;
    boost::filesystem::load_string_file("aFileName.xyz", result);
    std::cout << result.size() << std::endl;
}

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

person b.g.    schedule 11.09.2020

Вы можете использовать функцию std :: getline и указать eof в качестве разделителя. Однако полученный код немного неясен:

std::string data;
std::ifstream in( "test.txt" );
std::getline( in, data, std::string::traits_type::to_char_type( 
                  std::string::traits_type::eof() ) );
person Martin Cote    schedule 22.09.2008
comment
Я только что протестировал это, похоже, это намного медленнее, чем получение размера файла и вызов чтения для всего размера файла в буфер. Примерно в 12 раз медленнее. - person David; 14.05.2013
comment
Это будет работать только до тех пор, пока в вашем файле нет символов eof (например, 0x00, 0xff, ...). Если есть, вы прочитаете только часть файла. - person Olaf Dietsche; 12.08.2017

#include <string>
#include <sstream>

using namespace std;

string GetStreamAsString(const istream& in)
{
    stringstream out;
    out << in.rdbuf();
    return out.str();
}

string GetFileAsString(static string& filePath)
{
    ifstream stream;
    try
    {
        // Set to throw on failure
        stream.exceptions(fstream::failbit | fstream::badbit);
        stream.open(filePath);
    }
    catch (system_error& error)
    {
        cerr << "Failed to open '" << filePath << "'\n" << error.code().message() << endl;
        return "Open fail";
    }

    return GetStreamAsString(stream);
}

использование:

const string logAsString = GetFileAsString(logFilePath);
person Paul Sumpner    schedule 17.09.2019

Обновленная функция, основанная на решении CTT:

#include <string>
#include <fstream>
#include <limits>
#include <string_view>
std::string readfile(const std::string_view path, bool binaryMode = true)
{
    std::ios::openmode openmode = std::ios::in;
    if(binaryMode)
    {
        openmode |= std::ios::binary;
    }
    std::ifstream ifs(path.data(), openmode);
    ifs.ignore(std::numeric_limits<std::streamsize>::max());
    std::string data(ifs.gcount(), 0);
    ifs.seekg(0);
    ifs.read(data.data(), data.size());
    return data;
}

Есть два важных отличия:

tellg() не гарантирует возврат смещения в байтах с начала файла. Вместо этого, как указал Пузомор Хорватия, это скорее токен, который можно использовать в вызовах fstream. gcount() однако возвращает количество последних извлеченных неформатированных байтов. Поэтому мы открываем файл, извлекаем и отбрасываем все его содержимое с помощью ignore(), чтобы получить размер файла, и на его основе строим строку вывода.

Во-вторых, нам не нужно копировать данные файла из std::vector<char> в std::string путем прямой записи в строку.

С точки зрения производительности это должно быть самым быстрым, если заранее выделить строку подходящего размера и вызвать read() один раз. Интересный факт: использование ignore() и countg() вместо ate и tellg() в gcc компилируется до почти того же самого По крупицам.

person kiroma    schedule 04.05.2020
comment
Этот код не работает, я получаю пустую строку. Я думаю, вы хотели ifs.seekg(0) вместо ifs.clear() (тогда это работает). - person Xeverous; 15.07.2020

Никогда не записывайте в буфер const char * std :: string. Никогда! Это серьезная ошибка.

Зарезервируйте () пространство для всей строки в вашем std :: string, прочтите фрагменты из вашего файла разумного размера в буфер и добавьте их (). Насколько большими должны быть фрагменты, зависит от размера входного файла. Я почти уверен, что все другие переносимые и совместимые с STL механизмы будут делать то же самое (но могут выглядеть красивее).

person Thorsten79    schedule 22.09.2008
comment
Начиная с C ++ 11, запись прямо в буфер std::string гарантированно допустима; и я считаю, что он работал правильно на всех фактических реализациях до этого - person M.M; 16.07.2018
comment
Начиная с C ++ 17 у нас даже есть неконстантный std::string::data() метод для изменение строкового буфера напрямую, не прибегая к уловкам вроде &str[0]. - person zett42; 16.11.2018
comment
Согласившись с @ zett42, этот ответ фактически неверен - person jeremyong; 16.03.2019

person    schedule
comment
Добавьте описание. - person Piotr Noga; 13.06.2020
comment
посетите и проверьте как ответить на вопрос. - person Yunus Temurlenk; 13.06.2020