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

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

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

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

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

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


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

Хотя это не (совсем) точная копия, это тесно связано с: как предварительно выделить память для объекта std :: string? (который, в отличие от утверждения Конрада выше, включал код для этого, считывая файл непосредственно в место назначения, без создания дополнительной копии).

Jerry Coffin 21.09.2012 19:11

«смежные по стандарту не требуются» - да, окольным путем. Как только вы используете op [] в строке, она должна быть объединена в непрерывный записываемый буфер, поэтому запись в & str [0] гарантированно безопасна, если сначала вы .resize () достаточно большой. А в C++ 11 строка просто всегда непрерывна.

Tino Didriksen 19.07.2013 21:22

Связанная ссылка: Как читать файл на C++? - тесты и обсуждение различных подходов. И да, rdbuf (тот, что в принятом ответе) не самый быстрый, read.

legends2k 27.11.2014 07:24

Обратите внимание, что у вас все еще есть недоопределенные параметры. Например, какова кодировка символов в файле? Будете ли вы пытаться автоматически определять (что работает только в некоторых конкретных случаях)? Будете ли вы уважать, например Заголовки XML сообщают вам кодировку файла? Также не существует таких понятий, как «текстовый режим» или «двоичный режим» - вы думаете о FTP?

Jason Cohen 22.09.2008 20:51

Текстовый и двоичный режимы - это специальные хаки для MSDOS и Windows, которые пытаются обойти тот факт, что новые строки представлены двумя символами в Windows (CR / LF). В текстовом режиме они обрабатываются как один символ ('\ n').

Ferruccio 22.09.2008 20:54

Обычно такие вещи обрабатываются подпрограммами, которые разбивают строки на строки, а не подпрограммами, считывающими данные из файлов. То есть в каждой среде, в которой я программировал, есть какие-то функции readAsLines () или breakIntoLines (), которые разбираются в таких вещах.

Jason Cohen 22.09.2008 20:56

Все эти решения приведут к неправильному формату строк, если ваша кодировка / интерпратация файла неверна. У меня была действительно странная проблема при сериализации файла JSON в строку, пока я вручную не преобразовал его в UTF-8; Независимо от того, какое решение я пробовал, я получал только первого персонажа! Просто нужно остерегаться! :)

kayleeFrye_onDeck 01.11.2018 07:18
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
197
7
85 295
15

Ответы 15

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

#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 не так, как просили.

Выглядит красиво, но не компилируется. Изменения для компиляции сводят его к другим ответам на этой странице. ideone.com/EyhfWm

JDiMatteo 29.06.2015 21:13

Почему цикл while?

Zitrax 19.10.2017 15:05

Согласовано. Когда operator>> читает в std::basic_streambuf, он потребляет (то, что осталось) входной поток, поэтому цикл не нужен.

Remy Lebeau 30.12.2018 21:44

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

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

Начиная с C++ 11, запись напрямую в буфер std::string гарантированно разрешена; и я считаю, что он работал правильно на всех фактических реализациях до этого

M.M 16.07.2018 02:24

Начиная с C++ 17 у нас даже есть неконстантный метод std::string::data() для непосредственного изменения строкового буфера, не прибегая к уловкам вроде &str[0].

zett42 16.11.2018 02:22

Согласившись с @ zett42, этот ответ фактически неверен

jeremyong 16.03.2019 01:24

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

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

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

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

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

Martin Beckett 22.09.2008 21:19

@ M.M Как я прочитал это сравнение, этот метод медленнее, чем чистый C++ метод чтения в предварительно выделенный буфер.

Konrad Rudolph 06.02.2016 15:27

Вы правы, это тот случай, когда заголовок находится под образцом кода, а не над ним :)

M.M 06.02.2016 22:44

Будет ли этот метод вызывать перераспределение памяти много раз?

coin cheung 11.03.2020 06:39

@coincheung К сожалению, да. Если вы хотите избежать выделения памяти, вам необходимо вручную буферизовать чтение. Потоки ввода-вывода C++ - это довольно дерьмо.

Konrad Rudolph 16.03.2020 13:08

@KonradRudolph Спасибо, я заметил, что есть еще один способ: stringstream ss; ifs >> ss.rdbuf(); str = ss.str();, будет ли этот метод также вызывать много перераспределений памяти, пожалуйста?

coin cheung 16.03.2020 14:33

@coincheung Этот должен позволяет избежать повторного распределения, но на практике это глупо. «Канонический» способ чтения всего файла в C++ 17 - gist.github.com/klmr/849cbb0c6e872dff0fdcc54787a66103. К сожалению, очень многословно.

Konrad Rudolph 16.03.2020 14:52

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

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. Более умная версия могла бы захватить весь буфер чтения и затем вызвать недополнение.

Вы должны проверить версию этого кода, которая использует std :: vector для начального чтения, а не строку. Намного намного быстрее.

paxos1977 08.02.2009 08:56

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

Я только что протестировал это, похоже, это намного медленнее, чем получение размера файла и вызов чтения для всего размера файла в буфер. Примерно в 12 раз медленнее.

David 14.05.2013 00:16

Это будет работать только до тех пор, пока в вашем файле нет символов "eof" (например, 0x00, 0xff, ...). Если есть, вы прочитаете только часть файла.

Olaf Dietsche 12.08.2017 13:37

Один из способов - сбросить буфер потока в отдельный поток памяти, а затем преобразовать его в 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;
}

Какой смысл делать это одним лайнером? Я всегда выбираю разборчивый код. Как самопровозглашенный энтузиаст VB.Net (IIRC), я думаю, вы должны понять это мнение?

sehe 21.09.2012 18:32

@sehe: Я ожидал, что любой наполовину компетентный программист на C++ легко поймет эту однострочную формулировку. Это довольно скучно по сравнению с другими вещами, которые есть поблизости.

DevSolar 21.09.2012 18:36

@DevSolar Что ж, более разборчивая версия на ~ 30% короче, без приведения и в остальном эквивалентна. Поэтому мой вопрос остается в силе: «Какой смысл делать его одинарным?»

sehe 21.09.2012 19:00

@sehe Я никогда не говорил, что буду использовать oneliner в реальном коде. Это было больше, чтобы показать, что это можно сделать одним выражением.

Konrad Rudolph 24.09.2012 14:59

@KonradRudolph Wokay. Рад это знать. Я просто немного сбит с толку, что вы тогда об этом упомянули. В любом случае, я проголосую за ваш другой ответ (??!), А затем за уловку :)

sehe 24.09.2012 16:32

Я знаю, что это очень старый, но я только что провел профилирование нескольких методов и обнаружил, что получение размера файла и вызов in.read в буфер, предварительно выделенный для правильного размера, намного быстрее, чем это. Около 10x. Я использую VS2012 и тестирую файл размером 100 МБ.

David 14.05.2013 00:18

@Dave Минимально быстрее - возможно. 10x? Это намекает на дефект в реализации стандартной библиотеки.

Konrad Rudolph 14.05.2013 07:59

Просто хотел добавить, что тому, кто изучает C++, это с первого взгляда сложно понять.

Rahul Iyer 13.10.2014 17:20

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

Konrad Rudolph 13.10.2014 17:23

Примечание: этот метод считывает файл в буфер строкового потока, а затем копирует весь этот буфер в string. Т.е. требует вдвое больше памяти, чем некоторые другие варианты. (Нет возможности переместить буфер). Для большого файла это было бы значительным штрафом, возможно, даже вызвав сбой выделения.

M.M 06.02.2016 15:08

@ M.M Хороший момент, я не знаю, как это так долго оставалось незамеченным.

Konrad Rudolph 06.02.2016 15:30

@sehe Как бы то ни было, я очень ценю лаконичность. Я не хочу вводить новую функцию только ради того, что в моей текущей программе является второстепенным функционалом, предполагающим чтение одной строки из файла для неважной цели. Просто требование добавить функцию для этого заставит меня даже не читать строку. В моем случае наличие одной строки кода позволяет одной строке кода не выделяться, поэтому я с радостью делаю это таким образом!

Dan Nissenbaum 12.02.2016 08:58

@DanNissenbaum Вы что-то путаете. Краткость действительно важна в программировании, но правильный способ добиться этого - разложить проблему на части и инкапсулировать их в независимые единицы (функции, классы и т. д.). Добавление функций не умаляет краткости; наоборот.

Konrad Rudolph 12.02.2016 11:47

@KonradRudolph, я тебя слышу. С годами я отошел от добавления функций и классов для одноразового использования, потому что само их присутствие придает вес их важности. Приятно иметь возможность взглянуть на код и увидеть простой небольшой набор функций и классов, представляющих основные функции. Я начал использовать «правило трех» - если блок короткого кода используется только один или даже два раза, преимущество нет, имеющего функцию, может перевесить преимущество инкапсуляции. Лишь к тому времени, когда он дойдет до третьего использования, я иногда буду склонен инкапсулировать его. Этот "прихлебывающий файл" подходит.

Dan Nissenbaum 12.02.2016 15:24

@DanNissenbaum, поэтому были введены лямбды :)

Ruslan 29.06.2016 21:56

Я считать, это решение работает, только если вы хотите читать файл в двоичном режиме. Если вы хотите читать его в текстовом режиме, istream_iterator - самый чистый способ. Это верно?

Maxpm 22.05.2017 23:28

Этот способ медленный (потому что std :: stringstream медленный).

Galik 16.07.2018 02:26

@Galik Медленно по сравнению с чем? Чтение в поток строк происходит очень быстро. Проблема в том, что строковые данные нельзя вывести из потока, их нужно скопировать.

Konrad Rudolph 16.07.2018 09:17

Почему не dynamic_cast вместо static_cast? Разве мы не просто опускаемся?

Ayxan Haqverdili 03.03.2019 21:30

@Ayxan Использование dynamic_cast действительно имеет смысл только в том случае, если вы не знаете, будет ли преобразование успешным, и проверить возвращаемое значение (или поймать потенциальный bad_cast). Однако мы знаем, что актерский состав здесь преуспел, поэтому нет необходимости хеджировать наши ставки. В идеале мы должны использовать приведение, при котором Только выполняет понижающее приведение и в то же время утверждает, что приведение будет успешным. Увы, в C++ такого приведения не существует.

Konrad Rudolph 03.03.2019 21:38

Будет ли этот метод вызывать перераспределение памяти много раз?

coin cheung 11.03.2020 06:40

это решение короткое, но запутанное. rdbuf() возвращает filebuf*. Как указатель на rdbuf заставляет stringstream читать содержимое файла? Я бы предпочел более подробный, но более понятный код, чем это волшебство.

anton_rh 27.04.2020 12:21

@anton_rh Это не волшебство, но для этого нужно знать, как работают соответствующие участники, что задокументировано. Кажется, вам не хватает overload (9) на этой странице: en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt

Konrad Rudolph 27.04.2020 13:09

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

Для вашего удобства я перепубликую решение 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 файла;)

связанные: сравнение временных характеристик различных методов: Чтение всего файла сразу в C++

jfs 03.12.2014 23:17

До сегодняшнего дня я ни разу не видел, чтобы tellg () сообщал о результатах, отличных от размера файла. На поиск источника ошибки у меня ушло несколько часов. Пожалуйста, не используйте tellg () для получения размера файла. stackoverflow.com/questions/22984956/…

Puzomor Croatia 27.12.2016 03:36

не следует ли вам звонить в ifs.seekg(0, ios::end) раньше, чем в tellg? сразу после открытия файла указатель чтения находится в начале, поэтому tellg возвращает ноль

Andriy Tylychko 09.02.2017 18:31

также вам нужно проверить наличие пустых файлов, так как вы разыменуете nullptr на &bytes[0]

Andriy Tylychko 09.02.2017 18:32

ок, я пропустил ios::ate, поэтому думаю, что версия с явным продвижением в конец была бы более читабельной

Andriy Tylychko 09.02.2017 18:43

Обратите внимание, что это решение работает только в двоичном режиме; тогда как OP запросил решение как для двоичного, так и для текстового режима.

M.M 12.06.2018 03:11

Поскольку строки C++ 11 гарантированно имеют непрерывное хранилище, вы можете напрямую использовать строку вместо вектора и, таким образом, пропустить вектор для копирования строки.

syam 18.12.2018 09:44

Это решение непереносимо, так как .tellg() не гарантирует возврата размера файла. (а на практике некоторые системы этого не делают).

spectras 25.02.2021 12:25

Если у вас 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], если он не поддерживает неконстантные данные std :: basic_string.

Это может вызвать неопределенное поведение; открытие файла в текстовом режиме дает другой поток, чем файл на диске в некоторых операционных системах.

M.M 12.06.2018 03:13

Первоначально разработан как boost::filesystem, поэтому вы также можете использовать boost, если у вас нет C++ 17

Gerhard Burger 29.09.2018 14:11

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

Arthur Tacca 24.10.2018 16:19

У меня недостаточно репутации, чтобы напрямую комментировать ответы, используя 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);
}

Более того, tellg() возвращает не размер, а токен. Многие системы используют байтовое смещение в качестве токена, но это не гарантируется, а некоторые системы этого не делают. Посмотрите, например, этот ответ.

spectras 25.02.2021 12:29

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

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

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

#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()); };

infile.open также может принимать std::string без преобразования с .c_str()

Matt Eding 02.01.2020 09:24

filepath - это не std::string, это std::filesystem::path. Оказывается, std::ifstream::open может принять и один из них.

David G 10.01.2020 01:21

@DavidG, std::filesystem::path неявно конвертируется в std::string

Jeffrey Cash 07.02.2020 14:53

Согласно cppreference.com, функция-член ::open на std::ifstream, которая принимает std::filesystem::path, работает так, как если бы метод ::c_str() был вызван на пути. Базовый ::value_type путей - это char в POSIX.

David G 18.02.2020 03:20

Обновленная функция, основанная на решении 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 компилируется до почти то же самое, бит за битом.

Этот код не работает, я получаю пустую строку. Думаю, вы хотели ifs.seekg(0) вместо ifs.clear() (тогда работает).

Xeverous 15.07.2020 12:46

#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;
main(){
    fstream file;
    //Open a file
    file.open("test.txt");
    string copy,temp;
    //While loop to store whole document in copy string
    //Temp reads a complete line
    //Loop stops until temp reads the last line of document
    while(getline(file,temp)){
        //add new line text in copy
        copy+=temp;
        //adds a new line
        copy+ = "\n";
    }
    //Display whole document
    cout<<copy;
    //close the document
    file.close();
}

Добавьте описание.

Peter 13.06.2020 11:08

посетите и проверьте как ответить на вопрос.

Yunus Temurlenk 13.06.2020 11:39

Поскольку это похоже на широко используемую утилиту, мой подход заключался бы в поиске и предпочтении уже доступных библиотек решениям, сделанным вручную, особенно если библиотеки 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', который перезаписывается содержимым файла.

Другие вопросы по теме